Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file avatar.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 Basic Splitting à la Avatar} *)openLogtkopenLibzipperpositionmoduleT=TermmoduleLit=LiteralmoduleFmt=CCFormattype'aprinter=Format.formatter->'a->unitletsection=Util.Section.make~parent:Const.section"avatar"(** {2 Avatar} *)letprof_splits=Util.mk_profiler"avatar.split"letprof_check=Util.mk_profiler"avatar.check"letprof_simp_trail=Util.mk_profiler"avatar.simp_trail"letstat_splits=Util.mk_stat"avatar.splits"letstat_trail_trivial=Util.mk_stat"avatar.trivial_trail"letstat_trail_simplify=Util.mk_stat"avatar.simplify_trail"letstat_backward_simp_trail=Util.mk_stat"avatar.backward_simplify_trail"(* annotate clauses that have been introduced by lemma *)letflag_cut_introduced=SClause.new_flag()moduletypeS=Avatar_intf.Sletk_avatar:(moduleS)Flex_state.key=Flex_state.create_key()letk_show_lemmas:boolFlex_state.key=Flex_state.create_key()letk_simplify_trail:boolFlex_state.key=Flex_state.create_key()letk_back_simplify_trail:boolFlex_state.key=Flex_state.create_key()moduleMake(E:Env.S)(Sat:Sat_solver.S)=structmoduleE=EmoduleCtx=E.CtxmoduleC=E.CmoduleSolver=SatmoduleBLit=BBox.Lit(* union-find that maps vars to list of literals, used for splitting *)moduleUF=UnionFind.Make(structtypekey=T.vartypevalue=Lit.Set.tletequal=HVar.equalType.equallethash=HVar.hashletzero=Lit.Set.emptyletmerge=Lit.Set.unionend)letsimplify_split_(c:C.t):C.tlistoption=letlits=C.litscin(* maps each variable to a list of literals. Sets can be merged whenever
two variables occur in the same literal. *)letuf_vars=C.Seq.varsc|>T.VarSet.of_seq|>T.VarSet.to_list|>UF.create(* set of ground literals (each one is its own component) *)andcluster_ground=refLit.Set.emptyin(* literals belong to either their own ground component, or to every
sets in [uf_vars] associated to their variables *)Array.iter(funlit->letv_opt=Lit.Seq.varslit|>Iter.headinbeginmatchv_optwith|None->(* ground, lit has its own component *)cluster_ground:=Lit.Set.addlit!cluster_ground|Somev->(* merge other variables of the literal with [v] *)Lit.Seq.varslit|>Iter.iter(funv'->(* lit is in the equiv class of [v'] *)UF.adduf_varsv'(Lit.Set.singletonlit);UF.unionuf_varsvv');end)lits;(* now gather all the components as a literal list list *)letcomponents=ref[]inLit.Set.iter(funlit->components:=[lit]::!components)!cluster_ground;UF.iteruf_vars(fun_comp->components:=Lit.Set.to_listcomp::!components);beginmatch!componentswith|[]->assert(Array.lengthlits=0);None|[_]->None|_::_->(* do a simplification! *)Util.incr_statstat_splits;letproof=Proof.Step.esa~rule:(Proof.Rule.mk"split")[Proof.Parent.from@@C.proofc]in(* elements of the trail to keep *)letkeep_trail=C.trailc|>Trail.filterBBox.must_be_keptinletclauses_and_names=List.map(funlits->letlits=Array.of_listlitsinletbool_name=BBox.inject_litslitsinUtil.debugf~section5"(@[<2>inject_lits@ :lits %a@ :blit %a@])"(funk->kLiterals.pplitsBBox.ppbool_name);(* new trail: add the new one *)lettrail=Trail.addbool_namekeep_trailinletc=C.create_a~trail~penalty:(C.penaltyc)litsproofinc,bool_name)!componentsinletclauses,bool_clause=List.splitclauses_and_namesinUtil.debugf~section4"@[split of @[%a@]@ yields @[%a@]@]"(funk->kC.ppc(Util.pp_listC.pp)clauses);(* add boolean constraint: trail(c) => bigor_{name in clauses} name *)letbool_guard=C.trailc|>Trail.to_list|>List.mapTrail.Lit.neginletbool_clause=List.appendbool_clausebool_guardinSat.add_clause~proofbool_clause;Util.debugf~section4"@[constraint clause is @[%a@]@]"(funk->kBBox.pp_bclausebool_clause);(* return the clauses *)Someclausesend(* Avatar splitting *)letsplitc=Util.enter_profprof_splits;letres=ifArray.length(C.litsc)<=1||Literals.is_trivial(C.litsc)thenNoneelsesimplify_split_cinUtil.exit_profprof_splits;resletfilter_absurd_trails_=ref(fun_->true)letfilter_absurd_trailsf=filter_absurd_trails_:=f(* if c.lits = [], negate c.trail *)letcheck_emptyc=ifArray.length(C.litsc)=0&&!filter_absurd_trails_(C.trailc)then(assert(not(Trail.is_empty(C.trailc)));letb_clause=C.trailc|>Trail.to_list|>List.mapTrail.Lit.neginUtil.debugf~section4"@[negate trail of @[%a@] (id %d)@ with @[%a@]@]"(funk->kC.ppc(C.idc)BBox.pp_bclauseb_clause);Sat.add_clause~proof:(C.proof_stepc)b_clause;);[](* never infers anything! *)(* check whether the trail is false and will remain so *)lettrail_is_trivial_(trail:Trail.t):bool=letres=Trail.to_seqtrail|>Iter.find_map(funlit->trymatchSat.valuation_levellitwith|false,0->Somelit(* false at level 0: proven false *)|_->NonewithSat.UndecidedLit->None)inbeginmatchreswith|None->false|Somelit->Util.incr_statstat_trail_trivial;Util.debugf~section3"(@[<hv2>trivial_trail@ :trail @[<hv>%a@]@ :lit `%a`@]"(funk->kC.pp_trailtrailBBox.pplit);trueendlettrail_is_trivialtr=Sat.last_result()=Sat_solver.Sat&&trail_is_trivial_trtypetrail_status=|Tr_trivial|Tr_simplify_intoofBLit.tlist*BLit.tlist(* kept, removed *)|Tr_sameexceptionTrail_is_trivial(* return [new_trail], [is_trivial] *)letsimplify_opt_(trail:Trail.t):trail_status=letn_simpl=ref0intrylettrail,removed=Trail.to_listtrail|>List.partition(funlit->trymatchSat.valuation_levellitwith|true,0->(* [lit] is proven true, it is therefore not necessary
to depend on it *)incrn_simpl;false|false,0->(* [lit] is proven false, the whole trail is trivial *)raiseTrail_is_trivial|_->truewithSat.UndecidedLit->true)inif!n_simpl>0then(assert(removed<>[]);Tr_simplify_into(trail,removed))elseTr_samewithTrail_is_trivial->Tr_trivialletsimplify_opttrail=Util.with_profprof_simp_trailsimplify_opt_trail(* simplify the trail of [c] using boolean literals that have been proven *)letsimplify_trail_c=lettrail=C.trailcin(* remove bool literals made trivial by SAT solver *)beginmatchsimplify_opttrailwith|Tr_same|Tr_trivial->SimplM.return_samec(* handled by [is_trivial] *)|Tr_simplify_into(new_trail,removed_trail)->Util.incr_statstat_trail_simplify;letnew_trail=Trail.of_listnew_trailin(* use SAT resolution proofs for tracking why the trail
has been simplified, so that the other branches that have been
closed can appear in the proof *)letproof_removed=List.map(CCFun.composeSat.get_proof_of_litProof.Parent.from)removed_trailinletproof=Proof.Step.simp~rule:(Proof.Rule.mk"simpl_trail")(Proof.Parent.from(C.proofc)::proof_removed)inletc'=C.create_a~trail:new_trail~penalty:(C.penaltyc)(C.litsc)proofinUtil.debugf~section3"@[<2>clause @[%a@]@ trail-simplifies into @[%a@]@]"(funk->kC.ppcC.ppc');SimplM.return_newc'end(* only simplify if SAT *)letsimplify_trailc=ifSat.last_result()=Sat_solver.Satthensimplify_trail_celseSimplM.return_samecletnew_proved_lits:unit->bool=letnum_proved_last_=ref0infun()->letset=Sat.all_proved()inletn=BLit.Set.cardinalsetinassert(n>=!num_proved_last_);letyes=n>!num_proved_last_innum_proved_last_:=n;yes(* subset of active clauses that have a trivial trail or simplifiable
trail *)letbackward_simplify_trails(_:C.t):C.ClauseSet.t=ifSat.last_result()=Sat_solver.Sat&&new_proved_lits()then(E.ProofState.ActiveSet.clauses()|>C.ClauseSet.to_seq|>Iter.filter(func->not(Trail.is_empty@@C.trailc))|>Iter.filter(func->letok=matchsimplify_opt(C.trailc)with|Tr_trivial|Tr_simplify_into_->true|Tr_same->falseinifokthen(Util.incr_statstat_backward_simp_trail;Util.debugf~section5"(@[<2>backward_simplify_trail@ %a@])"(funk->kC.ppc););ok)|>C.ClauseSet.of_seq)elseC.ClauseSet.emptyletskolem_count_=ref0typecut_res={cut_form:Cut_form.t;(** the lemma itself *)cut_pos:E.C.tlist;(** clauses true if lemma is true *)cut_lit:BLit.t;(** lit that is true if lemma is true *)cut_depth:int;(** if the lemma is used to prove another lemma *)cut_proof:Proof.Step.t;(** where does the lemma come from? *)cut_proof_parent:Proof.Parent.t;(** how to justify sth from the lemma *)cut_reason:unitCCFormat.printeroption;(** Informal reason why the lemma was added *)}letcut_formc=c.cut_formletcut_posc=c.cut_posletcut_litc=c.cut_litletcut_depthc=c.cut_depthletcut_proofc=c.cut_proofletcut_proof_parentc=c.cut_proof_parentletpp_cut_resout(c:cut_res):unit=letpp_depthoutd=ifd>0thenFormat.fprintfout"@ :depth %d"dinletpp_reasonoutr=Format.fprintfout"@ :reason @[%a@]"r()inFormat.fprintfout"(@[<4>@[<hv>cut@ :form @[%a@]@ :lit @[%a@]%a]%a@])"(Util.pp_listE.C.pp)c.cut_posBLit.ppc.cut_litpp_depthc.cut_depth(Fmt.somepp_reason)c.cut_reasonletcut_res_clausesc=Iter.of_listc.cut_pos(* generic mechanism for adding clause(s)
and make a lemma out of them, including Skolemization, etc. *)letintroduce_cut?reason?(penalty=1)?(depth=0)(f:Cut_form.t)proof:cut_res=letbox=BBox.inject_lemmafinletcut_proof_parent=letform=Cut_form.to_s_formfinletst=Statement.lemma~proof:(Proof.Step.lemma@@Proof.Src.internal[])[form]inProof.Parent.from@@Statement.as_proof_istin(* positive clauses *)letproof_pos=Proof.Step.esa~rule:(Proof.Rule.mk"cut")[cut_proof_parent]inletc_pos=List.map(funlits->C.create_a~trail:(Trail.singletonbox)~penaltylitsproof_pos)(Cut_form.csf)in{cut_form=f;cut_pos=c_pos;cut_lit=box;cut_depth=depth;cut_proof=proof;cut_reason=reason;cut_proof_parent;}leton_input_lemma:cut_resSignal.t=Signal.create()leton_lemma:cut_resSignal.t=Signal.create()moduleLemma_tbl=BBox.Lit.Tbl(* map from [cut.cut_lit] to [cut] *)letall_lemmas_:cut_resLemma_tbl.t=Lemma_tbl.create64letprove_lemma_handlers_:(cut_res->C.tlistE.conversion_result)listref=ref[]letadd_prove_lemmax=CCList.Ref.pushprove_lemma_handlers_x(* clauses recently pushed while trying to prove lemmas *)letnew_clauses_from_lemmas_:C.tlistref=ref[](* return the list of new lemmas *)letinf_new_lemmas~full:_()=letl=!new_clauses_from_lemmas_innew_clauses_from_lemmas_:=[];l(* try to prove a lemma, by trying handlers one by one, or just skolemizing *)letprove_lemma(c:cut_res):unit=letdefault()=letg=cut_formcin(* proof step *)letproof=Proof.Step.esa[cut_proof_parentc]~rule:(Proof.Rule.mk"cut")inletvars=Cut_form.varsg|>T.VarSet.to_listinUtil.debugf~section2"(@[<hv2>prove_lemma@ :form %a@ :vars (@[%a@])@])"(funk->kCut_form.ppg(Util.pp_listHVar.pp)vars);(* map variables to skolems *)letsubst:Subst.t=vars|>List.map(funv->letty_v=HVar.tyvinletid=Ind_cst.make_skolemty_vinCtx.declareidty_v;(v,0),(T.const~ty:ty_vid,0))|>Subst.FO.of_list'?init:Nonein(* for each clause, apply [subst] to it and negate its
literals, obtaining a DNF of [¬ And_i ctx_i[case]];
then turn DNF into CNF *)letrenaming=Subst.Renaming.create()inletclauses=beginCut_form.apply_substrenamingsubst(g,0)|>Cut_form.cs|>Util.map_product~f:(funlits->letlits=Array.map(funl->[Literal.negatel])litsinArray.to_listlits)|>CCList.map(funl->letlits=Array.of_listlinlettrail=Trail.singleton(BLit.neg@@cut_litc)inC.create_alitsproof~trail~penalty:1)endinclausesinletrecauxacc=function|[]->List.rev_append(default())acc|proof_handler::tail->beginmatchproof_handlercwith|E.CR_drop|E.CR_skip->auxacctail|E.CR_returncs->List.rev_appendcsacc|E.CR_addcs->aux(List.rev_appendcsacc)tailendin(* add proof clauses to the positive clauses *)letcs=aux(cut_posc)!prove_lemma_handlers_inUtil.debugf~section3"(@[prove_lemma@ :lemma %a@ :clauses (@[<hv>%a@])@])"(funk->kCut_form.pp(cut_formc)(Util.pp_listC.pp)cs);CCList.Ref.push_listnew_clauses_from_lemmas_csletadd_lemma(c:cut_res):unit=ifnot(Lemma_tbl.memall_lemmas_c.cut_lit)then(Util.debugf~section2"(@[<2>add_lemma@ :on `[@[<hv>%a@]]`@ :lit %a@])"(funk->kCut_form.ppc.cut_formBBox.ppc.cut_lit);Lemma_tbl.addall_lemmas_c.cut_litc;(* start a subproof for the lemma *)prove_lemmac;Signal.sendon_lemmac;)else((* already existing lemma *)Util.debugf~section3"(@[<2>add_lemma [already there]@ :on `[@[<hv>%a@]]`@])"(funk->kCut_form.ppc.cut_form);)letadd_imply(l:cut_reslist)(res:cut_res)(p:Proof.Step.t):unit=letc=res.cut_lit::List.map(funcut->BLit.negcut.cut_lit)linUtil.debugf~section3"(@[<2>add_imply@ :premises (@[<hv>%a@])@ :concl %a@ :proof %a@])"(funk->k(Util.pp_listpp_cut_res)lpp_cut_resresProof.Step.ppp);Solver.add_clause~proof:pc;()letlemma_seq:cut_resIter.t=funyield->Lemma_tbl.iter(fun_c->yieldc)all_lemmas_(* is this literal involved in the proof? *)letrecin_proof_of_(p:Proof.t)(lit:BLit.t):bool=leteq_absl1l2=BLit.equal(BLit.absl1)(BLit.absl2)inletin_proof_(p:Proof.Step.t)(lit:BLit.t):bool=List.exists(funparent->in_proof_of_(Proof.Parent.proofparent)lit)(Proof.Step.parentsp)inbeginmatchProof.S.resultpwith|Proof.Res(_,Bool_clause.E_proofl)->List.exists(eq_abslit)l||in_proof_(Proof.S.stepp)lit|_->in_proof_(Proof.S.stepp)litendletprint_lemmasout()=letin_core=matchSat.get_proof_opt()with|None->(fun_->false)|Somep->in_proof_of_pandpp_reasonoutr=Format.fprintfout"@ :reason @[%a@]"r()inletpp_lemmaoutc=letstatus=matchSat.proved_at_0c.cut_litwith|_whenin_corec.cut_lit->"in_proof"|None->"unknown"|Sometrue->"proved"|Somefalse->"refuted"inFormat.fprintfout"@[<4>@[<hv>@{<Green>*@} %s %a@]%a@]"statusCut_form.ppc.cut_form(Fmt.somepp_reason)c.cut_reasoninletl=lemma_seq|>Iter.to_rev_listinFormat.fprintfout"@[<v2>lemmas (%d): {@ %a@,@]}"(List.lengthl)(Util.pp_list~sep:""pp_lemma)l;()letshow_lemmas()=Format.printf"%a@."print_lemmas()letconvert_lemmast=matchStatement.viewstwith|Statement.Lemmal->letproof_st=Statement.proof_stepstinletf=l|>List.map(List.mapCtx.Lit.of_form)|>List.mapArray.of_list|>Cut_form.makeinletproof=Cut_form.csf|>List.map(func->Proof.Parent.from@@Proof.S.mkproof_st@@SClause.mk_proof_res@@SClause.make~trail:Trail.emptyc)|>Proof.Step.esa~rule:(Proof.Rule.mk"lemma")inletcut=introduce_cut~reason:Fmt.(return"in-input")fproofinletall_clauses=cut_res_clausescut|>Iter.to_rev_listinadd_lemmacut;Signal.sendon_input_lemmacut;(* interrupt here *)E.cr_returnall_clauses|_->E.cr_skipletbefore_check_sat=Signal.create()letafter_check_sat=Signal.create()(* Just check the solver *)letcheck_satisfiability~full()=Util.enter_profprof_check;Signal.sendbefore_check_sat();letres=matchSat.check~full()with|Sat_solver.Sat->Util.debug~section3"SAT-solver reports \"SAT\"";[]|Sat_solver.Unsatproof->Util.debug~section1"SAT-solver reports \"UNSAT\"";letproof=Proof.S.stepproofinletc=C.create~trail:Trail.empty~penalty:1[]proofin[c]inSignal.sendafter_check_sat();Util.exit_profprof_check;resletregister~split:do_split()=Util.debugf~section:Const.section2"register extension Avatar (split: %B)"(funk->kdo_split);Sat.set_printerBBox.pp;ifdo_splitthen(E.add_multi_simpl_rulesplit;);E.add_unary_inf"avatar_check_empty"check_empty;E.add_generate"avatar_check_sat"check_satisfiability;E.add_generate"avatar.lemmas"inf_new_lemmas;E.add_clause_conversionconvert_lemma;E.add_is_trivial_trailtrail_is_trivial;ifE.flex_getk_simplify_trailthen(E.add_unary_simplifysimplify_trail;ifE.flex_getk_back_simplify_trailthen(E.add_backward_simplifybackward_simplify_trails;););ifE.flex_getk_show_lemmasthen(Signal.onceSignals.on_exit(fun_->show_lemmas()););(* be sure there is an initial valuation *)ignore(Sat.check~full:true());()endletget_env(moduleE:Env.S):(moduleS)=E.flex_getk_avatarletenabled_=reftrueletshow_lemmas_=reffalseletsimplify_trail_=reftrueletback_simplify_trail_=reftrueletextension=letactionenv=letmoduleE=(valenv:Env.S)inUtil.debug1"create new SAT solver";letmoduleSat=Sat_solver.Make(structend)inSat.setup();letmoduleA=Make(E)(Sat)inE.flex_addk_avatar(moduleA:S);E.flex_addk_show_lemmas!show_lemmas_;E.flex_addk_simplify_trail!simplify_trail_;E.flex_addk_back_simplify_trail!back_simplify_trail_;Util.debug1"enable Avatar";A.register~split:!enabled_()inExtensions.({defaultwithname="avatar";env_actions=[action]})let()=Params.add_opts["--avatar",Arg.Setenabled_," enable Avatar splitting";"--no-avatar",Arg.Clearenabled_," disable Avatar splitting";"--print-lemmas",Arg.Setshow_lemmas_," show status of Avatar lemmas";"--avatar-simp-trail",Arg.Setsimplify_trail_," simplify boolean trails in Avatar";"--no-avatar-simp-trail",Arg.Clearsimplify_trail_," do not simplify boolean trails in Avatar";"--avatar-backward-simp-trail",Arg.Setback_simplify_trail_," backward-simplify boolean trails in Avatar";"--no-avatar-backward-simp-trail",Arg.Clearback_simplify_trail_," do not backward-simplify boolean trails in Avatar"];Params.add_to_mode"ho-complete-basic"(fun()->enabled_:=false);Params.add_to_mode"ho-pragmatic"(fun()->enabled_:=false);Params.add_to_mode"ho-competitive"(fun()->enabled_:=false);Params.add_to_mode"fo-complete-basic"(fun()->enabled_:=false);