123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 Constraint Solving for LPO} *)openLogtkmoduleSI=Msat.Solver_intfletsection=Util.Section.(make~parent:(make"solving")"lpo")(** {5 Constraints} *)moduleConstraint=structtypeexpr=ID.ttypet=|EQofexpr*expr|LEofexpr*expr|LTofexpr*expr|Andoftlist|Oroftlist|Notoft|True(* tautology *)|False(* impossible constraint *)leteqab=EQ(a,b)letneqab=Not(EQ(a,b))letleab=LE(a,b)letltab=LT(a,b)letgeab=LE(b,a)letgtab=LT(b,a)letand_l=Andlletor_l=Orlletnot_t=Nottletimplyab=Or[Nota;b]lettrue_=Trueletfalse_=FalsemoduleSeq=structletexprsck=letreciterc=matchcwith|EQ(a,b)|LE(a,b)|LT(a,b)->ka;kb|Andl|Orl->List.iteriterl|Nott->itert|True|False->()initercendletpp_expr=ID.ppletrecppoutt=matchtwith|EQ(a,b)->Format.fprintfout"(= %a %a)"pp_exprapp_exprb|LE(a,b)->Format.fprintfout"(<= %a %a)"pp_exprapp_exprb|LT(a,b)->Format.fprintfout"(< %a %a)"pp_exprapp_exprb|Andl->Format.fprintfout"(and %a)"(Util.pp_list~sep:" "pp)l|Orl->Format.fprintfout"(or %a)"(Util.pp_list~sep:" "pp)l|Nott->Format.fprintfout"(not %a)"ppt|True->CCFormat.stringout"true"|False->CCFormat.stringout"false"letto_string=CCFormat.to_stringpp(* simplify the constraints *)letrecsimplifyt=matchtwith|EQ(a,b)whenID.equalab->true_|LE(a,b)whenID.equalab->true_|LT(a,b)whenID.equalab->false_|Not(Nott)->simplifyt|NotTrue->true_|NotFalse->true_|Not(Andl)->simplify(or_(List.mapnot_l))|Not(Orl)->simplify(and_(List.mapnot_l))|And[]->true_|Or[]->true_|And[x]->simplifyx|Or[x]->simplifyx|Andl->letl'=List.fold_leftflatten_and[]linbeginmatchl'with|[]->true_|_whenList.memfalse_l'->false_|[x]->x|_->and_l'end|Orl->letl'=List.fold_leftflatten_or[]linbeginmatchl'with|[]->false_|_whenList.memtrue_l'->true_|[x]->x|_->or_l'end|_->tandflatten_oracct=matchsimplifytwith|False->acc|Orl->List.fold_leftflatten_oraccl|t'->t'::accandflatten_andacct=matchsimplifytwith|True->acc|Andl->List.fold_leftflatten_andaccl|t'->t'::accendmoduleSolution=structtypet=(ID.t*ID.t)list(* constraint that prohibits this solution. We build the
clause that makes at least one a>b false. *)letneg_to_constraintsol=letmoduleC=Constraintinletl=List.map(fun(a,b)->C.leab)solinC.or_lletppouts=Util.pp_list(Util.pp_pair~sep:" > "ID.ppID.pp)outsletto_string=CCFormat.to_stringppendmoduleC=Constraint(** Functor to use Sat, and encode/decode the solution.
Use "Solving Partial Order Constraints for LPO Termination", Codish & al *)moduleMakeSolver(X:sigend)=structmoduleLit=structtypet=intletfresh=letn=ref0infun()->incrn;!nletsignx=x>0letabs=absletpp=Format.pp_print_intletdummy=0letnegi=-ilethashi=ilandmax_intletequalij=i=jletnormi=ifi>0theni,SI.Same_signelse-i,SI.NegatedendmoduleSolver=Msat.Make_pure_sat(structmoduleFormula=Littypeproof=unitend)letsolver=Solver.create~size:`Big()(* propositional atoms map symbols to the binary digits of
their index in the precedence *)moduleAtom:sigtypetvalmake:ID.t->int->tvalequal:t->t->boolvalhash:t->intvalprint:Format.formatter->t->unitend=structtypet=ID.t*intletmakesi=s,iletequal(s1,i1)(s2,i2)=ID.equals1s2&&i1=i2lethash(s,i)=Hash.combine342(ID.hashs)(Hash.inti)letprintfmt(s,i)=Format.fprintffmt"%a/%d"ID.ppsiendmoduleAtomTbl=CCHashtbl.Make(Atom)letatom_to_int_=AtomTbl.create16letint_to_atom_=Hashtbl.create16(* unique "literal" (int) for this atom *)letatom_to_lita=tryAtomTbl.findatom_to_int_awithNot_found->leti=Lit.fresh()inAtomTbl.addatom_to_int_ai;Hashtbl.addint_to_atom_ia;i(* get the propositional variable that represents the n-th bit of [s] *)letdigitsn=atom_to_lit(Atom.makesn)moduleF=Msat_tseitin.Make(Lit)(* encode [a < b]_n where [n] is the number of digits.
either the n-th digit of [a] is false and the one of [b] is true,
or they are equal and [a < b]_{n-1}.
@return a formula *)letrecencode_lt~nab=ifn=0thenF.f_falseelseletd_a=F.make_atom(digitan)andd_b=F.make_atom(digitbn)inF.make_or[F.make_and[F.make_notd_a;d_b];F.make_and[F.make_equivd_ad_b;encode_lt~n:(n-1)ab]](* encode [a <= b]_n, with not [b < a]_n. *)letencode_leq~nab=F.make_not(encode_lt~nba)(* encode [a = b]_n, digit per digit *)letrecencode_eq~nab=ifn=0thenF.f_trueelseletd_a=F.make_atom(digitan)andd_b=F.make_atom(digitbn)inF.make_and[F.make_equivd_ad_b;encode_eq~n:(n-1)ab](* encode a constraint with [n] bits into a formula. *)letrecencode_constr~nc=matchcwith|C.EQ(a,b)->encode_eq~nab|C.LE(a,b)->encode_leq~nab|C.LT(a,b)->encode_lt~nab|C.Andl->F.make_and(List.rev_map(encode_constr~n)l)|C.Orl->F.make_or(List.rev_map(encode_constr~n)l)|C.Notc'->F.make_not(encode_constr~nc')|C.True->F.f_true|C.False->F.f_false(* function to extract symbol -> int from a solution *)letint_of_symbolsat~ns:int=letr=ref0infori=ndownto1doletlit=digitsiinletis_true=sat.SI.evallitinifis_truethenr:=2*!r+1elser:=2*!rdone;Util.debugf~section3"index of symbol %a in precedence is %d"(funk->kID.pps!r);!r(* extract a solution *)letget_solutionsat~n(symbols:ID.tlist):(ID.t*ID.t)list=letsyms=List.rev_map(funs->int_of_symbolsat~ns,s)symbolsin(* sort in increasing order *)letsyms=List.sort(fun(n1,_)(n2,_)->n1-n2)symsin(* build solution by imposing f>g iff n(f) > n(g) *)let_,_,sol=List.fold_left(fun(cur_n,other_s,acc)(n,s)->ifn=cur_nthenn,s::other_s,acc(* yet another symbol with rank [n] *)elsebegin(* elements of [other_s] have a lower rank, force them to be smaller *)assert(cur_n<n);letacc=List.fold_left(funaccs'->(s,s')::acc)accother_sinn,[s],accend)(~-1,[],[])symsinsolletprint_litfmti=ifnot(Lit.signi)thenFormat.fprintffmt"¬";tryleta=Hashtbl.findint_to_atom_(Lit.absi)inAtom.printfmtawithNot_found->Format.fprintffmt"L%d"(abs(i:Lit.t:>int))(* tseitin *)letprint_clausefmtc=Format.fprintffmt"@[<hv2>%a@]"(Util.pp_list~sep:" or "print_lit)cletprint_clausesfmtc=Format.fprintffmt"@[<v>%a@]"(Util.pp_list~sep:""print_clause)c(* solve the given list of constraints *)letsolve_listl=(* count the number of symbols *)letsymbols=Iter.of_listl|>Iter.flat_mapC.Seq.exprs|>ID.Set.of_iter|>ID.Set.elementsinletnum=List.lengthsymbolsin(* the number of digits required to map each symbol to a distinct int *)letn=int_of_float(ceil(log(float_of_intnum)/.log2.))inUtil.debugf~section2"constraints on %d symbols -> %d digits (%d bool vars)"(funk->knumn(n*num));letencode_constrc=Util.debugf~section5"encode constr %a..."(funk->kC.ppc);letf=encode_constr~ncinUtil.debugf~section5" ... @[<2>%a@]"(funk->kF.ppf);letclauses=F.make_cnffinUtil.debugf~section5" ... @[<0>%a@]"(funk->kprint_clausesclauses);Solver.assumesolverclauses();Util.debug~section5"form assumed"inList.iterencode_constrl;(* generator of solutions *)letrecnext()=Util.debug~section5"check satisfiability";matchSolver.solvesolverwith|Solver.Satsat->Util.debug~section5"next solution exists, try to extract it...";letsolution=get_solutionsat~nsymbolsinUtil.debugf~section5"... solution is %a"(funk->kSolution.ppsolution);(* obtain another solution: negate current one and continue *)lettl=lazy(negate~nsolution)inLazyList.Cons(solution,tl)|Solver.Unsat_->Util.debug~section5"no solution";LazyList.Nilandnegate~n:_solution=(* negate current solution to get the next one... if any *)letc=Solution.neg_to_constraintsolutioninencode_constrc;matchSolver.solvesolverwith|Solver.Sat_->next()|Solver.Unsat_->LazyList.Nilinlazy(next())endletsolve_multiplel=letl=List.rev_mapC.simplifylinUtil.debugf~section2"lpo: solve constraints %a"(funk->k(CCFormat.listC.pp)l);letmoduleS=MakeSolver(structend)inS.solve_listl(** {5 LPO} *)(* constraint that some term in [l] is bigger than [b] *)letany_bigger~orient_lpolb=matchlwith|[]->C.false_|[x]->orient_lpoxb|_->(* any element of [l] bigger than [r]? *)C.or_(List.rev_map(funx->orient_lpoxb)l)(* [a] bigger than all the elements of [l] *)andall_bigger~orient_lpoal=matchlwith|[]->C.true_|[x]->orient_lpoax|_->C.and_(List.rev_map(funy->orient_lpoay)l)(* constraint for l1 >_lex l2 (lexicographic extension of LPO) *)andlexico_order~eq~orient_lpol1l2=assert(List.lengthl1=List.lengthl2);letc=List.fold_left2(funconstrab->matchconstrwith|Some_->constr|Nonewheneqab->None|None->Some(orient_lpoab))Nonel1l2inmatchcwith|None->C.false_(* they are equal *)|Somec->cmoduleFO=structmoduleT=Termtypeterm=T.tmoduleTC=T.Classic(* constraint for a > b *)letrecorient_lpoab=matchTC.viewa,TC.viewbwith|(TC.Var_|TC.DB_),_->C.false_(* a variable cannot be > *)|_,_whenT.subterm~sub:ba->C.true_(* trivial subterm property --> ok! *)|TC.App(f,((_::_)asl)),TC.App(g,l')whenList.lengthl=List.lengthl'->(* three cases: either some element of [l] is > [r],
or precedence of first symbol applies,
or lexicographic case applies (with non empty lists) *)C.or_[C.and_[C.eqfg;lexico_order~eq:T.equal~orient_lpoll'](* f=g, lexicographic order of subterms *);C.and_[C.gtfg;all_bigger~orient_lpoal'](* f>g and a > all subterms of b *);any_bigger~orient_lpolb(* some subterm of a is > b *)]|TC.App(f,l),TC.App(g,l')->(* two cases: either some element of [l] is > [r],
or precedence of first symbol applies *)C.or_[C.and_[C.gtfg;all_bigger~orient_lpoal'](* f>g and a > all subterms of b *);any_bigger~orient_lpolb(* some subterm of a is > b *)]|TC.App(_,l),_->(* only the subterm property can apply *)any_bigger~orient_lpolb|TC.AppBuiltin_,_|_,TC.AppBuiltin_|TC.NonFO,_->(* no clue... *)C.false_letorient_lpo_listl=List.map(fun(l,r)->letc=orient_lpolrinletc'=C.simplifycinUtil.debugf~section2"constr %a simplified into %a"(funk->kC.ppcC.ppc');c')lendmoduleTypedSTerm=structmoduleT=TypedSTermtypeterm=T.t(* constraint for a > b *)letrecorient_lpoab=matchT.viewa,T.viewbwith|T.Var_,_->C.false_(* a variable cannot be > *)|_whenT.equalab->C.false_|_whenT.is_subterm~strict:trueb~of_:a->C.true_(* trivial subterm property --> ok! *)|T.App(f,l),T.App(g,l')->beginmatchT.viewf,T.viewgwith|T.Constf,T.ConstgwhenList.lengthl=List.lengthl'->(* three cases: either some element of [l] is > [r],
or precedence of first symbol applies,
or lexicographic case applies (with non empty lists) *)C.or_[C.and_[C.eqfg;lexico_order~eq:T.equal~orient_lpoll'](* f=g, lexicographic order of subterms *);C.and_[C.gtfg;all_bigger~orient_lpoal'](* f>g and a > all subterms of b *);any_bigger~orient_lpolb(* some subterm of a is > b *)]|T.Constf,T.Constg->(* two cases: either some element of [l] is > [r],
or precedence of first symbol applies *)C.or_[C.and_[C.gtfg;all_bigger~orient_lpoal'](* f>g and a > all subterms of b *);any_bigger~orient_lpolb(* some subterm of a is > b *)]|_->C.false_(* no clue *)end|T.App(f,l),_whenT.is_constf->(* only the subterm property can apply *)any_bigger~orient_lpolb|_->(* no clue... *)C.false_letorient_lpo_listl=List.map(fun(l,r)->letc=orient_lpolrinletc'=C.simplifycinUtil.debugf~section2"constr %a simplified into %a"(funk->kC.ppcC.ppc');c')lend