123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740(* This file is free software, part of Zipperposition. See file "license" for more details. *)openLogtkopenLibzipperpositionmoduleBV=CCBVmoduleT=TermmoduleO=OrderingmoduleS=SubstmoduleLit=LiteralmoduleLits=LiteralsmoduleComp=ComparisonmoduleUS=Unif_substletsection=Util.Section.make~parent:Const.section"sup"(* flag meaning the clause has been simplified already *)letflag_simplified=SClause.new_flag()moduletypeS=Superposition_intf.S(* statistics *)letstat_basic_simplify_calls=Util.mk_stat"sup.basic_simplify calls"letstat_basic_simplify=Util.mk_stat"sup.basic_simplify"letstat_superposition_call=Util.mk_stat"sup.superposition calls"letstat_equality_resolution_call=Util.mk_stat"sup.equality_resolution calls"letstat_equality_factoring_call=Util.mk_stat"sup.equality_factoring calls"letstat_subsumption_call=Util.mk_stat"sup.subsumption_calls"letstat_eq_subsumption_call=Util.mk_stat"sup.equality_subsumption calls"letstat_eq_subsumption_success=Util.mk_stat"sup.equality_subsumption success"letstat_subsumed_in_active_set_call=Util.mk_stat"sup.subsumed_in_active_set calls"letstat_subsumed_by_active_set_call=Util.mk_stat"sup.subsumed_by_active_set calls"letstat_clauses_subsumed=Util.mk_stat"sup.num_clauses_subsumed"letstat_demodulate_call=Util.mk_stat"sup.demodulate calls"letstat_demodulate_step=Util.mk_stat"sup.demodulate steps"letstat_semantic_tautology=Util.mk_stat"sup.semantic_tautologies"letstat_condensation=Util.mk_stat"sup.condensation"letstat_clc=Util.mk_stat"sup.clc"letprof_demodulate=Util.mk_profiler"sup.demodulate"letprof_back_demodulate=Util.mk_profiler"sup.backward_demodulate"letprof_pos_simplify_reflect=Util.mk_profiler"sup.simplify_reflect+"letprof_neg_simplify_reflect=Util.mk_profiler"sup.simplify_reflect-"letprof_clc=Util.mk_profiler"sup.contextual_literal_cutting"letprof_semantic_tautology=Util.mk_profiler"sup.semantic_tautology"letprof_condensation=Util.mk_profiler"sup.condensation"letprof_basic_simplify=Util.mk_profiler"sup.basic_simplify"letprof_subsumption=Util.mk_profiler"sup.subsumption"letprof_eq_subsumption=Util.mk_profiler"sup.equality_subsumption"letprof_subsumption_set=Util.mk_profiler"sup.forward_subsumption"letprof_subsumption_in_set=Util.mk_profiler"sup.backward_subsumption"letprof_infer_active=Util.mk_profiler"sup.infer_active"letprof_infer_passive=Util.mk_profiler"sup.infer_passive"letprof_infer_equality_resolution=Util.mk_profiler"sup.infer_equality_resolution"letprof_infer_equality_factoring=Util.mk_profiler"sup.infer_equality_factoring"let_use_semantic_tauto=reftruelet_use_simultaneous_sup=reftruelet_dot_sup_into=refNonelet_dot_sup_from=refNonelet_dot_simpl=refNonelet_dont_simplify=reffalselet_sup_at_vars=reffalselet_restrict_hidden_sup_at_vars=reffalselet_dot_demod_into=refNonemoduleMake(Env:Env.S):SwithmoduleEnv=Env=structmoduleEnv=EnvmoduleCtx=Env.CtxmoduleC=Env.CmodulePS=Env.ProofStatemoduleI=PS.TermIndexmoduleTermIndex=PS.TermIndexmoduleSubsumIdx=PS.SubsumptionIndexmoduleUnitIdx=PS.UnitIndex(** {6 Index Management} *)let_idx_sup_into=ref(TermIndex.empty())let_idx_sup_from=ref(TermIndex.empty())let_idx_back_demod=ref(TermIndex.empty())let_idx_fv=ref(SubsumIdx.empty())let_idx_simpl=ref(UnitIdx.empty())letidx_sup_into()=!_idx_sup_intoletidx_sup_from()=!_idx_sup_fromletidx_fv()=!_idx_fv(* apply operation [f] to some parts of the clause [c] just added/removed
from the active set *)let_update_activefc=letord=Ctx.ord()in(* index subterms that can be rewritten by superposition *)_idx_sup_into:=Lits.fold_terms~vars:!_sup_at_vars~ty_args:false~ord~which:`Max~subterms:true~eligible:(C.Eligible.resc)(C.litsc)|>Iter.filter(fun(t,_)->not(T.is_vart)||T.is_ho_vart)(* TODO: could exclude more variables from the index:
they are not needed if they occur with the same args everywhere in the clause *)|>Iter.fold(funtree(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c;})inftreetwith_pos)!_idx_sup_into;(* index terms that can rewrite into other clauses *)_idx_sup_from:=Lits.fold_eqn~ord~both:true~sign:true~eligible:(C.Eligible.paramc)(C.litsc)|>Iter.fold(funtree(l,_,sign,pos)->assertsign;letwith_pos=C.WithPos.({term=l;pos;clause=c;})inftreelwith_pos)!_idx_sup_from;(* terms that can be demodulated: all subterms (but vars) *)_idx_back_demod:=Lits.fold_terms~vars:false~ty_args:false~ord~subterms:true~which:`All~eligible:C.Eligible.always(C.litsc)|>Iter.fold(funtree(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})inftreetwith_pos)!_idx_back_demod;Signal.ContinueListening(* update simpl. index using the clause [c] just added or removed to
the simplification set *)let_update_simplfc=letord=Ctx.ord()inletidx=!_idx_simplinletidx'=matchC.litscwith|[|Lit.Equation(l,r,true)|]->beginmatchOrdering.compareordlrwith|Comparison.Gt->fidx(l,r,true,c)|Comparison.Lt->fidx(r,l,true,c)|Comparison.Incomparable->letidx=fidx(l,r,true,c)infidx(r,l,true,c)|Comparison.Eq->idx(* no modif *)end|[|Lit.Equation(l,r,false)|]->fidx(l,r,false,c)|[|Lit.Prop(p,sign)|]->fidx(p,T.true_,sign,c)|_->idxin_idx_simpl:=idx';Signal.ContinueListeninglet()=Signal.onPS.ActiveSet.on_add_clause(func->_idx_fv:=SubsumIdx.add!_idx_fvc;_update_activeTermIndex.addc);Signal.onPS.ActiveSet.on_remove_clause(func->_idx_fv:=SubsumIdx.remove!_idx_fvc;_update_activeTermIndex.removec);Signal.onPS.SimplSet.on_add_clause(_update_simplUnitIdx.add);Signal.onPS.SimplSet.on_remove_clause(_update_simplUnitIdx.remove);()(** {6 Inference Rules} *)(* all the information needed for a superposition inference *)moduleSupInfo=structtypet={active:C.t;active_pos:Position.t;(* position of [s] *)scope_active:int;s:T.t;(* lhs of rule *)t:T.t;(* rhs of rule *)passive:C.t;passive_pos:Position.t;(* position of [u_p] *)passive_lit:Lit.t;scope_passive:int;u_p:T.t;(* rewritten subterm *)subst:US.t;}endexceptionExitSuperpositionofstring(* check for hidden superposition at variables,
e.g. superposing g x = f x into h (x b) = a to give h (f b) = a.
Returns a term only containing the concerned variable
and a term consisting of the part of info.t that unifies with the variable,
e.g. (x, f) in the example above. *)letis_hidden_sup_at_varinfo=letopenSupInfoinletactive_idx=Lits.Pos.idxinfo.active_posinbeginmatchT.viewinfo.u_pwith|T.App(head,args)->beginmatchT.as_varheadwith|Some_->(* rewritten term is variable-headed *)beginmatchT.viewinfo.s,T.viewinfo.twith|T.App(f,ss),T.App(g,tt)->lets_args=Array.of_listssinlett_args=Array.of_listttinifArray.lengths_args>=List.lengthargs&&Array.lengtht_args>=List.lengthargs(* Check whether the last argument(s) of s and t are equal *)&&Array.subs_args(Array.lengths_args-List.lengthargs)(List.lengthargs)=Array.subt_args(Array.lengtht_args-List.lengthargs)(List.lengthargs)(* Check whether they are all variables that occur nowhere else *)&&CCList.(Array.lengths_args-List.lengthargs--^Array.lengths_args)|>List.for_all(funidx->matchT.as_var(Array.gets_argsidx)with|Somev->(* Check whether variable occurs in previous arguments: *)not(CCArray.exists(T.var_occurs~var:v)(Array.subs_args0idx))&¬(CCArray.exists(T.var_occurs~var:v)(Array.subt_args0(Array.lengtht_args-List.lengthargs))(* Check whether variable occurs in heads: *)&¬(T.var_occurs~var:vf)&¬(T.var_occurs~var:vg)(* Check whether variable occurs in other literals: *)&¬(List.exists(Literal.var_occursv)(CCArray.except_idx(C.litsinfo.active)active_idx)))|None->false)then(* Calculate the part of t that unifies with the variable *)lett_prefix=T.appg(Array.to_list(Array.subt_args0(Array.lengtht_args-List.lengthargs)))inSome(head,t_prefix)elseNone|_->Noneend|None->Noneend|_->Noneend(* Checks whether we must allow superposition at variables to be complete. *)letsup_at_var_conditioninfovarreplacement=letopenSupInfoinletord=Ctx.ord()inletus=info.substinletsubst=US.substusinletrenaming=S.Renaming.create()inletreplacement'=S.FO.applyrenamingsubst(replacement,info.scope_active)inletvar'=S.FO.applyrenamingsubst(var,info.scope_passive)inif(not(Type.is_fun(Term.tyvar'))||not(O.might_flipordvar'replacement'))then(Util.debugf~section5"Cannot flip: %a = %a"(funk->kT.ppvar'T.ppreplacement');false(* If the lhs vs rhs cannot flip, we don't need a sup at var *))else((* Check whether var occurs only with the same arguments everywhere. *)letunique_args_of_var=C.litsinfo.passive|>Lits.fold_terms~vars:true~ty_args:false~which:`All~ord~subterms:true~eligible:(fun__->true)|>Iter.fold_while(fununique_args(t,_)->ifHead.term_to_headt==Head.term_to_headvarthen(ifunique_args==Some(Head.term_to_argst)then(unique_args,`Continue)(* found the same arguments of var again *)else(None,`Stop)(* different arguments of var found *))else(unique_args,`Continue)(* this term doesn't have var as head *))Noneinmatchunique_args_of_varwith|Some_->Util.debugf~section5"Variable %a has same args everywhere in %a"(funk->kT.ppvarC.ppinfo.passive);false(* If var occurs with the same arguments everywhere, we don't need sup at vars *)|None->(* Check whether Cσ is >= C[var -> replacement]σ *)letpassive'_lits=Lits.apply_substrenamingsubst(C.litsinfo.passive,info.scope_passive)inletsubst_t=Unif.FO.updatesubst(T.as_var_exnvar,info.scope_passive)(replacement,info.scope_active)inletpassive_t'_lits=Lits.apply_substrenamingsubst_t(C.litsinfo.passive,info.scope_passive)inifLits.compare_multiset~ordpassive'_litspassive_t'_lits=Comp.Gtthen(Util.debugf~section5"Sup at var condition is not fulfilled because: %a >= %a"(funk->kLits.pppassive'_litsLits.pppassive_t'_lits);false)elsetrue(* If Cσ is either <= or incomparable to C[var -> replacement]σ, we need sup at var.*))(* Helper that does one or zero superposition inference, with all
the given parameters. Clauses have a scope. *)letdo_classic_superpositioninfoacc=letord=Ctx.ord()inletopenSupInfoinletmoduleP=PositioninUtil.incr_statstat_superposition_call;letsc_a=info.scope_activeinletsc_p=info.scope_passiveinUtil.debugf~section3"@[<2>sup@ (@[<2>%a[%d]@ @[s=%a@]@ @[t=%a@]@])@ \
(@[<2>%a[%d]@ @[passive_lit=%a@]@ @[p=%a@]@])@ with subst=@[%a@]@]"(funk->kC.ppinfo.activesc_aT.ppinfo.sT.ppinfo.tC.ppinfo.passivesc_pLit.ppinfo.passive_litPosition.ppinfo.passive_posUS.ppinfo.subst);assert(InnerTerm.DB.closed(info.s:>InnerTerm.t));assert(InnerTerm.DB.closed(info.u_p:T.t:>InnerTerm.t));assert(not(T.is_varinfo.u_p)||T.is_ho_varinfo.u_p);letactive_idx=Lits.Pos.idxinfo.active_posinletpassive_idx,passive_lit_pos=Lits.Pos.cutinfo.passive_posintryletrenaming=S.Renaming.create()inletus=info.substinletsubst=US.substusinlett'=S.FO.applyrenamingsubst(info.t,sc_a)inbeginmatchinfo.passive_lit,info.passive_poswith|Lit.Prop(_,true),P.Arg(_,P.LeftP.Stop)->ifT.equalt'T.true_thenraise(ExitSuperposition"will yield a bool tautology")|Lit.Equation(_,v,true),P.Arg(_,P.LeftP.Stop)|Lit.Equation(v,_,true),P.Arg(_,P.RightP.Stop)->(* are we in the specific, but no that rare, case where we
rewrite s=t using s=t (into a tautology t=t)? *)(* TODO: use Unif.FO.eq? *)letv'=S.FO.applyrenamingsubst(v,sc_p)inifT.equalt'v'thenraise(ExitSuperposition"will yield a tautology");|_->()end;letpassive_lit'=Lit.apply_subst_no_simprenamingsubst(info.passive_lit,sc_p)inletnew_trail=C.trail_l[info.active;info.passive]inifEnv.is_trivial_trailnew_trailthenraise(ExitSuperposition"trivial trail");lets'=S.FO.applyrenamingsubst(info.s,sc_a)inif(O.compareords't'=Comp.Lt||not(Lit.Pos.is_max_term~ordpassive_lit'passive_lit_pos)||not(BV.get(C.eligible_res(info.passive,sc_p)subst)passive_idx)||not(C.is_eligible_param(info.active,sc_a)subst~idx:active_idx))thenraise(ExitSuperposition"bad ordering conditions");(* Check for superposition at a variable *)ifnot!_sup_at_varsthenassert(not(T.is_varinfo.u_p))elseifT.is_varinfo.u_p&¬(sup_at_var_conditioninfoinfo.u_pinfo.t)thenraise(ExitSuperposition"superposition at variable");(* Check for hidden superposition at a variable *)if!_restrict_hidden_sup_at_varsthen(matchis_hidden_sup_at_varinfowith|Some(var,replacement)whennot(!_sup_at_vars&&sup_at_var_conditioninfovarreplacement)->raise(ExitSuperposition"hidden superposition at variable")|_->());(* ordering constraints are ok *)letlits_a=CCArray.except_idx(C.litsinfo.active)active_idxinletlits_p=CCArray.except_idx(C.litsinfo.passive)passive_idxin(* replace s\sigma by t\sigma in u|_p\sigma *)letnew_passive_lit=Lit.Pos.replacepassive_lit'~at:passive_lit_pos~by:t'inletc_guard=Literal.of_unif_substrenamingusinlettags=Unif_subst.tagsusin(* apply substitution to other literals *)letnew_lits=new_passive_lit::c_guard@Lit.apply_subst_listrenamingsubst(lits_a,sc_a)@Lit.apply_subst_listrenamingsubst(lits_p,sc_p)inletrule=letname=ifLit.signpassive_lit'then"sup+"else"sup-"inProof.Rule.mknameinletproof=Proof.Step.inference~rule~tags[C.proof_parent_substrenaming(info.active,sc_a)subst;C.proof_parent_substrenaming(info.passive,sc_p)subst]andpenalty=C.penaltyinfo.active+C.penaltyinfo.passive+(ifT.is_vars'then2else0)(* superposition from var = bad *)inletnew_clause=C.create~trail:new_trail~penaltynew_litsproofinUtil.debugf~section3"@[... ok, conclusion@ @[%a@]@]"(funk->kC.ppnew_clause);new_clause::accwithExitSuperpositionreason->Util.debugf~section3"... cancel, %s"(funk->kreason);acc(* simultaneous superposition: when rewriting D with C \lor s=t,
replace s with t everywhere in D rather than at one place. *)letdo_simultaneous_superpositioninfoacc=letord=Ctx.ord()inletopenSupInfoinletmoduleP=PositioninUtil.incr_statstat_superposition_call;letsc_a=info.scope_activeinletsc_p=info.scope_passiveinUtil.debugf~section3"@[<hv2>simultaneous sup@ \
@[<2>active@ %a[%d]@ s=@[%a@]@ t=@[%a@]@]@ \
@[<2>passive@ %a[%d]@ passive_lit=@[%a@]@ p=@[%a@]@]@ with subst=@[%a@]@]"(funk->kC.ppinfo.activesc_aT.ppinfo.sT.ppinfo.tC.ppinfo.passivesc_pLit.ppinfo.passive_litPosition.ppinfo.passive_posUS.ppinfo.subst);assert(InnerTerm.DB.closed(info.s:>InnerTerm.t));assert(InnerTerm.DB.closed(info.u_p:T.t:>InnerTerm.t));assert(not(T.is_varinfo.u_p)||T.is_ho_varinfo.u_p);letactive_idx=Lits.Pos.idxinfo.active_posinletpassive_idx,passive_lit_pos=Lits.Pos.cutinfo.passive_posintryletrenaming=S.Renaming.create()inletus=info.substinletsubst=US.substusinlett'=S.FO.applyrenamingsubst(info.t,sc_a)inbeginmatchinfo.passive_lit,info.passive_poswith|Lit.Prop(_,true),P.Arg(_,P.LeftP.Stop)->ifT.equalt'T.true_thenraise(ExitSuperposition"will yield a bool tautology")|Lit.Equation(_,v,true),P.Arg(_,P.LeftP.Stop)|Lit.Equation(v,_,true),P.Arg(_,P.RightP.Stop)->(* are we in the specific, but no that rare, case where we
rewrite s=t using s=t (into a tautology t=t)? *)letv'=S.FO.applyrenamingsubst(v,sc_p)inifT.equalt'v'thenraise(ExitSuperposition"will yield a tautology");|_->()end;letpassive_lit'=Lit.apply_subst_no_simprenamingsubst(info.passive_lit,sc_p)inletnew_trail=C.trail_l[info.active;info.passive]inifEnv.is_trivial_trailnew_trailthenraise(ExitSuperposition"trivial trail");lets'=S.FO.applyrenamingsubst(info.s,sc_a)inif(O.compareords't'=Comp.Lt||not(Lit.Pos.is_max_term~ordpassive_lit'passive_lit_pos)||not(BV.get(C.eligible_res(info.passive,sc_p)subst)passive_idx)||not(C.is_eligible_param(info.active,sc_a)subst~idx:active_idx))thenraise(ExitSuperposition"bad ordering conditions");(* Check for superposition at a variable *)ifnot!_sup_at_varsthenassert(not(T.is_varinfo.u_p))elseifT.is_varinfo.u_p&¬(sup_at_var_conditioninfoinfo.u_pinfo.t)thenraise(ExitSuperposition"superposition at variable");(* Check for hidden superposition at a variable *)matchis_hidden_sup_at_varinfowith|Some(var,replacement)whennot(!_sup_at_vars&&sup_at_var_conditioninfovarreplacement)->raise(ExitSuperposition"hidden superposition at variable")|_->();(* ordering constraints are ok, build new active lits (excepted s=t) *)letlits_a=CCArray.except_idx(C.litsinfo.active)active_idxinletlits_a=Lit.apply_subst_listrenamingsubst(lits_a,sc_a)in(* build passive literals and replace u|p\sigma with t\sigma *)letu'=S.FO.applyrenamingsubst(info.u_p,sc_p)inassert(Type.equal(T.tyu')(T.tyt'));letlits_p=Array.to_list(C.litsinfo.passive)inletlits_p=Lit.apply_subst_listrenamingsubst(lits_p,sc_p)in(* assert (T.equal (Lits.Pos.at (Array.of_list lits_p) info.passive_pos) u'); *)letlits_p=List.map(Lit.map(funt->T.replacet~old:u'~by:t'))lits_pinletc_guard=Literal.of_unif_substrenamingusinlettags=Unif_subst.tagsusin(* build clause *)letnew_lits=c_guard@lits_a@lits_pinletrule=letname=ifLit.signpassive_lit'then"s_sup+"else"s_sup-"inProof.Rule.mknameinletproof=Proof.Step.inference~rule~tags[C.proof_parent_substrenaming(info.active,sc_a)subst;C.proof_parent_substrenaming(info.passive,sc_p)subst]andpenalty=C.penaltyinfo.active+C.penaltyinfo.passive+(ifT.is_vars'then2else0)(* superposition from var = bad *)inletnew_clause=C.create~trail:new_trail~penaltynew_litsproofinUtil.debugf~section3"@[... ok, conclusion@ @[%a@]@]"(funk->kC.ppnew_clause);new_clause::accwithExitSuperpositionreason->Util.debugf~section3"@[... cancel, %s@]"(funk->kreason);acc(* choose between regular and simultaneous superposition *)letdo_superpositioninfoacc=letopenSupInfoinassert(Type.equal(T.tyinfo.s)(T.tyinfo.t));assert(Unif.Ty.equal~subst:(US.substinfo.subst)(T.tyinfo.s,info.scope_active)(T.tyinfo.u_p,info.scope_passive));if!_use_simultaneous_supthendo_simultaneous_superpositioninfoaccelsedo_classic_superpositioninfoaccletinfer_activeclause=Util.enter_profprof_infer_active;(* no literal can be eligible for paramodulation if some are selected.
This checks if inferences with i-th literal are needed? *)leteligible=C.Eligible.paramclausein(* do the inferences where clause is active; for this,
we try to rewrite conditionally other clauses using
non-minimal sides of every positive literal *)letnew_clauses=Lits.fold_eqn~sign:true~ord:(Ctx.ord())~both:true~eligible(C.litsclause)|>Iter.fold(funacc(s,t,_,s_pos)->(* rewrite clauses using s *)I.retrieve_unifiables(!_idx_sup_into,1)(s,0)|>Iter.filter(fun(u_p,_,_)->T.DB.is_closedu_p)|>Iter.fold(funacc(u_p,with_pos,subst)->(* rewrite u_p with s *)letpassive=with_pos.C.WithPos.clauseinletpassive_pos=with_pos.C.WithPos.posinletpassive_lit,_=Lits.Pos.lit_at(C.litspassive)passive_posinletinfo=SupInfo.({s;t;active=clause;active_pos=s_pos;scope_active=0;u_p;passive;passive_lit;passive_pos;scope_passive=1;subst;})indo_superpositioninfoacc)acc)[]inUtil.exit_profprof_infer_active;new_clausesletinfer_passiveclause=Util.enter_profprof_infer_passive;(* perform inference on this lit? *)leteligible=C.Eligible.(resclause)in(* do the inferences in which clause is passive (rewritten),
so we consider both negative and positive literals *)letnew_clauses=Lits.fold_terms~vars:!_sup_at_vars~subterms:true~ord:(Ctx.ord())~which:`Max~eligible~ty_args:false(C.litsclause)|>Iter.filter(fun(u_p,_)->not(T.is_varu_p)||T.is_ho_varu_p)(* TODO: could exclude more variables from the index:
they are not needed if they occur with the same args everywhere in the clause *)|>Iter.filter(fun(u_p,_)->T.DB.is_closedu_p)|>Iter.fold(funacc(u_p,passive_pos)->letpassive_lit,_=Lits.Pos.lit_at(C.litsclause)passive_posin(* all terms that occur in an equation in the active_set
and that are potentially unifiable with u_p (u at position p) *)I.retrieve_unifiables(!_idx_sup_from,1)(u_p,0)|>Iter.fold(funacc(_,with_pos,subst)->letactive=with_pos.C.WithPos.clauseinlets_pos=with_pos.C.WithPos.posinmatchLits.View.get_eqn(C.litsactive)s_poswith|Some(s,t,true)->letinfo=SupInfo.({s;t;active;active_pos=s_pos;scope_active=1;subst;u_p;passive=clause;passive_lit;passive_pos;scope_passive=0;})indo_superpositioninfoacc|_->acc)acc)[]inUtil.exit_profprof_infer_passive;new_clausesletinfer_equality_resolutionclause=Util.enter_profprof_infer_equality_resolution;leteligible=C.Eligible.alwaysin(* iterate on those literals *)letnew_clauses=Lits.fold_eqn~sign:false~ord:(Ctx.ord())~both:false~eligible(C.litsclause)|>Iter.filter_map(fun(l,r,_,l_pos)->letpos=Lits.Pos.idxl_posintryletus=Unif.FO.unify_full(l,0)(r,0)inifBV.get(C.eligible_res_no_substclause)pos(* subst(lit) is maximal, we can do the inference *)then(Util.incr_statstat_equality_resolution_call;letrenaming=Subst.Renaming.create()inletsubst=US.substusinletrule=Proof.Rule.mk"eq_res"inletnew_lits=CCArray.except_idx(C.litsclause)posinletnew_lits=Lit.apply_subst_listrenamingsubst(new_lits,0)inletc_guard=Literal.of_unif_substrenamingusinlettags=Unif_subst.tagsusinlettrail=C.trailclauseandpenalty=C.penaltyclauseinletproof=Proof.Step.inference~rule~tags[C.proof_parent_substrenaming(clause,0)subst]inletnew_clause=C.create~trail~penalty(c_guard@new_lits)proofinUtil.debugf~section3"@[<hv2>equality resolution on@ @[%a@]@ yields @[%a@]@]"(funk->kC.ppclauseC.ppnew_clause);Somenew_clause)elseNonewithUnif.Fail->(* l and r not unifiable, try next *)None)|>Iter.to_rev_listinUtil.exit_profprof_infer_equality_resolution;new_clausesmoduleEqFactInfo=structtypet={clause:C.t;active_idx:int;s:T.t;t:T.t;u:T.t;v:T.t;subst:US.t;scope:int;}end(* do the inference between given positions, if ordering conditions are respected *)letdo_eq_factoringinfoacc=letopenEqFactInfoinletord=Ctx.ord()inlets=info.sandt=info.tandv=info.vinletus=info.substin(* check whether subst(lit) is maximal, and not (subst(s) < subst(t)) *)letrenaming=S.Renaming.create()inletsubst=US.substusinifO.compareord(S.FO.applyrenamingsubst(s,info.scope))(S.FO.applyrenamingsubst(t,info.scope))<>Comp.Lt&&C.is_eligible_param(info.clause,info.scope)subst~idx:info.active_idxthen(Util.incr_statstat_equality_factoring_call;lettags=Unif_subst.tagsusinletproof=Proof.Step.inference~rule:(Proof.Rule.mk"eq_fact")~tags[C.proof_parent_substrenaming(info.clause,0)subst](* new_lits: literals of the new clause. remove active literal
and replace it by a t!=v one, and apply subst *)andnew_lits=CCArray.except_idx(C.litsinfo.clause)info.active_idxinletnew_lits=Lit.apply_subst_listrenamingsubst(new_lits,info.scope)inletc_guard=Literal.of_unif_substrenamingusinletlit'=Lit.mk_neq(S.FO.applyrenamingsubst(t,info.scope))(S.FO.applyrenamingsubst(v,info.scope))inletnew_lits=lit'::c_guard@new_litsinletnew_clause=C.create~trail:(C.trailinfo.clause)~penalty:(C.penaltyinfo.clause)new_litsproofinUtil.debugf~section3"@[<hv2>equality factoring on@ @[%a@]@ yields @[%a@]@]"(funk->kC.ppinfo.clauseC.ppnew_clause);new_clause::acc)elseaccletinfer_equality_factoringclause=Util.enter_profprof_infer_equality_factoring;leteligible=C.Eligible.(filterLit.is_pos)in(* find root terms that are unifiable with s and are not in the
literal at s_pos. Calls [k] with a position and substitution *)letfind_unifiable_litsidxs_s_posk=Array.iteri(funilit->matchlitwith|_wheni=idx->()(* same index *)|Lit.Prop(p,true)->(* positive proposition *)begintryletsubst=Unif.FO.unify_full(s,0)(p,0)ink(p,T.true_,subst)withUnif.Fail->()end|Lit.Equation(u,v,true)->(* positive equation *)begintryletsubst=Unif.FO.unify_full(s,0)(u,0)ink(u,v,subst)withUnif.Fail->()end;begintryletsubst=Unif.FO.unify_full(s,0)(v,0)ink(v,u,subst)withUnif.Fail->()end;|_->()(* ignore other literals *))(C.litsclause)in(* try to do inferences with each positive literal *)letnew_clauses=Lits.fold_eqn~sign:true~ord:(Ctx.ord())~both:true~eligible(C.litsclause)|>Iter.fold(funacc(s,t,_,s_pos)->(* try with s=t *)letactive_idx=Lits.Pos.idxs_posinfind_unifiable_litsactive_idxss_pos|>Iter.fold(funacc(u,v,subst)->letinfo=EqFactInfo.({clause;s;t;u;v;active_idx;subst;scope=0;})indo_eq_factoringinfoacc)acc)[]inUtil.exit_profprof_infer_equality_factoring;new_clauses(* ----------------------------------------------------------------------
* simplifications
* ---------------------------------------------------------------------- *)(* TODO: put forward pointers in simpl_set, to make some rewriting steps
faster? (invalidate when updated, also allows to reclaim memory) *)(* TODO: use a record with
- head
- args
- subst
so as not to rebuild intermediate terms, and also to avoid mixing
the head normal form and the substitution for (evaluated) arguments.
Might even convert rules into De Bruijn, because:
- special restriction (vars rhs ⊆ vars lhs)
- indexing on first symbol might be sufficient if matching is fast
- must rewrite matching to work on the record anyway
*)letlazy_false=Lazy.from_valfalsetypedemod_state={mutabledemod_clauses:(C.t*Subst.t*Scoped.scope)list;(* rules used *)mutabledemod_sc:Scoped.scope;(* current scope *)}(** Compute normal form of term w.r.t active set. Clauses used to
rewrite are added to the clauses hashset.
restrict is an option for restricting demodulation in positive maximal terms *)letdemod_nf?(restrict=lazy_false)(st:demod_state)ct:T.t=letord=Ctx.ord()in(* compute normal form of subterm. If restrict is true, substitutions that
are variable renamings are forbidden (since we are at root of a max term) *)letrecreduce_at_root~restricttk=(* find equations l=r that match subterm *)letcur_sc=st.demod_scinassert(cur_sc>0);letstep=UnitIdx.retrieve~sign:true(!_idx_simpl,cur_sc)(t,0)|>Iter.find_map(fun(l,r,(_,_,sign,unit_clause),subst)->(* r is the term subterm is going to be rewritten into *)assert(C.is_unit_clauseunit_clause);ifsign&&(not(Lazy.forcerestrict)||not(S.is_renamingsubst))&&C.trail_subsumesunit_clausec&&(O.compareord(S.FO.applySubst.Renaming.nonesubst(l,cur_sc))(S.FO.applySubst.Renaming.nonesubst(r,cur_sc))=Comp.Gt)(* subst(l) > subst(r) and restriction does not apply, we can rewrite *)then(Util.debugf~section5"@[<hv2>demod:@ @[<hv>t=%a[%d],@ l=%a[%d],@ r=%a[%d]@],@ subst=@[%a@]@]"(funk->kT.ppt0T.pplcur_scT.pprcur_scS.ppsubst);(* sanity checks *)assert(Type.equal(T.tyl)(T.tyr));assert(Unif.FO.equal~subst(l,cur_sc)(t,0));st.demod_clauses<-(unit_clause,subst,cur_sc)::st.demod_clauses;st.demod_sc<-1+st.demod_sc;(* allocate new scope *)Util.incr_statstat_demodulate_step;Some(r,subst,cur_sc))elseNone)inbeginmatchstepwith|None->kt(* not found any match, normal form found *)|Some(rhs,subst,cur_sc)->(* reduce [rhs] in current scope [cur_sc] *)assert(cur_sc<st.demod_sc);Util.debugf~section5"@[<2>demod:@ rewrite `@[%a@]`@ into `@[%a@]`@ using %a[%d]@]"(funk->kT.pptT.pprhsSubst.ppsubstcur_sc);(* NOTE: we retraverse the term several times, but this is simpler *)letrhs=Subst.FO.applySubst.Renaming.nonesubst(rhs,cur_sc)innormal_form~restrictrhsk(* done one rewriting step, continue *)end(* rewrite innermost-leftmost of [subst(t,scope)]. The initial scope is
0, but then we normal_form terms in which variables are really the variables
of the RHS of a previously applied rule (in context !sc); all those
variables are bound to terms in context 0 *)andnormal_form~restricttk=matchT.viewtwith|T.Const_->reduce_at_root~restricttk|T.App(hd,l)->(* rewrite subterms in call by value.
Note that we keep restrictions for the head, so as
not to rewrite [f x=g x] into ⊤ after equality completion
of [f=g] *)letrewrite_head=(* Don't rewrite heads in the following situations: *)(List.lengthl=0||not(T.is_type(List.hdl)))&¬(Ordering.monotonicord)in(ifrewrite_headthennormal_form~restricthdelse(funk->khd))(funhd'->normal_form_ll(funl'->lett'=ifT.equalhdhd'&&T.same_lll'thentelseT.apphd'l'in(* rewrite term at root *)reduce_at_root~restrictt'k))|T.Fun(ty_arg,body)->(* reduce under lambdas *)normal_form~restrict:lazy_falsebody(funbody'->letu=ifT.equalbodybody'thentelseT.fun_ty_argbody'inku)|T.Var_|T.DB_->kt|T.AppBuiltin(b,l)->normal_form_ll(funl'->letu=ifT.same_lll'thentelseT.app_builtin~ty:(T.tyt)bl'inku)andnormal_form_llk=matchlwith|[]->k[]|t::tail->normal_form~restrict:lazy_falset(funt'->normal_form_ltail(funl'->k(t'::l')))innormal_form~restrictt(funt->t)let[@inline]eq_c_subst(c1,s1,sc1)(c2,s2,sc2)=C.equalc1c2&&sc1=sc2&&Subst.equals1s2(* Demodulate the clause, with restrictions on which terms to rewrite *)letdemodulate_c=Util.incr_statstat_demodulate_call;letord=Ctx.ord()in(* state for storing proofs and scope *)letst={demod_clauses=[];demod_sc=1;}in(* literals that are eligible for paramodulation. *)leteligible_param=lazy(C.eligible_param(c,0)S.empty)in(* demodulate literals *)letdemod_litilit=(* strictly maximal terms might be blocked *)letstrictly_max=lazy(beginmatchlitwith|Lit.Equation(t1,t2,true)->beginmatchO.compareordt1t2with|Comp.Gt->[t1]|Comp.Lt->[t2]|_->[]end|Lit.Prop(t,true)->[t]|_->[]end)in(* shall we restrict a subterm? only for max terms in positive
equations that are eligible for paramodulation.
NOTE: E's paper mentions that restrictions should occur for
literals eligible for {b resolution}, not paramodulation, but
it seems it might be a typo
*)letrestrict_termt=lazy(Lit.is_poslit&&BV.get(Lazy.forceeligible_param)i&&(* restrict max terms in positive literals eligible for resolution *)CCList.mem~eq:T.equalt(Lazy.forcestrictly_max))inLit.map_no_simp(funt->demod_nf~restrict:(restrict_termt)stct)litin(* demodulate every literal *)letlits=Array.mapidemod_lit(C.litsc)inifCCList.is_emptyst.demod_clausesthen((* no rewriting performed *)SimplM.return_samec)else(assert(not(Lits.equal_comlits(C.litsc)));(* construct new clause *)st.demod_clauses<-CCList.uniq~eq:eq_c_substst.demod_clauses;letproof=Proof.Step.simp~rule:(Proof.Rule.mk"demod")(C.proof_parentc::List.rev_map(fun(c,subst,sc)->C.proof_parent_substSubst.Renaming.none(c,sc)subst)st.demod_clauses)inlettrail=C.trailcin(* we know that demodulating rules have smaller trail *)letnew_c=C.create_a~trail~penalty:(C.penaltyc)litsproofinUtil.debugf~section3"@[<hv2>demodulate@ @[%a@]@ into @[%a@]@ using {@[<hv>%a@]}@]"(funk->letpp_c_sout(c,s,sc)=Format.fprintfout"(@[%a@ :subst %a[%d]@])"C.ppcSubst.ppsscinkC.ppcC.ppnew_c(Util.pp_listpp_c_s)st.demod_clauses);(* return simplified clause *)SimplM.return_newnew_c)letdemodulatec=Util.with_profprof_demodulatedemodulate_c(** Find clauses that [given] may demodulate, add them to set *)letbackward_demodulatesetgiven=Util.enter_profprof_back_demodulate;letord=Ctx.ord()inletrenaming=Subst.Renaming.create()in(* find clauses that might be rewritten by l -> r *)letrecurse~orientedsetlr=I.retrieve_specializations(!_idx_back_demod,1)(l,0)|>Iter.fold(funset(_t',with_pos,subst)->letc=with_pos.C.WithPos.clausein(* subst(l) matches t' and is > subst(r), very likely to rewrite! *)if((oriented||O.compareord(S.FO.applyrenamingsubst(l,0))(S.FO.applyrenamingsubst(r,0))=Comp.Gt)&&C.trail_subsumescgiven)then(* add the clause to the set, it may be rewritten by l -> r *)C.ClauseSet.addcsetelseset)setinletset'=matchC.litsgivenwith|[|Lit.Equation(l,r,true)|]->beginmatchOrdering.compareordlrwith|Comp.Gt->recurse~oriented:truesetlr|Comp.Lt->recurse~oriented:truesetrl|_->letset'=recurse~oriented:falsesetlrinrecurse~oriented:falseset'rl(* both sides can rewrite, but we need to check ordering *)end|_->setinUtil.exit_profprof_back_demodulate;set'letis_tautologyc=letis_tauto=Lits.is_trivial(C.litsc)||Trail.is_trivial(C.trailc)inifis_tautothenUtil.debugf~section3"@[@[%a@]@ is a tautology@]"(funk->kC.ppc);is_tauto(* semantic tautology deletion, using a congruence closure algorithm
to see if negative literals imply some positive literal *)letis_semantic_tautology_real(c:C.t):bool=(* create the congruence closure of all negative equations of [c] *)letcc=Congruence.FO.create~size:8()inletcc=Array.fold_left(funcclit->matchlitwith|Lit.Equation(l,r,false)->Congruence.FO.mk_eqcclr|Lit.Prop(p,false)->Congruence.FO.mk_eqccpT.true_|_->cc)cc(C.litsc)inletres=CCArray.exists(function|Lit.Equation(l,r,true)->(* if l=r is implied by the congruence, then the clause is redundant *)Congruence.FO.is_eqcclr|Lit.Prop(p,true)->Congruence.FO.is_eqccpT.true_|_->false)(C.litsc)inifresthen(Util.incr_statstat_semantic_tautology;Util.debugf~section2"@[@[%a@]@ is a semantic tautology@]"(funk->kC.ppc););resletis_semantic_tautology_c=ifArray.length(C.litsc)>=2&&CCArray.existsLit.is_neg(C.litsc)&&CCArray.existsLit.is_pos(C.litsc)thenis_semantic_tautology_realcelsefalseletis_semantic_tautologyc=Util.with_profprof_semantic_tautologyis_semantic_tautology_cletvar_in_subst_usvsc=S.mem(US.substus)((v:T.var:>InnerTerm.tHVar.t),sc)letbasic_simplifyc=ifC.get_flagflag_simplifiedcthenSimplM.return_samecelse(Util.enter_profprof_basic_simplify;Util.incr_statstat_basic_simplify_calls;letlits=C.litscinlethas_changed=reffalseinlettags=ref[]in(* bv: literals to keep *)letbv=BV.create~size:(Array.lengthlits)truein(* eliminate absurd lits *)Array.iteri(funilit->ifLit.is_absurdlitthen(has_changed:=true;tags:=Lit.is_absurd_tagslit@!tags;BV.resetbvi))lits;(* eliminate inequations x != t *)letus=refUS.emptyinlettry_unifit1sc1t2sc2=tryletsubst'=Unif.FO.unify_full~subst:!us(t1,sc1)(t2,sc2)inhas_changed:=true;BV.resetbvi;us:=subst';withUnif.Fail->()inArray.iteri(funilit->letcan_destr_eq_varv=not(var_in_subst_!usv0)&¬(Type.is_fun(HVar.tyv))inifBV.getbvithenmatchlitwith|Lit.Equation(l,r,false)->beginmatchT.viewl,T.viewrwith|T.Varv,_whencan_destr_eq_varv->(* eligible for destructive Equality Resolution, try to update
[subst]. Careful: in the case [X!=a | X!=b | C] we must
bind X only to [a] or [b], not unify [a] with [b].
NOTE: this also works for HO constraints for unshielded vars *)try_unifil0r0|_,T.Varvwhencan_destr_eq_varv->try_unifir0l0|_->()end|Lit.Equation(l,r,true)whenType.is_prop(T.tyl)->beginmatchT.viewl,T.viewrwith|(T.AppBuiltin(Builtin.True,[]),T.Varx|T.Varx,T.AppBuiltin(Builtin.True,[]))whennot(var_in_subst_!usx0)->(* [C or x=true ---> C[x:=false]] *)begintryletsubst'=US.FO.bind!us(x,0)(T.false_,0)inhas_changed:=true;BV.resetbvi;us:=subst';withUnif.Fail->()end|_->()end|_->())lits;letnew_lits=BV.selectbvlitsinletnew_lits=ifUS.is_empty!usthennew_litselse(assert!has_changed;letsubst=US.subst!usinlettgs=US.tags!usintags:=tgs@!tags;letc_guard=Literal.of_unif_substSubst.Renaming.none!usinc_guard@Lit.apply_subst_listSubst.Renaming.nonesubst(new_lits,0))inletnew_lits=CCList.uniq~eq:Lit.equal_comnew_litsinifnot!has_changed&&List.lengthnew_lits=Array.lengthlitsthen(Util.exit_profprof_basic_simplify;C.set_flagflag_simplifiedctrue;SimplM.return_samec(* no simplification *))else(letparent=ifSubst.is_empty(US.subst!us)thenC.proof_parentcelseC.proof_parent_substSubst.Renaming.none(c,0)(US.subst!us)inletproof=Proof.Step.simp[parent]~tags:!tags~rule:(Proof.Rule.mk"simplify")inletnew_clause=C.create~trail:(C.trailc)~penalty:(C.penaltyc)new_litsproofinUtil.debugf~section3"@[<>@[%a@]@ @[<2>basic_simplifies into@ @[%a@]@]@ with @[%a@]@]"(funk->kC.ppcC.ppnew_clauseUS.pp!us);Util.incr_statstat_basic_simplify;Util.exit_profprof_basic_simplify;SimplM.return_newnew_clause))lethandle_distinct_constantslit=matchlitwith|Lit.Equation(l,r,sign)whenT.is_constl&&T.is_constr->lets1=T.head_exnlands2=T.head_exnrinifID.is_distinct_objects1&&ID.is_distinct_objects2thenifsign=(ID.equals1s2)thenSome(Lit.mk_tauto,[],[Proof.Tag.T_distinct])(* "a" = "a", or "a" != "b" *)elseSome(Lit.mk_absurd,[],[Proof.Tag.T_distinct])(* "a" = "b" or "a" != "a" *)elseNone|_->NoneexceptionFoundMatchofT.t*C.t*S.tletpositive_simplify_reflectc=Util.enter_profprof_pos_simplify_reflect;(* iterate through literals and try to resolve negative ones *)letreciterate_litsacclitsclauses=matchlitswith|[]->List.revacc,clauses|(Lit.Equation(s,t,false)aslit)::lits'->beginmatchequatable_termsclausesstwith|None->(* keep literal *)iterate_lits(lit::acc)lits'clauses|Somenew_clauses->(* drop literal, remember clauses *)iterate_litsacclits'new_clausesend|lit::lits'->iterate_lits(lit::acc)lits'clauses(* try to make the terms equal using some positive unit clauses
from active_set *)andequatable_termsclausest1t2=matchT.Classic.viewt1,T.Classic.viewt2with|_whenT.equalt1t2->Someclauses(* trivial *)|T.Classic.App(f,ss),T.Classic.App(g,ts)whenID.equalfg&&List.lengthss=List.lengthts->(* try to make the terms equal directly *)beginmatchequate_rootclausest1t2with|None->(* otherwise try to make subterms pairwise equal *)letok,clauses=List.fold_left2(fun(ok,clauses)t1't2'->ifokthenmatchequatable_termsclausest1't2'with|None->false,[]|Someclauses->true,clauseselsefalse,[])(true,clauses)sstsinifokthenSomeclauseselseNone|Someclauses->Someclausesend|_->equate_rootclausest1t2(* try to solve it with a unit equality *)(* try to equate terms with a positive unit clause that match them *)andequate_rootclausest1t2=tryUnitIdx.retrieve~sign:true(!_idx_simpl,1)(t1,0)|>Iter.iter(fun(l,r,(_,_,_,c'),subst)->assert(Unif.FO.equal~subst(l,1)(t1,0));ifUnif.FO.equal~subst(r,1)(t2,0)&&C.trail_subsumesc'cthenbegin(* t1!=t2 is refuted by l\sigma = r\sigma *)Util.debugf~section4"@[<2>equate @[%a@]@ and @[%a@]@ using @[%a@]@]"(funk->kT.ppt1T.ppt2C.ppc');raise(FoundMatch(r,c',subst))(* success *)end);None(* no match *)withFoundMatch(_r,c',subst)->Some(C.proof_parent_substSubst.Renaming.none(c',1)subst::clauses)(* success *)in(* fold over literals *)letlits,premises=iterate_lits[](C.litsc|>Array.to_list)[]inifList.lengthlits=Array.length(C.litsc)then((* no literal removed, keep c *)Util.exit_profprof_pos_simplify_reflect;SimplM.return_samec)else(letproof=Proof.Step.simp~rule:(Proof.Rule.mk"simplify_reflect+")(C.proof_parentc::premises)inlettrail=C.trailcandpenalty=C.penaltycinletnew_c=C.create~trail~penaltylitsproofinUtil.debugf~section3"@[@[%a@]@ pos_simplify_reflect into @[%a@]@]"(funk->kC.ppcC.ppnew_c);Util.exit_profprof_pos_simplify_reflect;SimplM.return_newnew_c)letnegative_simplify_reflectc=Util.enter_profprof_neg_simplify_reflect;(* iterate through literals and try to resolve positive ones *)letreciterate_litsacclitsclauses=matchlitswith|[]->List.revacc,clauses|(Lit.Equation(s,t,true)aslit)::lits'->beginmatchcan_refutest,can_refutetswith|None,None->(* keep literal *)iterate_lits(lit::acc)lits'clauses|Somenew_clause,_|_,Somenew_clause->(* drop literal, remember clause *)iterate_litsacclits'(new_clause::clauses)end|lit::lits'->iterate_lits(lit::acc)lits'clauses(* try to remove the literal using a negative unit clause *)andcan_refutest=tryUnitIdx.retrieve~sign:false(!_idx_simpl,1)(s,0)|>Iter.iter(fun(l,r,(_,_,_,c'),subst)->assert(Unif.FO.equal~subst(l,1)(s,0));ifUnif.FO.equal~subst(r,1)(t,0)&&C.trail_subsumesc'cthenbegin(* TODO: useless? *)letsubst=Unif.FO.matching~subst~pattern:(r,1)(t,0)inUtil.debugf~section3"@[neg_reflect eliminates@ @[%a=%a@]@ with @[%a@]@]"(funk->kT.ppsT.pptC.ppc');raise(FoundMatch(r,c',subst))(* success *)end);None(* no match *)withFoundMatch(_r,c',subst)->Some(C.proof_parent_substSubst.Renaming.none(c',1)subst)(* success *)in(* fold over literals *)letlits,premises=iterate_lits[](C.litsc|>Array.to_list)[]inifList.lengthlits=Array.length(C.litsc)then((* no literal removed *)Util.exit_profprof_neg_simplify_reflect;SimplM.return_samec)else(letproof=Proof.Step.simp~rule:(Proof.Rule.mk"simplify_reflect-")(C.proof_parentc::premises)inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)litsproofinUtil.debugf~section3"@[@[%a@]@ neg_simplify_reflect into @[%a@]@]"(funk->kC.ppcC.ppnew_c);Util.exit_profprof_neg_simplify_reflect;SimplM.return_newnew_c)(* ----------------------------------------------------------------------
* subsumption
* ---------------------------------------------------------------------- *)(** raised when a subsuming substitution is found *)exceptionSubsumptionFoundofS.t(** check that every literal in a matches at least one literal in b *)letall_lits_matchasc_absc_b=CCArray.for_all(funlita->CCArray.exists(funlitb->not(Iter.is_empty(Lit.subsumes(lita,sc_a)(litb,sc_b))))b)a(** Compare literals by subsumption difficulty
(see "towards efficient subsumption", Tammet).
We sort by increasing order, so non-ground, deep, heavy literals are
smaller (thus tested early) *)letcompare_literals_subsumptionlitalitb=CCOrd.((* ground literal is bigger *)bool(Lit.is_groundlita)(Lit.is_groundlitb)(* deep literal is smaller *)<?>(mapLit.depth(oppint),lita,litb)(* heavy literal is smaller *)<?>(mapLit.weight(oppint),lita,litb))(* replace the bitvector system by some backtracking scheme?
XXX: maybe not a good idea. the algorithm is actually quite subtle
and needs tight control over the traversal (lookahead of free
variables in next literals, see [check_vars]...) *)(** Check whether [a] subsumes [b], and if it does, return the
corresponding substitution *)letsubsumes_with_(a,sc_a)(b,sc_b):_option=(* a must not have more literals, and it must be possible to bind
all its vars during subsumption *)ifArray.lengtha>Array.lengthb||not(all_lits_matchasc_absc_b)thenNoneelse((* sort a copy of [a] by decreasing difficulty *)leta=Array.copyainlettags=ref[]in(* try to subsumes literals of b whose index are not in bv, with [subst] *)letrectry_permutationsisubstbv=ifi=Array.lengthathenraise(SubsumptionFoundsubst)elseletlita=a.(i)infind_matchedlitaisubstbv0(* find literals of b that are not bv and that are matched by lita *)andfind_matchedlitaisubstbvj=ifj=Array.lengthbthen()(* if litb is already matched, continue *)elseifBV.getbvjthenfind_matchedlitaisubstbv(j+1)else(letlitb=b.(j)inBV.setbvj;(* match lita and litb, then flag litb as used, and try with next literal of a *)letn_subst=ref0inLit.subsumes~subst(lita,sc_a)(litb,sc_b)(fun(subst',tgs)->incrn_subst;tags:=tgs@!tags;try_permutations(i+1)subst'bv);BV.resetbvj;(* some variable of lita occur in a[j+1...], try another literal of b *)if!n_subst>0&¬(check_varslita(i+1))then()(* no backtracking for litb *)elsefind_matchedlitaisubstbv(j+1))(* does some literal in a[j...] contain a variable in l or r? *)andcheck_varslitj=letvars=Lit.varslitinifvars=[]thenfalseelsetryfork=jtoArray.lengtha-1doifList.exists(funv->Lit.var_occursva.(k))varsthenraiseExitdone;falsewithExit->trueintryArray.sortcompare_literals_subsumptiona;letbv=BV.empty()intry_permutations0S.emptybv;Nonewith(SubsumptionFoundsubst)->Util.debugf~section2"(@[<hv>subsumes@ :c1 @[%a@]@ :c2 @[%a@]@ :subst %a%a@]"(funk->kLits.ppaLits.ppbSubst.ppsubstProof.pp_tags!tags);Some(subst,!tags))letsubsumes_withab=Util.enter_profprof_subsumption;Util.incr_statstat_subsumption_call;letres=subsumes_with_abinUtil.exit_profprof_subsumption;resletsubsumesab=matchsubsumes_with(a,0)(b,1)with|None->false|Some_->true(* anti-unification of the two terms with at most one disagreement point *)letanti_unify(t:T.t)(u:T.t):(T.t*T.t)option=matchUnif.FO.anti_unify~cut:1tuwith|Some[pair]->Somepair|_->Noneleteq_subsumes_with(a,sc_a)(b,sc_b)=(* subsume a literal using a = b *)letrecequate_lit_withablit=matchlitwith|Lit.Equation(u,v,true)whennot(T.equaluv)->equate_termsabuv|_->None(* make u=v using a=b once *)andequate_termsabuv=beginmatchanti_unifyuvwith|None->None|Some(u',v')->equate_rootabu'v'end(* check whether a\sigma = u and b\sigma = v, for some sigma;
or the commutation thereof *)andequate_rootabuv:Subst.toption=letcheck_abuv=tryletsubst=Unif.FO.matching~pattern:(a,sc_a)(u,sc_b)inletsubst=Unif.FO.matching~subst~pattern:(b,sc_a)(v,sc_b)inSomesubstwithUnif.Fail->Noneinbeginmatchcheck_abuvwith|Some_ass->s|None->check_bauvendin(* check for each literal *)Util.enter_profprof_eq_subsumption;Util.incr_statstat_eq_subsumption_call;letres=matchawith|[|Lit.Equation(s,t,true)|]->letres=CCArray.find(equate_lit_withst)binbeginmatchreswith|None->None|Somesubst->Util.debugf~section3"@[<2>@[%a@]@ eq-subsumes @[%a@]@ :subst %a@]"(funk->kLits.ppaLits.ppbSubst.ppsubst);Util.incr_statstat_eq_subsumption_success;Somesubstend|_->None(* only a positive unit clause unit-subsumes a clause *)inUtil.exit_profprof_eq_subsumption;resleteq_subsumesab=CCOpt.is_some(eq_subsumes_with(a,1)(b,0))letsubsumed_by_active_setc=Util.enter_profprof_subsumption_set;Util.incr_statstat_subsumed_by_active_set_call;(* if there is an equation in c, try equality subsumption *)lettry_eq_subsumption=CCArray.existsLit.is_eqn(C.litsc)in(* use feature vector indexing *)letres=SubsumIdx.retrieve_subsuming_c!_idx_fvc|>Iter.exists(func'->C.trail_subsumesc'c&&((try_eq_subsumption&&eq_subsumes(C.litsc')(C.litsc))||subsumes(C.litsc')(C.litsc)))inUtil.exit_profprof_subsumption_set;ifresthen(Util.debugf~section3"@[<2>@[%a@]@ subsumed by active set@]"(funk->kC.ppc);Util.incr_statstat_clauses_subsumed;);resletsubsumed_in_active_setaccc=Util.enter_profprof_subsumption_in_set;Util.incr_statstat_subsumed_in_active_set_call;(* if c is a single unit clause *)lettry_eq_subsumption=C.is_unit_clausec&&Lit.is_pos(C.litsc).(0)in(* use feature vector indexing *)letres=SubsumIdx.retrieve_subsumed_c!_idx_fvc|>Iter.fold(funresc'->ifC.trail_subsumescc'thenletredundant=(try_eq_subsumption&&eq_subsumes(C.litsc)(C.litsc'))||subsumes(C.litsc)(C.litsc')inifredundantthen(Util.incr_statstat_clauses_subsumed;C.ClauseSet.addc'res)elsereselseres)accinUtil.exit_profprof_subsumption_in_set;res(* Number of equational lits. Used as an estimation for the difficulty of the subsumption
check for this clause. *)letnum_equationallits=Array.fold_left(funacclit->matchlitwith|Lit.Equation_->acc+1|_->acc)0lits(* ----------------------------------------------------------------------
* contextual literal cutting
* ---------------------------------------------------------------------- *)(* Performs successive contextual literal cuttings *)letreccontextual_literal_cutting_recc=letopenSimplM.InfixinifArray.length(C.litsc)<=1||num_equational(C.litsc)>3||Array.length(C.litsc)>8thenSimplM.return_samecelse((* do we need to try to use equality subsumption? *)lettry_eq_subsumption=CCArray.existsLit.is_eqn(C.litsc)in(* try to remove one literal from the literal array *)letremove_one_litlits=Iter.of_array_ilits|>Iter.filter(fun(_,lit)->not(Lit.is_constraintlit))|>Iter.find_map(fun(i,old_lit)->(* negate literal *)lits.(i)<-Lit.negateold_lit;(* test for subsumption *)SubsumIdx.retrieve_subsuming!_idx_fv(Lits.Seq.to_formlits)(C.trailc|>Trail.labels)|>Iter.filter(func'->C.trail_subsumesc'c)|>Iter.find_map(func'->letsubst=matchiftry_eq_subsumptiontheneq_subsumes_with(C.litsc',1)(lits,0)elseNonewith|Somes->Some(s,[])|None->subsumes_with(C.litsc',1)(lits,0)insubst|>CCOpt.map(fun(subst,tags)->(* remove the literal and recurse *)CCArray.except_idxlitsi,i,c',subst,tags))|>CCFun.tap(fun_->(* restore literal *)lits.(i)<-old_lit))inbeginmatchremove_one_lit(Array.copy(C.litsc))with|None->SimplM.return_samec(* no literal removed *)|Some(new_lits,_,c',subst,tags)->(* hc' allowed us to cut a literal *)assert(List.lengthnew_lits+1=Array.length(C.litsc));letproof=Proof.Step.inference~rule:(Proof.Rule.mk"clc")~tags[C.proof_parentc;C.proof_parent_substSubst.Renaming.none(c',1)subst]inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)new_litsproofinUtil.debugf~section3"@[<2>contextual literal cutting@ in @[%a@]@ using @[%a@]@ gives @[%a@]@]"(funk->kC.ppcC.ppc'C.ppnew_c);Util.incr_statstat_clc;(* try to cut another literal *)SimplM.return_newnew_c>>=contextual_literal_cutting_recend)letcontextual_literal_cuttingc=Util.enter_profprof_clc;letres=contextual_literal_cutting_reccinUtil.exit_profprof_clc;res(* ----------------------------------------------------------------------
* contraction (condensation)
* ---------------------------------------------------------------------- *)exceptionCondensedIntoofLit.tarray*S.t*Subst.Renaming.t*Proof.taglist(** performs condensation on the clause. It looks for two literals l1 and l2 of same
sign such that l1\sigma = l2, and hc\sigma \ {l2} subsumes hc. Then
hc is simplified into hc\sigma \ {l2}.
If there are too many equational literals, the simplification is disabled to
avoid pathologically expensive subsumption checks.
TODO remove this limitation after an efficient subsumption check is implemented. *)letreccondensation_recc=letopenSimplM.InfixinifArray.length(C.litsc)<=1||num_equational(C.litsc)>3||Array.length(C.litsc)>8thenSimplM.return_samecelse(* scope is used to rename literals for subsumption *)letlits=C.litscinletn=Array.lengthlitsintryfori=0ton-1doletlit=lits.(i)inforj=i+1ton-1doletlit'=lits.(j)in(* see whether [lit |= lit'], and if removing [lit] gives a clause
that subsumes c. Also try to swap [lit] and [lit']. *)letsubst_remove_lit=Lit.subsumes(lit,0)(lit',0)|>Iter.map(funs->s,i)andsubst_remove_lit'=Lit.subsumes(lit',0)(lit,0)|>Iter.map(funs->s,j)in(* potential condensing substitutions *)letsubsts=Iter.appendsubst_remove_litsubst_remove_lit'inIter.iter(fun((subst,tags),idx_to_remove)->letnew_lits=Array.sublits0(n-1)inifidx_to_remove<>n-1thennew_lits.(idx_to_remove)<-lits.(n-1);(* remove lit *)letrenaming=Subst.Renaming.create()inletnew_lits=Lits.apply_substrenamingsubst(new_lits,0)in(* check subsumption *)ifsubsumesnew_litslitsthen(raise(CondensedInto(new_lits,subst,renaming,tags))))substsdone;done;SimplM.return_samecwithCondensedInto(new_lits,subst,renaming,tags)->(* clause is simplified *)letproof=Proof.Step.simp~rule:(Proof.Rule.mk"condensation")~tags[C.proof_parent_substrenaming(c,0)subst]inletc'=C.create_a~trail:(C.trailc)~penalty:(C.penaltyc)new_litsproofinUtil.debugf~section3"@[<2>condensation@ of @[%a@] (with @[%a@])@ gives @[%a@]@]"(funk->kC.ppcS.ppsubstC.ppc');(* try to condense further *)Util.incr_statstat_condensation;SimplM.return_newc'>>=condensation_recletcondensationc=Util.with_profprof_condensationcondensation_recc(** {2 Registration} *)(* print index into file *)let_print_idx~ffileidx=CCIO.with_outfile(funoc->letout=Format.formatter_of_out_channelocinFormat.fprintfout"@[%a@]@."fidx;flushoc)letsetup_dot_printers()=letpp_leaf__=()inCCOpt.iter(funfile->Signal.onceSignals.on_dot_output(fun()->_print_idx~f:(TermIndex.to_dotpp_leaf)file!_idx_sup_into))!_dot_sup_into;CCOpt.iter(funfile->Signal.onceSignals.on_dot_output(fun()->_print_idx~f:(TermIndex.to_dotpp_leaf)file!_idx_sup_from))!_dot_sup_from;CCOpt.iter(funfile->Signal.onceSignals.on_dot_output(fun()->_print_idx~f:UnitIdx.to_dotfile!_idx_simpl))!_dot_simpl;CCOpt.iter(funfile->Signal.onceSignals.on_dot_output(fun()->_print_idx~f:(TermIndex.to_dotpp_leaf)file!_idx_back_demod))!_dot_demod_into;()letregister()=letopenSimplM.Infixinletrw_simplifyc=demodulatec>>=basic_simplify>>=positive_simplify_reflect>>=negative_simplify_reflectandactive_simplifyc=condensationc>>=contextual_literal_cuttingandbackward_simplifyc=letset=C.ClauseSet.emptyinbackward_demodulatesetcandredundant=subsumed_by_active_setandbackward_redundant=subsumed_in_active_setandis_trivial=is_tautologyinEnv.add_binary_inf"superposition_passive"infer_passive;Env.add_binary_inf"superposition_active"infer_active;Env.add_unary_inf"equality_factoring"infer_equality_factoring;Env.add_unary_inf"equality_resolution"infer_equality_resolution;ifnot(!_dont_simplify)then(Env.add_rw_simplifyrw_simplify;Env.add_basic_simplifybasic_simplify;Env.add_active_simplifyactive_simplify;Env.add_backward_simplifybackward_simplify);Env.add_redundantredundant;Env.add_backward_redundantbackward_redundant;if!_use_semantic_tautothenEnv.add_is_trivialis_semantic_tautology;Env.add_is_trivialis_trivial;Env.add_lit_rule"distinct_symbol"handle_distinct_constants;setup_dot_printers();()endletkey=Flex_state.create_key()letregister~sup=letmoduleSup=(valsup:S)inletmoduleE=Sup.EnvinE.update_flex_state(Flex_state.addkeysup)(* TODO: move DOT index printing into the extension *)letextension=letactionenv=letmoduleE=(valenv:Env.S)inletmoduleSup=Make(E)inSup.register();register~sup:(moduleSup:S)in{Extensions.defaultwithExtensions.name="superposition";env_actions=[action];}let()=Params.add_opts["--semantic-tauto",Arg.Set_use_semantic_tauto," enable semantic tautology check";"--no-semantic-tauto",Arg.Clear_use_semantic_tauto," disable semantic tautology check";"--dot-sup-into",Arg.String(funs->_dot_sup_into:=Somes)," print superposition-into index into file";"--dot-sup-from",Arg.String(funs->_dot_sup_from:=Somes)," print superposition-from index into file";"--dot-demod",Arg.String(funs->_dot_simpl:=Somes)," print forward rewriting index into file";"--dot-demod-into",Arg.String(funs->_dot_demod_into:=Somes)," print backward rewriting index into file";"--simultaneous-sup",Arg.Bool(funb->_use_simultaneous_sup:=b)," enable/disable simultaneous superposition";"--dont-simplify",Arg.Set_dont_simplify," disable simplification rules";"--sup-at-vars",Arg.Set_sup_at_vars," enable superposition at variables under certain ordering conditions";"--restrict-hidden-sup-at-vars",Arg.Set_restrict_hidden_sup_at_vars," perform hidden superposition at variables only under certain ordering conditions"]