123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433(** {1 Low Level Prover} *)openLogtkmoduleT=LLTermmoduleF=LLTerm.FormmoduleFmt=CCFormattypeform=LLTerm.Form.ttyperes=|R_ok|R_failletsection=LLProof.sectionletstat_solve=Util.mk_stat"llproof.prove"letprof_check=ZProf.make"llproof.prove"(** Congruence Closure *)moduleCC=Congruence.Make(structincludeTletpp=pp_innerletsubtermst=matchT.viewtwith|T.App(f,a)->[f;a]|T.Arrow(a,b)->[a;b]|T.AppBuiltin(Builtin.Box_opaque,_)->[](* simple equality *)|T.AppBuiltin(_,l)->l|T.Ite(a,b,c)->[a;b;c]|T.Bind{body;_}->[body]|Int_pred(l,_)->T.Linexp_int.subtermsl|>Iter.to_list|Rat_pred(l,_)->T.Linexp_rat.subtermsl|>Iter.to_list|T.Const_|T.Var_|T.Type->[]letupdate_subtermstl=matchT.viewt,lwith|T.App(_,_),[f;a]->T.appfa|T.Arrow(_,_),[a;b]->T.arrowab|T.AppBuiltin(b,l1),l1'whenList.lengthl1=List.lengthl1'->T.app_builtin~ty:(T.ty_exnt)bl1'|T.Bind{binder;ty_var;_},[body]->T.bind~ty:(T.ty_exnt)binder~ty_varbody|(T.Const_|T.Var_),[]->t|T.Ite(_,_,_),[a;b;c]->T.iteabc|Int_pred(le,op),l->letl'=T.Linexp_int.subtermsle|>Iter.to_listinassert(List.lengthl=List.lengthl');letmap=List.combinel'linletle'=T.Linexp_int.map(funt->CCList.Assoc.get_exn~eq:T.equaltmap)leinT.int_predle'op|Rat_pred(le,op),l->letl'=T.Linexp_rat.subtermsle|>Iter.to_listinassert(List.lengthl=List.lengthl');letmap=List.combinel'linletle'=T.Linexp_rat.map(funt->CCList.Assoc.get_exn~eq:T.equaltmap)leinT.rat_predle'op|T.App_,_|T.Arrow_,_|T.AppBuiltin_,_|T.Bind_,_|T.Const_,_|T.Var_,_|T.Type,_|T.Ite_,_->assertfalseend)(** Branches of the tableau. A branch is a conjunction of formulas
plus some theory context (congruence closure).
A branch is closed if it's inconsistent *)moduleBranch:sigtypetvalroot:unit->tvalcheck_closed:t->tvalis_closed:t->boolvaladd:t->T.tlist->t(** add the given set of formulas *)valpop_to_expand:t->(T.t*t)option(** remove and return next formula to expand *)typeclosed=|C_not_closed|C_closed_by_diseqofT.t*T.t|C_closed_by_theoryofstringvalto_expand:t->T.tIter.tvalform:t->F.toptionvalclosed:t->closedvalid:t->intvalparent:t->toptionvaldiseq:t->(T.t*T.t)Iter.tvaldebug:tCCFormat.printerend=structmoduleT_set=T.Settypeclosed=|C_not_closed|C_closed_by_diseqofT.t*T.t|C_closed_by_theoryofstringtypet={id:int;(* generative ID *)form:T.toption;to_expand:T_set.t;(* all to expand *)cc:CC.t;diseq:(T.t*T.t)list;(* negative constraints *)mutableclosed:closed;parent:toption;}let[@inline]closedt=t.closedlet[@inline]to_expandt=T_set.to_itert.to_expandlet[@inline]parentt=t.parentlet[@inline]idt=t.idlet[@inline]diseqt=Iter.of_listt.diseqlet[@inline]formt=t.formletroot():t={id=0;form=None;to_expand=T_set.empty;cc=CC.create();diseq=[(F.true_,F.false_)];closed=C_not_closed;parent=None;}let[@inline]is_closedb:bool=matchb.closedwith|C_not_closed->false|_->trueletmk_child=letn=ref1inletaux(f:F.t)(b:t):t={bwithid=CCRef.incr_then_getn;form=Somef;parent=Someb}inaux(* check if some diseq is true *)letcheck_closed(b:t):t=ifis_closedbthenbelse(beginmatchCCList.find_pred(fun(t,u)->CC.is_eqb.cctu)b.diseqwith|None->()|Some(t,u)->b.closed<-C_closed_by_diseq(t,u)end;b)let[@inline]add_cc_tb={bwithcc=CC.addb.cct;}let[@inline]add_cc_eqtub={bwithcc=CC.mk_eqb.cctu}let[@inline]add_diseq_l_tub={bwithdiseq=(t,u)::b.diseq}let[@inline]add_eqtub:t=ifis_closedbthenbelseb|>add_cc_eqtu|>check_closedlet[@inline]add_diseqtub:t=ifis_closedbthenbelseb|>add_cc_t|>add_cc_u|>add_diseq_l_tu|>check_closedlet[@inline]add_to_expandfb=ifis_closedbthenbelse{bwithto_expand=T_set.addfb.to_expand}let[@inline]add_form_to_expandfb=b|>add_to_expandf|>check_closedletrecis_atomicf=matchF.viewfwith|F.Eq_|F.Neq_|F.Atom_|F.Int_pred_|F.Rat_pred_->true|F.Notu->is_atomicu|_->false(* add one formula to [b] *)letadd1_(br:t)(f:T.t):t=letbr=mk_childf@@add_cc_fbrinbeginmatchF.viewfwith|F.Atomt->add_eqtF.true_br|F.True->br|F.False->add_eqF.true_F.false_br|F.Eq(t,u)->add_eqtubr|F.Neq(t,u)->add_diseqtubr|F.Int_pred(_,_)->add_eqfF.true_br(* TODO: decision proc *)|F.Rat_pred(_,_)->add_eqfF.true_br(* TODO: simplex *)|F.Notf'->beginmatchF.viewf'with|F.Eq_|F.Neq_|F.Not_|F.Int_pred_|F.Rat_pred_->assertfalse|F.True->add_diseqF.true_F.false_br|F.False->br|F.Atomt->add_eqtF.false_br|F.Andl->add_form_to_expand(F.or_(List.mapF.not_l))br|F.Orl->add_form_to_expand(F.and_(List.mapF.not_l))br|F.Imply(a,b)->add_form_to_expand(F.and_[a;F.not_b])br|F.Equiv(a,b)->add_form_to_expand(F.or_[F.and_[a;F.not_b];F.and_[b;F.not_a]])br|F.Xor(a,b)->add_form_to_expand(F.and_[F.or_[a;F.not_b];F.or_[b;F.not_a]])br|F.Exists{ty_var;body}->letf=F.forall~ty_var(F.not_body)inbr|>add_eqfF.true_|F.Forall_->br|>add_eqfF.false_end|F.And_|F.Or_->add_form_to_expandfbr|F.Imply(a,b)->add_form_to_expand(F.or_[F.not_a;b])br|F.Xor(a,b)->add_form_to_expand(F.or_[F.and_[a;F.not_b];F.and_[b;F.not_a]])br|F.Equiv(a,b)->add_form_to_expand(F.and_[F.or_[a;F.not_b];F.or_[b;F.not_a]])br|F.Forall_->br|>add_eqfF.true_|F.Exists{ty_var;body}->letf=F.forall~ty_var(F.not_body)inbr|>add_eqfF.false_endletadd1brf:t=ifis_closedbrthenbrelseadd1_brfletaddbl=(* put atomic formulas first *)letl=List.sort(funab->matchis_atomica,is_atomicbwith|true,true|false,false->0|true,false->-1|false,true->1)linList.fold_leftadd1blletpop_to_expandb=ifis_closedbthenNoneelsebeginmatchT_set.chooseb.to_expandwith|f->lettail=T_set.removefb.to_expandinSome(f,{bwithto_expand=tail;})|exceptionNot_found->Noneendletrecunfold_parentsb=matchb.parentwith|None->[b]|Somep->b::unfold_parentspletdebugout(b:t):unit=(* print one branch *)letpp_boutb=Fmt.fprintfout"(@[<hv>branch/%d :closed %B@ :form (%a)@ \
:to_expand (@[<hv>%a@])@])"b.id(is_closedb)(Fmt.someT.pp)b.form(Util.pp_iterT.pp)(T_set.to_iterb.to_expand)inFmt.fprintfout"(@[<v>%a@])"(Util.pp_listpp_b)(unfold_parentsb)end(** Rules for the Tableau calculus *)moduleRule:sigtypetvalapply:tlist->F.t->F.tlistlist(** Return a disjunctive list of conjunctions *)valall:tlistend=structtypet=F.t->F.tlistlistletor_f=matchF.viewfwith|F.Orl->List.mapCCList.returnl|_->[]letand_f=matchF.viewfwith|F.Andl->[l]|_->[]letall=[or_;and_;]let[@inline]apply(l:tlist)(f:F.t):F.tlistlist=CCList.flat_map(funr->rf)lendtypebranch=Branch.t(** main state *)typet={mutableopen_branches:branchlist;mutableclosed_branches:branchlist;mutablesaturated:branchoption;}typefinal_state=tletdebug_tabout(tab:t):unit=Fmt.fprintfout"(@[tab@ :branches (@[<v>%a@])@ :saturated (%a)@])"(Util.pp_listBranch.debug)(List.rev_appendtab.open_branchestab.closed_branches)(Fmt.someBranch.debug)tab.saturated(* solve tableau by expanding it piece by piece *)letsolve_(tab:t):res=whilenot(CCList.is_emptytab.open_branches)&&CCOpt.is_nonetab.saturateddoletb=List.hdtab.open_branchesintab.open_branches<-List.tltab.open_branches;Util.debugf~section3"(@[llproof.check.tab.solve@ %a@])"(funk->kdebug_tabtab);beginmatchBranch.pop_to_expandbwith|None->ifBranch.is_closedbthen(tab.closed_branches<-b::tab.closed_branches)else((* cannot close this branch, it has no form to expand *)tab.saturated<-Someb;)|Some(f,b_tail)->letnew_branches=Rule.applyRule.allf|>List.map(funforms->Branch.addb_tailforms)inassert(not@@CCList.is_emptynew_branches);(* add new branches *)List.iter(funb->ifBranch.is_closedbthen(tab.closed_branches<-b::tab.closed_branches)else(tab.open_branches<-b::tab.open_branches))new_branches;enddone;(* closed all branches, or found saturation *)beginmatchtab.saturatedwith|Someb->(* found a branch that is not refutable *)assert(not(Branch.is_closed@@Branch.check_closedb));Util.debugf~section1"(@[llprover.prove.failed@ :branch %a@])"(funk->kBranch.debugb);R_fail|None->assert(CCList.is_emptytab.open_branches);Util.debugf~section5"(@[llprover.prove.success@ :branches (@[<v>%a@])@ :saturated %a@])"(funk->k(Util.pp_listBranch.debug)tab.closed_branches(Fmt.someBranch.debug)tab.saturated);R_okendletcan_check:LLProof.taglist->bool=letopenBuiltin.Taginletf=function|T_ho->true|T_lra|T_lia|T_ind|T_data|T_live_cnf|T_ho_norm|T_dont_increase_depth|T_distinct|T_ac_|T_ext|T_cannot_orphan->falseinList.for_allfletprove(a:formlist)(b:form)=Util.debugf~section3"(@[@{<yellow>llprover.prove@}@ :hyps (@[<hv>%a@])@ :concl %a@])"(funk->k(Util.pp_listT.pp)aT.ppb);Util.incr_statstat_solve;ZProf.enter_profprof_check;(* prove [a ∧ -b ⇒ ⊥] *)letb_init=Branch.add(Branch.root())(F.not_b::a)inlettab={open_branches=[b_init];closed_branches=[];saturated=None;}inletres=solve_tabinZProf.exit_profprof_check;res,tabletpp_statsout(s:final_state)=letn_open=List.lengths.open_branchesinletn_closed=List.lengths.closed_branchesinFormat.fprintfout"(@[llprover.stats@ :n_branches %d@ :n_closed %d@])"(n_closed+n_open)n_closedlet_to_str_escapefmt=Util.ksprintf_noc~f:Util.escape_dotfmtletpp_dotout(s:final_state):unit=letmoduleISet=Util.Int_setinletas_graph=CCGraph.make(funb->Branch.parentb|>Iter.of_opt|>Iter.map(funv->(),v))inletsaturated_set=Iter.of_opts.saturated|>Iter.fold(funsb->ISet.add(Branch.idb)s)ISet.emptyinletbr_eqb1b2=CCInt.equal(Branch.idb1)(Branch.idb2)inletbr_hashb=Hash.int@@Branch.idbinlettbl=CCGraph.mk_table~eq:br_eq~hash:br_hash32inletattrs_v(b:Branch.t):_list=letcolor=ifBranch.is_closedbthen[`Color"green"]elseifISet.mem(Branch.idb)saturated_setthen[`Color"red"]else[]inletpp_closedoutb=matchBranch.closedbwith|Branch.C_not_closed->Fmt.fprintfout"<not closed> (%d to expand)"(Branch.to_expandb|>Iter.length)|Branch.C_closed_by_diseq(t,u)->Fmt.fprintfout"<closed by `@[<1>%a ≠@ %a@]`>"T.pptT.ppu|Branch.C_closed_by_theorys->Fmt.fprintfout"<closed by theory %S>"sinletlabel=_to_str_escape"@[<v>[%d] %a@ (@[%a@])@]"(Branch.idb)pp_closedb(Fmt.someT.pp)(Branch.formb)in[`Labellabel;`Shape"box";`Style"filled"]@colorinletall_branches=Iter.append(Iter.of_lists.open_branches)(Iter.of_lists.closed_branches)inCCGraph.Dot.pp_all~tbl~eq:br_eq~graph:as_graph~attrs_voutall_branches;()(* TODO *)