12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 Cancellative Inferences} *)openLogtkopenLibzipperpositionmoduleT=TermmoduleLit=LiteralmoduleLits=LiteralsmoduleS=SubstmoduleM=MonomemoduleMF=Monome.FocusmoduleAL=Rat_litmoduleALF=AL.FocusmoduleStmt=StatementmoduleUS=Unif_substletstat_rat_sup=Util.mk_stat"rat.superposition"letstat_rat_cancellation=Util.mk_stat"rat.rat_cancellation"letstat_rat_eq_factoring=Util.mk_stat"rat.eq_factoring"letstat_rat_ineq_chaining=Util.mk_stat"rat.ineq_chaining"letstat_rat_semantic_tautology=Util.mk_stat"rat.semantic_tauto"letstat_rat_ineq_factoring=Util.mk_stat"rat.ineq_factoring"letstat_rat_demod=Util.mk_stat"rat.demod"letstat_rat_backward_demod=Util.mk_stat"rat.backward_demod"letstat_rat_trivial_ineq=Util.mk_stat"rat.redundant_by_ineq.calls"letstat_rat_trivial_ineq_steps=Util.mk_stat"rat.redundant_by_ineq.steps"letstat_rat_demod_ineq=Util.mk_stat"rat.demod_ineq.calls"letstat_rat_demod_ineq_steps=Util.mk_stat"rat.demod_ineq.steps"(*
let stat_rat_reflexivity_resolution = Util.mk_stat "rat.reflexivity_resolution"
*)letprof_rat_sup=Util.mk_profiler"rat.superposition"letprof_rat_cancellation=Util.mk_profiler"rat.rat_cancellation"letprof_rat_eq_factoring=Util.mk_profiler"rat.eq_factoring"letprof_rat_ineq_chaining=Util.mk_profiler"rat.ineq_chaining"letprof_rat_demod=Util.mk_profiler"rat.demod"letprof_rat_backward_demod=Util.mk_profiler"rat.backward_demod"letprof_rat_semantic_tautology=Util.mk_profiler"rat.semantic_tauto"letprof_rat_ineq_factoring=Util.mk_profiler"rat.ineq_factoring"letprof_rat_trivial_ineq=Util.mk_profiler"rat.redundant_by_ineq"letprof_rat_demod_ineq=Util.mk_profiler"rat.demod_ineq"(*
let prof_rat_reflexivity_resolution = Util.mk_profiler "rat.reflexivity_resolution"
*)letsection=Util.Section.make~parent:Const.section"rat-arith"letenable_rat_=reftrueletenable_ac_=reffalseletenable_semantic_tauto_=reftrueletdot_unit_=refNoneletflag_tauto=SClause.new_flag()letflag_computed_tauto=SClause.new_flag()moduletypeS=sigmoduleEnv:Env.SmoduleC:moduletypeofEnv.CmodulePS:moduletypeofEnv.ProofState(** {2 Contributions to Env} *)valregister:unit->unitendletenable_trivial_ineq_=reftrueletenable_demod_ineq_=reftruemoduleMake(E:Env.S):SwithmoduleEnv=E=structmoduleEnv=EmoduleCtx=Env.CtxmoduleC=Env.CmodulePS=Env.ProofStatelet_idx_eq=ref(PS.TermIndex.empty())let_idx_ineq_left=ref(PS.TermIndex.empty())let_idx_ineq_right=ref(PS.TermIndex.empty())let_idx_all=ref(PS.TermIndex.empty())(* unit clauses *)let_idx_unit_eq=ref(PS.TermIndex.empty())let_idx_unit_ineq=ref(PS.TermIndex.empty())(* apply [f] to some subterms of [c] *)letupdatefc=letord=Ctx.ord()in_idx_eq:=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_rat_eq**maxc)(C.litsc)|>Iter.fold(funacc(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})infacctwith_pos)!_idx_eq;letleft,right=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_rat_less**maxc)(C.litsc)|>Iter.fold(fun(left,right)(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})inmatchposwith|Position.Arg(_,Position.Left_)->flefttwith_pos,right|Position.Arg(_,Position.Right_)->left,frighttwith_pos|_->assertfalse)(!_idx_ineq_left,!_idx_ineq_right)in_idx_ineq_left:=left;_idx_ineq_right:=right;_idx_all:=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_rat**maxc)(C.litsc)|>Iter.fold(funacc(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})infacctwith_pos)!_idx_all;()(* simplification set *)letupdate_simplfc=letord=Ctx.ord()inbeginmatchC.litscwith|[|Lit.Rat({AL.op=AL.Equal;_}asalit)|]->letpos=Position.(arg0stop)in_idx_unit_eq:=AL.fold_terms~subterms:false~vars:false~pos~which:`Max~ordalit|>Iter.fold(funacc(t,pos)->assert(not(T.is_vart));letwith_pos=C.WithPos.({term=t;pos;clause=c;})infacctwith_pos)!_idx_unit_eq|[|Lit.Rat({AL.op=AL.Less;_}asalit)|]->letpos=Position.(arg0stop)in_idx_unit_ineq:=if!enable_trivial_ineq_||!enable_demod_ineq_thenAL.fold_terms~subterms:false~vars:false~pos~which:`Max~ordalit|>Iter.fold(funacc(t,pos)->assert(not(T.is_vart));letwith_pos=C.WithPos.({term=t;pos;clause=c;})infacctwith_pos)!_idx_unit_ineqelse!_idx_unit_ineq|_->()end;()let()=Signal.onPS.ActiveSet.on_add_clause(func->if!enable_rat_thenupdatePS.TermIndex.addc;Signal.ContinueListening);Signal.onPS.SimplSet.on_add_clause(func->if!enable_rat_thenupdate_simplPS.TermIndex.addc;Signal.ContinueListening);Signal.onPS.ActiveSet.on_remove_clause(func->if!enable_rat_thenupdatePS.TermIndex.removec;Signal.ContinueListening);Signal.onPS.SimplSet.on_remove_clause(func->if!enable_rat_thenupdate_simplPS.TermIndex.removec;Signal.ContinueListening);()(** {2 Utils} *)(* data required for superposition *)moduleSupInfo=structtypet={active:C.t;active_pos:Position.t;active_lit:AL.Focus.t;active_scope:int;passive:C.t;passive_pos:Position.t;passive_lit:AL.Focus.t;passive_scope:int;subst:Unif_subst.t;}endletrule_canc=Proof.Rule.mk"canc_sup"(* do cancellative superposition *)let_do_cancinfoacc=letopenSupInfoinletord=Ctx.ord()inletrenaming=Subst.Renaming.create()inletus=info.substinletidx_a,_=Lits.Pos.cutinfo.active_posinletidx_p,_=Lits.Pos.cutinfo.passive_posinlets_a=info.active_scopeands_p=info.passive_scopeinletsubst=Unif_subst.substusinletlit_a=ALF.apply_substrenamingsubst(info.active_lit,s_a)inletlit_p=ALF.apply_substrenamingsubst(info.passive_lit,s_p)inUtil.debugf~section5"@[<2>arith superposition@ between @[%a[%d]@]@ and @[%a[%d]@]@ (subst @[%a@])...@]"(funk->kC.ppinfo.actives_aC.ppinfo.passives_pSubst.ppsubst);(* check ordering conditions *)ifC.is_maxlit(info.active,s_a)subst~idx:idx_a&&C.is_maxlit(info.passive,s_p)subst~idx:idx_p&&ALF.is_max~ordlit_a(* && ALF.is_max ~ord lit_p *)then((* the active literals *)letlit_a,lit_p=ALF.scalelit_alit_pin(* other literals *)letlits_a=CCArray.except_idx(C.litsinfo.active)idx_ainletlits_a=Lit.apply_subst_listrenamingsubst(lits_a,s_a)inletlits_p=CCArray.except_idx(C.litsinfo.passive)idx_pinletlits_p=Lit.apply_subst_listrenamingsubst(lits_p,s_p)in(* new literal: lit_a=[t+m1=m2], lit_p=[t'+m1' R m2'] for some
relation R. Now let's replace t' by [m2-m1] in lit', ie,
build m = [m1'-m2'+(m2-m1) R 0]. *)letmf_a,m_a=matchlit_awith|ALF.Left(AL.Equal,mf,m)|ALF.Right(AL.Equal,m,mf)->mf,m|_->assertfalseinletnew_lit=matchlit_pwith|ALF.Left(op,mf_p,m_p)->Lit.mk_rat_opop(M.sum(MF.restmf_p)m_a)(M.summ_p(MF.restmf_a))|ALF.Right(op,m_p,mf_p)->Lit.mk_rat_opop(M.summ_p(MF.restmf_a))(M.sum(MF.restmf_p)m_a)inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits_a@lits_pin(* build clause *)letproof=Proof.Step.inference~tags:[Proof.Tag.T_lra]~rule:rule_canc[C.proof_parent_substrenaming(info.active,s_a)subst;C.proof_parent_substrenaming(info.passive,s_p)subst]inlettrail=C.trail_l[info.active;info.passive]inletpenalty=C.penaltyinfo.active+C.penaltyinfo.passiveinletnew_c=C.create~penalty~trailall_litsproofinUtil.debugf~section5"@[<2>... gives@ @[%a@]@]"(funk->kC.ppnew_c);Util.incr_statstat_rat_sup;new_c::acc)else(Util.debug~section5"... has bad ordering conditions";acc)letcanc_sup_activec=Util.enter_profprof_rat_sup;letord=Ctx.ord()inleteligible=C.Eligible.(pos**maxc**filterLit.is_rat_eq)inletsc_a=0andsc_p=1inletres=Lits.fold_rat_terms~eligible~which:`Max~ord(C.litsc)|>Iter.fold(funacc(t,active_lit,active_pos)->assert(ALF.opactive_lit=AL.Equal);Util.debugf~section5"@[<2>active canc. sup.@ with @[%a@]@ in @[%a@]@]"(funk->kALF.ppactive_litC.ppc);PS.TermIndex.retrieve_unifiables(!_idx_all,sc_p)(t,sc_a)|>Iter.fold(funacc(t',with_pos,subst)->letpassive=with_pos.C.WithPos.clauseinletpassive_pos=with_pos.C.WithPos.posinletpassive_lit=Lits.View.get_rat_exn(C.litspassive)passive_posinUtil.debugf~section5"@[<4> possible match:@ @[%a@]@ in @[%a@]@]"(funk->kALF.pppassive_litC.pppassive);(* now to unify active_lit and passive_lit further *)ifT.is_vart||T.is_vart'thenaccelseALF.unify~subst(active_lit,sc_a)(passive_lit,sc_p)|>Iter.fold(funacc(active_lit,passive_lit,subst)->letinfo=SupInfo.({active=c;active_pos;active_lit;active_scope=sc_a;passive;passive_pos;passive_lit;passive_scope=sc_p;subst;})in_do_cancinfoacc)acc)acc)[]inUtil.exit_profprof_rat_sup;resletcanc_sup_passivec=Util.enter_profprof_rat_sup;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**arith)inletsc_a=0andsc_p=1inletres=Lits.fold_rat_terms~eligible~which:`All~ord(C.litsc)|>Iter.fold(funacc(t,passive_lit,passive_pos)->Util.debugf~section5"@[<2>passive canc. sup.@ with @[%a@]@ in @[%a@]@]"(funk->kALF.pppassive_litC.ppc);PS.TermIndex.retrieve_unifiables(!_idx_eq,sc_a)(t,sc_p)|>Iter.fold(funacc(t',with_pos,subst)->letactive=with_pos.C.WithPos.clauseinletactive_pos=with_pos.C.WithPos.posinletactive_lit=Lits.View.get_rat_exn(C.litsactive)active_posin(* must have an equation as active lit *)matchALF.opactive_litwith|AL.Equalwhennot(T.is_vart)&¬(T.is_vart')->Util.debugf~section5"@[<4> possible match:@ @[%a@]@ in @[%a@]@]"(funk->kALF.pppassive_litC.ppc);(* unify literals further *)ALF.unify~subst(active_lit,sc_a)(passive_lit,sc_p)|>Iter.fold(funacc(active_lit,passive_lit,subst)->letinfo=SupInfo.({active;active_pos;active_lit;active_scope=sc_a;passive=c;passive_pos;passive_lit;passive_scope=sc_p;subst;})in_do_cancinfoacc)acc|_->acc)acc)[]inUtil.exit_profprof_rat_sup;resexceptionSimplifyIntoofAL.t*C.t*S.t(* how to simplify the passive lit with the active lit, in one step *)let_try_demod_step~substpassive_lit_s_pcposactive_lits_ac'_pos'=letord=Ctx.ord()inleti=Lits.Pos.idxposinletrenaming=S.Renaming.create()inletactive_lit'=ALF.apply_substrenamingsubst(active_lit,s_a)in(* restrictions:
- the rewriting term must be bigger than other terms
(in other words, the inference is strictly decreasing)
- all variables of active clause must be bound by subst
- must not rewrite itself (c != c')
- trail(active) must subsume trail(passive) *)ifALF.is_strictly_max~ordactive_lit'&&(C.Seq.varsc'|>Iter.for_all(funv->S.memsubst((v:Type.tHVar.t:>InnerTerm.tHVar.t),s_a)))&&((C.litsc|>Array.length)>1||not(Lit.equal(C.litsc).(i)(C.litsc').(0))||not(ALF.is_max~ordpassive_lit&&C.is_maxlit(c,0)S.empty~idx:i))&&(C.trail_subsumesc'c)then((* we know all variables of [active_lit] are bound, no need
for a renaming *)letactive_lit=ALF.apply_substSubst.Renaming.nonesubst(active_lit,s_a)inletactive_lit,passive_lit=ALF.scaleactive_litpassive_litinmatchactive_lit,passive_litwith|ALF.Left(AL.Equal,mf1,m1),_|ALF.Right(AL.Equal,m1,mf1),_->letnew_lit=ALF.replacepassive_lit(M.differencem1(MF.restmf1))inraise(SimplifyInto(new_lit,c',subst))|_->())else()(* reduce an arithmetic literal to its current normal form *)letrec_demod_lit_nf~add_lit~add_premise~ica_lit=letord=Ctx.ord()inlets_a=1ands_p=0in(* scopes *)begintryAL.fold_terms~pos:Position.stop~vars:false~which:`Max~ord~subterms:falsea_lit|>Iter.iter(fun(t,lit_pos)->assert(not(T.is_vart));letpassive_lit=ALF.get_exna_litlit_posin(* search for generalizations of [t] *)PS.TermIndex.retrieve_generalizations(!_idx_unit_eq,s_a)(t,s_p)|>Iter.iter(fun(_t',with_pos,subst)->letc'=with_pos.C.WithPos.clauseinletpos'=with_pos.C.WithPos.posinassert(C.is_unit_clausec');assert(Lits.Pos.idxpos'=0);letactive_lit=Lits.View.get_rat_exn(C.litsc')pos'inletpos=Position.(argilit_pos)in_try_demod_step~substpassive_lits_pcposactive_lits_ac'pos'));(* could not simplify, keep the literal *)add_lit(Lit.mk_rata_lit)withSimplifyInto(a_lit',c',subst)->(* lit ----> lit' *)add_premisec'subst;(* recurse until the literal isn't reducible *)Util.debugf~section4"(@[<hv2>rewrite_rat_arith@ :lit `%a`@ :into `%a`@ \
:using @[%a@]@ :subst @[%a@]@])"(funk->kAL.ppa_litAL.ppa_lit'C.ppc'S.ppsubst);_demod_lit_nf~add_premise~add_lit~ica_lit'endleteq_c_subst(c1,s1)(c2,s2)=C.equalc1c2&&Subst.equals1s2(* demodulation (simplification) *)let_demodulationc=Util.enter_profprof_rat_demod;letdid_simplify=reffalseinletlits=ref[]in(* simplified literals *)letadd_litl=lits:=l::!litsinletclauses=ref[]in(* simplifying clauses *)(* add a rewriting clause *)letadd_premisec'subst=did_simplify:=true;clauses:=(c',subst)::!clausesin(* simplify each and every literal *)Lits.fold_lits~eligible:C.Eligible.always(C.litsc)|>Iter.iter(fun(lit,i)->matchlitwith|Lit.Rata_lit->_demod_lit_nf~add_lit~add_premise~ica_lit|_->add_litlit(* keep non-arith literals *));(* build result clause (if it was simplified) *)letres=if!did_simplifythen(clauses:=CCList.uniq~eq:eq_c_subst!clauses;letproof=Proof.Step.inference~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mk"canc_demod")(C.proof_parentc::List.rev_map(fun(c,subst)->C.proof_parent_substSubst.Renaming.none(c,1)subst)!clauses)inlettrail=C.trailcinletnew_c=C.create~penalty:(C.penaltyc)~trail(List.rev!lits)proofinUtil.incr_statstat_rat_demod;Util.debugf~section5"@[<2>arith demodulation@ of @[%a@]@ with [@[%a@]]@ gives @[%a@]@]"(funk->letpp_c_sout(c,s)=Format.fprintfout"(@[%a@ :subst %a@])"C.ppcSubst.ppsinkC.ppc(Util.pp_listpp_c_s)!clausesC.ppnew_c);SimplM.return_newnew_c)elseSimplM.return_samecinUtil.exit_profprof_rat_demod;resletcanc_demodulationc=_demodulationc(* find clauses in which some literal could be rewritten by [c], iff
[c] is a positive unit arith clause *)letcanc_backward_demodulationc=Util.enter_profprof_rat_backward_demod;letord=Ctx.ord()inletres=C.ClauseSet.emptyinletres=matchC.litscwith|[|Lit.Rat({AL.op=AL.Equal;_}asalit)|]->AL.fold_terms~vars:false~which:`Max~subterms:false~ordalit|>Iter.fold(funacc(t,pos)->PS.TermIndex.retrieve_specializations(!_idx_all,0)(t,1)|>Iter.fold(funacc(_t',with_pos,subst)->letc'=with_pos.C.WithPos.clausein(* check whether the term [t] is indeed maximal in
its literal (and clause) after substitution *)letalit'=ALF.get_exnalitposinletalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesc'c&&ALF.is_max~ordalit'then(Util.incr_statstat_rat_backward_demod;C.ClauseSet.addc'acc)elseacc)acc)C.ClauseSet.empty|_->res(* no demod *)inUtil.exit_profprof_rat_backward_demod;resletcancellationc=Util.enter_profprof_rat_cancellation;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**arith)inletres=Lits.fold_rat~eligible(C.litsc)|>Iter.fold(funacc(a_lit,pos)->letidx=Lits.Pos.idxposin(* cancellation depends on what the literal looks like *)let{AL.left=m1;right=m2;op}=a_litinUtil.debugf~section5"@[<2>try cancellation@ in @[%a@]@]"(funk->kAL.ppa_lit);(* try to unify terms in [m1] and [m2] *)MF.unify_mm(m1,0)(m2,0)|>Iter.fold(funacc(mf1,mf2,us)->letrenaming=Subst.Renaming.create()inletsubst=US.substusinletmf1'=MF.apply_substrenamingsubst(mf1,0)inletmf2'=MF.apply_substrenamingsubst(mf2,0)inletis_max_lit=C.is_maxlit(c,0)subst~idxinUtil.debugf~section5"@[<4>... candidate:@ @[%a@] (max lit ? %B)@]"(funk->kS.ppsubstis_max_lit);ifis_max_lit&&MF.is_max~ordmf1'&&MF.is_max~ordmf2'then((* do the inference *)letlits'=CCArray.except_idx(C.litsc)idxinletlits'=Lit.apply_subst_listrenamingsubst(lits',0)inletnew_lit=Lit.mk_rat_opop(MF.restmf1')(MF.restmf2')inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits'inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mk"cancellation")[C.proof_parent_substrenaming(c,0)subst]inlettrail=C.trailcinletpenalty=C.penaltycinletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section3"@[<2>cancellation@ of @[%a@]@ (with %a)@ into @[%a@]@]"(funk->kC.ppcSubst.ppsubstC.ppnew_c);Util.incr_statstat_rat_cancellation;new_c::acc)elseacc)acc)[]inUtil.exit_profprof_rat_cancellation;resletrule_canc_eq_fact=Proof.Rule.mk"rat_eq_factoring"letcanc_equality_factoringc=Util.enter_profprof_rat_eq_factoring;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**filterLit.is_rat_eq)inletres=Lits.fold_rat_terms~which:`Max~eligible~ord(C.litsc)|>Iter.fold(funacc(t1,lit1,pos1)->assert(ALF.oplit1=AL.Equal);letidx1=Lits.Pos.idxpos1in(* lit1 is the factored literal *)Lits.fold_rat_terms~which:`Max~ord~eligible:C.Eligible.(filterLit.is_rat_eq)(C.litsc)|>Iter.fold(funacc(t2,lit2,pos2)->assert(ALF.oplit2=AL.Equal);letidx2=Lits.Pos.idxpos2inletmf1=ALF.focused_monomelit1andmf2=ALF.focused_monomelit2intryifidx1=idx2thenraiseUnif.Fail;(* exit *)letsubst=Unif.FO.unify_full(t1,0)(t2,0)inUtil.debugf~section5"@[<2>arith canc. eq. factoring:@ possible match in @[%a@]@ (at %d, %d)@]"(funk->kC.ppcidx1idx2);MF.unify_ff~subst(mf1,0)(mf2,0)|>Iter.fold(funacc(_,_,us)->letrenaming=Subst.Renaming.create()inletsubst=US.substusinletlit1'=ALF.apply_substrenamingsubst(lit1,0)inletlit2'=ALF.apply_substrenamingsubst(lit2,0)inifC.is_maxlit(c,0)subst~idx:idx1&&ALF.is_max~ordlit1'&&ALF.is_max~ordlit2'then((* lit1 is a.t + l1 = l2,
lit2 is a'.t + l1' = l2',
so we scale them, and replace lit1 with
a'.l1 + a.l2' != a'.l2 + a.l1' *)letlit1',lit2'=ALF.scalelit1'lit2'inletm1=ALF.opposite_monomelit1'andmf1=ALF.focused_monomelit1'andm2=ALF.opposite_monomelit2'andmf2=ALF.focused_monomelit2'in(* m1 != m2, translated as two ineqs *)letnew_lits=letx=M.summ1(MF.restmf2)inlety=M.summ2(MF.restmf1)in[Lit.mk_rat_lessxy;Lit.mk_rat_lessyx]inletother_lits=CCArray.except_idx(C.litsc)idx1inletother_lits=Lit.apply_subst_listrenamingsubst(other_lits,0)inletc_guard=Literal.of_unif_substrenamingusin(* apply subst and build clause *)letall_lits=c_guard@new_lits@other_litsinletproof=Proof.Step.inference~tags:[Proof.Tag.T_lra]~rule:rule_canc_eq_fact[C.proof_parent_substrenaming(c,0)subst]inletpenalty=C.penaltycandtrail=C.trailcinletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section5"@[<2>arith_eq_factoring:@ @[%a@]@ gives @[%a@]@]"(funk->kC.ppcC.ppnew_c);Util.incr_statstat_rat_eq_factoring;new_c::acc)elseacc)accwithUnif.Fail->acc)acc)[]inUtil.exit_profprof_rat_eq_factoring;res(** Data necessary to fully describe a chaining inference.
[left] is basically the clause/literal in which the chained
term is on the left of <,
[right] is the other one. *)moduleChainingInfo=structtypet={left:C.t;left_scope:int;left_pos:Position.t;left_lit:AL.Focus.t;right:C.t;right_scope:int;right_pos:Position.t;right_lit:AL.Focus.t;subst:US.t;}end(* range from low to low+len *)(* cancellative chaining *)let_do_chaininginfoacc=letopenChainingInfoinletord=Ctx.ord()inletrenaming=S.Renaming.create()inletus=info.substinletidx_l,_=Lits.Pos.cutinfo.left_posinletidx_r,_=Lits.Pos.cutinfo.right_posinlets_l=info.left_scopeands_r=info.right_scopeinletsubst=Unif_subst.substusinletlit_l=ALF.apply_substrenamingsubst(info.left_lit,s_l)inletlit_r=ALF.apply_substrenamingsubst(info.right_lit,s_r)inUtil.debugf~section5"@[<2>arith chaining@ between @[%a[%d]@]@ and @[%a[%d]@]@ (subst @[%a@])...@]"(funk->kC.ppinfo.lefts_lC.ppinfo.rights_rSubst.ppsubst);(* check ordering conditions *)ifC.is_maxlit(info.left,s_l)subst~idx:idx_l&&C.is_maxlit(info.right,s_r)subst~idx:idx_r&&ALF.is_max~ordlit_l&&ALF.is_max~ordlit_rthen((* scale literals *)letlit_l,lit_r=ALF.scalelit_llit_rinmatchlit_l,lit_rwith|ALF.Left(AL.Less,mf_1,m1),ALF.Right(AL.Less,m2,mf_2)->(* m2 ≤ mf_2 and mf_1 ≤ m1, with mf_1 and mf_2 sharing the same
focused term. We deduce m2 + mf_1 ≤ m1 + mf_2 and cancel the
term out (after scaling) *)assert(Q.equal(MF.coeffmf_1)(MF.coeffmf_2));letnew_lit=Lit.mk_rat_less(M.summ2(MF.restmf_1))(M.summ1(MF.restmf_2))inletlits_l=CCArray.except_idx(C.litsinfo.left)idx_linletlits_l=Lit.apply_subst_listrenamingsubst(lits_l,s_l)inletlits_r=CCArray.except_idx(C.litsinfo.right)idx_rinletlits_r=Lit.apply_subst_listrenamingsubst(lits_r,s_r)inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits_l@lits_rinletproof=Proof.Step.inference~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mk"canc_ineq_chaining")[C.proof_parent_substrenaming(info.left,s_l)subst;C.proof_parent_substrenaming(info.right,s_r)subst]inlettrail=C.trail_l[info.left;info.right]in(* penalty for some chaining *)letpenalty=C.penaltyinfo.left+C.penaltyinfo.right+3(* nested chainings are dangerous *)+(ifMF.termmf_1|>T.is_varthen10else0)+(ifMF.termmf_2|>T.is_varthen10else0)inletnew_c=C.create~penalty~trailall_litsproofinUtil.debugf~section5"@[<2>ineq chaining@ of @[%a@]@ and @[%a@]@ gives @[%a@]@]"(funk->kC.ppinfo.leftC.ppinfo.rightC.ppnew_c);Util.incr_statstat_rat_ineq_chaining;new_c::acc|_->assertfalse)elseaccletcanc_ineq_chainingc=Util.enter_profprof_rat_ineq_chaining;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**filterLit.is_rat_less)inletsc_l=0andsc_r=1inletres=Lits.fold_rat_terms~eligible~ord~which:`Max(C.litsc)|>Iter.fold(funacc(t,lit,pos)->matchlitwith|_whenT.is_vart->acc(* ignore variables *)|ALF.Left(AL.Less,mf_l,_)->(* find a right-chaining literal in some other clause *)PS.TermIndex.retrieve_unifiables(!_idx_ineq_right,sc_r)(t,sc_l)|>Iter.fold(funacc(_t',with_pos,subst)->letright=with_pos.C.WithPos.clauseinletright_pos=with_pos.C.WithPos.posinletlit_r=Lits.View.get_rat_exn(C.litsright)right_posinmatchlit_rwith|ALF.Right(AL.Less,_,mf_r)->MF.unify_ff~subst(mf_l,sc_l)(mf_r,sc_r)|>Iter.fold(funacc(_,_,subst)->letinfo=ChainingInfo.({left=c;left_scope=sc_l;left_lit=lit;left_pos=pos;right;right_scope=sc_r;right_lit=lit_r;right_pos;subst;})in_do_chaininginfoacc)acc|_->acc)acc|ALF.Right(AL.Less,_,mf_r)->(* find a right-chaining literal in some other clause *)PS.TermIndex.retrieve_unifiables(!_idx_ineq_left,sc_l)(t,sc_r)|>Iter.fold(funacc(_t',with_pos,subst)->letleft=with_pos.C.WithPos.clauseinletleft_pos=with_pos.C.WithPos.posinletlit_l=Lits.View.get_rat_exn(C.litsleft)left_posinmatchlit_lwith|ALF.Left(AL.Less,mf_l,_)->MF.unify_ff~subst(mf_l,sc_l)(mf_r,sc_r)|>Iter.fold(funacc(_,_,subst)->letinfo=ChainingInfo.({left;left_scope=sc_l;left_lit=lit_l;left_pos;subst;right=c;right_scope=sc_r;right_lit=lit;right_pos=pos;})in_do_chaininginfoacc)acc|_->acc)acc|_->assertfalse)[]inUtil.exit_profprof_rat_ineq_chaining;res(* TODO: update with equality case, check that signs correct *)letcanc_ineq_factoringc=Util.enter_profprof_rat_ineq_factoring;letord=Ctx.ord()inletacc=ref[]in(* do the factoring if ordering conditions are ok *)let_do_factoring~subst:uslit1lit2ij=letrenaming=S.Renaming.create()inletsubst=US.substusinletlit1=ALF.apply_substrenamingsubst(lit1,0)inletlit2=ALF.apply_substrenamingsubst(lit2,0)in(* same coefficient for the focused term *)letlit1,lit2=ALF.scalelit1lit2inmatchlit1,lit2with|ALF.Left(AL.Less,mf1,m1),ALF.Left(AL.Less,mf2,m2)|ALF.Right(AL.Less,m1,mf1),ALF.Right(AL.Less,m2,mf2)->(* mf1 ≤ m1 or mf2 ≤ m2 (symmetry with > if needed)
so we deduce that if m1-mf1.rest ≤ m2 - mf2.rest
then the first literal implies the second, so we only
keep the second one *)if(C.is_maxlit(c,0)subst~idx:i||C.is_maxlit(c,0)subst~idx:j)&&(ALF.is_max~ordlit1||ALF.is_max~ordlit2)then(letleft=matchlit1withALF.Left_->true|_->falsein(* remove lit1, add the guard *)letother_lits=CCArray.except_idx(C.litsc)iinletother_lits=Lit.apply_subst_listrenamingsubst(other_lits,0)in(* build new literal *)letnew_lit=ifleftthenLit.mk_rat_less(M.differencem1(MF.restmf1))(M.differencem2(MF.restmf2))elseLit.mk_rat_less(M.differencem2(MF.restmf2))(M.differencem1(MF.restmf1))in(* negate the literal to obtain a guard *)letnew_lit=Lit.negatenew_litinletc_guard=Literal.of_unif_substrenamingusinletlits=new_lit::c_guard@other_litsin(* build clauses *)letproof=Proof.Step.inference~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mk"canc_ineq_factoring")[C.proof_parent_substrenaming(c,0)subst]inlettrail=C.trailcandpenalty=C.penaltycinletnew_c=C.create~trail~penaltylitsproofinUtil.debugf~section5"@[<2>ineq factoring@ of @[%a@]@ gives @[%a@]@"(funk->kC.ppcC.ppnew_c);Util.incr_statstat_rat_ineq_factoring;acc:=new_c::!acc)|_->()in(* traverse the clause to find matching pairs *)leteligible=C.Eligible.(maxc**filterLit.is_rat_less)inLits.fold_rat~eligible(C.litsc)|>Iter.iter(fun(lit1,pos1)->leti=Lits.Pos.idxpos1inleteligible'=C.Eligible.(filterLit.is_rat_less)inLits.fold_rat~eligible:eligible'(C.litsc)|>Iter.iter(fun(lit2,pos2)->letj=Lits.Pos.idxpos2inmatchlit1,lit2with|_wheni=j->()(* need distinct lits *)|{AL.op=AL.Less;left=l1;right=r1},{AL.op=AL.Less;left=l2;right=r2}->(* see whether we have l1 < a.x + mf1 and l2 < a.x + mf2 *)MF.unify_mm(r1,0)(r2,0)(fun(mf1,mf2,subst)->letlit1=ALF.mk_rightAL.Lessl1mf1inletlit2=ALF.mk_rightAL.Lessl2mf2in_do_factoring~substlit1lit2ij);(* see whether we have a.x + mf1 < r1 and a.x + mf2 < r2 *)MF.unify_mm(l1,0)(l2,0)(fun(mf1,mf2,subst)->letlit1=ALF.mk_leftAL.Lessmf1r1inletlit2=ALF.mk_leftAL.Lessmf2r2in_do_factoring~substlit1lit2ij);()|_->assertfalse));Util.exit_profprof_rat_ineq_factoring;!acc(** One-shot literal/clause removal.
We use unit clauses to try to prove a literal absurd/tautological, possibly
using {b several} instances of unit clauses.
For instance, 0 ≤ f(x) makes 0 ≤ f(a) + f(b) redundant, but subsumption
is not able to detect it. *)(* allow traces of depth at most 3 *)letmax_ineq_trivial_steps=3(* rewrite a literal [l] into a smaller literal [l'], such that [l'] and
the current set of unit clauses imply [l]; then compute the
transitive closure of this relation. If we obtain a trivial
literal, then [l] is redundant (we keep a trace of literals used).
We use continuations to deal with the multiple choices. *)letrec_ineq_find_sufficient~ord~traceclitk=matchlitwith|_whenAL.is_triviallit->k(trace,lit)|_whenList.lengthtrace>=max_ineq_trivial_steps->()(* need another step, but it would exceed the limit *)|{AL.op=AL.Less;_}whenIter.existsT.is_var(AL.Seq.termslit)->()(* no way we rewrite this into a tautology *)|{AL.op=AL.Less;_}->Util.incr_statstat_rat_trivial_ineq;Util.debugf~section5"(@[try_ineq_find_sufficient@ :lit `%a`@ :trace (@[%a@])@])"(funk->kAL.pplit(Util.pp_listC.pp)trace);AL.fold_terms~vars:false~which:`Max~ord~subterms:falselit|>Iter.iter(fun(t,pos)->letplit=ALF.get_exnlitposinletis_left=matchposwith|Position.Left_->true|Position.Right_->false|_->assertfalsein(* try to eliminate [t] in passive lit [plit]*)PS.TermIndex.retrieve_generalizations(!_idx_unit_ineq,1)(t,0)|>Iter.iter(fun(_t',with_pos,subst)->letactive_clause=with_pos.C.WithPos.clauseinletactive_pos=with_pos.C.WithPos.posinmatchLits.View.get_rat(C.litsactive_clause)active_poswith|None->assertfalse|Some(ALF.Left(AL.Less,_,_)asalit')whenis_left->letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletmf1',m2'=matchLits.View.get_rat(C.litsactive_clause)active_poswith|Some(ALF.Left(_,mf1',m2'))->mf1',m2'|_->assertfalseinletmf1'=MF.apply_substSubst.Renaming.nonesubst(mf1',1)inletm2'=M.apply_substSubst.Renaming.nonesubst(m2',1)in(* from t+mf1 ≤ m2 and t+mf1' ≤ m2', we deduce
that if m2'-mf1' ≤ m2-mf1 then [lit] is redundant.
That is, the sufficient literal is
mf1 + m2' ≤ m2 + mf1' (we replace [t] with [m2'-mf1']) *)letnew_plit=ALF.replaceplit(M.differencem2'(MF.restmf1'))in(* transitive closure *)lettrace=active_clause::tracein_ineq_find_sufficient~ord~tracecnew_plitk)|Some(ALF.Right(AL.Less,_,_)asalit')whennotis_left->(* symmetric case *)letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletm1',mf2'=matchLits.View.get_rat(C.litsactive_clause)active_poswith|Some(ALF.Right(_,m1',mf2'))->m1',mf2'|_->assertfalseinletmf2'=MF.apply_substSubst.Renaming.nonesubst(mf2',1)inletm1'=M.apply_substSubst.Renaming.nonesubst(m1',1)inletnew_plit=ALF.replaceplit(M.differencem1'(MF.restmf2'))in(* transitive closure *)lettrace=active_clause::tracein_ineq_find_sufficient~ord~tracecnew_plitk)|Some_->()(* cannot make a sufficient literal *)))|_->()(* is a literal redundant w.r.t the current set of unit clauses *)let_ineq_is_redundant_by_unitclit=matchlitwith|_whenLit.is_triviallit||Lit.is_absurdlit->None(* something more efficient will take care of it *)|Lit.Rat({AL.op=AL.Less;_}asalit)->letord=Ctx.ord()inlettraces=_ineq_find_sufficient~ord~trace:[]calit|>Iter.head(* one is enough *)inbeginmatchtraceswith|Some(trace,_lit')->assert(AL.is_trivial_lit');lettrace=CCList.uniq~eq:C.equaltraceinSometrace|None->Noneend|_->Noneletis_redundant_by_ineqc=Util.enter_profprof_rat_trivial_ineq;letres=CCArray.exists(funlit->match_ineq_is_redundant_by_unitclitwith|None->false|Sometrace->Util.debugf~section3"@[<2>clause @[%a@]@ trivial by inequations @[%a@]@]"(funk->kC.ppc(CCFormat.listC.pp)trace);Util.incr_statstat_rat_trivial_ineq_steps;true)(C.litsc)inUtil.exit_profprof_rat_trivial_ineq;res(* allow traces of depth at most 3 *)letmax_ineq_demod_steps=3(* rewrite a literal [l] into a smaller literal [l'], such that [l] and
the current set of unit clauses imply [l']; then compute the
transitive closure of this relation. If we obtain an absurd
literal, then [l] is absurd (we keep a trace of literals used).
We use continuations to deal with the multiple choices.
Each step looks like: from [l == (t <= u) && l' == (l <= t)]
we deduce [l <= u]. If at some point we deduce [⊥], we win. *)letrecineq_find_necessary_~ord~traceclitk=matchlitwith|_whenAL.is_absurdlit->k(trace,lit)|_whenList.lengthtrace>=max_ineq_demod_steps->()(* need another step, but it would exceed the limit *)|{AL.op=AL.Less;_}whenIter.existsT.is_var(AL.Seq.termslit)->()(* too costly (will match too many things) *)|{AL.op=AL.Less;_}->Util.incr_statstat_rat_demod_ineq;Util.debugf~section5"(@[try_ineq_find_necessary@ :lit `%a`@ :trace (@[%a@])@])"(funk->kAL.pplit(Util.pp_listC.pp)trace);AL.fold_terms~vars:false~which:`Max~ord~subterms:falselit|>Iter.iter(fun(t,pos)->letplit=ALF.get_exnlitposinletis_left=matchposwith|Position.Left_->true|Position.Right_->false|_->assertfalsein(* try to eliminate [t] in passive lit [plit]*)PS.TermIndex.retrieve_generalizations(!_idx_unit_ineq,1)(t,0)|>Iter.iter(fun(_t',with_pos,subst)->letactive_clause=with_pos.C.WithPos.clauseinletactive_pos=with_pos.C.WithPos.posinmatchLits.View.get_rat(C.litsactive_clause)active_poswith|None->assertfalse|Some(ALF.Left(AL.Less,_,_)asalit')whennotis_left->letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletmf1',m2'=matchLits.View.get_rat(C.litsactive_clause)active_poswith|Some(ALF.Left(_,mf1',m2'))->mf1',m2'|_->assertfalseinletmf1'=MF.apply_substSubst.Renaming.nonesubst(mf1',1)inletm2'=M.apply_substSubst.Renaming.nonesubst(m2',1)in(* from m1 ≤ t+mf2 and t+mf1' ≤ m2', we deduce
m1 + mf1' ≤ mf2 + m2'. If this literal is absurd
then so is [m1 ≤ t+mf2].
We replace [t] with [m2'-mf1'] *)letnew_plit=ALF.replaceplit(M.differencem2'(MF.restmf1'))in(* transitive closure *)lettrace=active_clause::traceinineq_find_necessary_~ord~tracecnew_plitk)|Some(ALF.Right(AL.Less,_,_)asalit')whenis_left->(* symmetric case *)letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletm1',mf2'=matchLits.View.get_rat(C.litsactive_clause)active_poswith|Some(ALF.Right(_,m1',mf2'))->m1',mf2'|_->assertfalseinletmf2'=MF.apply_substSubst.Renaming.nonesubst(mf2',1)inletm1'=M.apply_substSubst.Renaming.nonesubst(m1',1)inletnew_plit=ALF.replaceplit(M.differencem1'(MF.restmf2'))in(* transitive closure *)lettrace=active_clause::traceinineq_find_necessary_~ord~tracecnew_plitk)|Some_->()(* cannot make a sufficient literal *)))|_->()(* is a literal absurd w.r.t the current set of unit clauses *)let_ineq_is_absurd_by_unitclit=matchlitwith|_whenLit.is_triviallit||Lit.is_absurdlit->None(* something more efficient will take care of it *)|Lit.Rat({AL.op=AL.Less;_}asalit)->letord=Ctx.ord()inlettraces=ineq_find_necessary_~ord~trace:[]calit|>Iter.head(* one is enough *)inbeginmatchtraceswith|Some(trace,_lit')->assert(AL.is_absurd_lit');lettrace=CCList.uniq~eq:C.equaltraceinSometrace|None->Noneend|_->None(* demodulate using inequalities *)letdemod_ineqc:C.tSimplM.t=Util.enter_profprof_rat_demod_ineq;letres=CCArray.findi(funilit->match_ineq_is_absurd_by_unitclitwith|None->None|Sometrace->Util.debugf~section3"@[<2>clause @[%a@]@ rewritten by inequations @[%a@]@]"(funk->kC.ppc(CCFormat.listC.pp)trace);Util.incr_statstat_rat_demod_ineq_steps;Some(i,trace))(C.litsc)inletres=matchreswith|None->SimplM.return_samec|Some(i,cs)->letlits=CCArray.except_idx(C.litsc)iinletproof=Proof.Step.simp~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mk"rat.demod_ineq")(C.proof_parentc::List.mapC.proof_parentcs)inletc'=C.createlitsproof~penalty:(C.penaltyc)~trail:(C.trailc)inSimplM.return_newc'inUtil.exit_profprof_rat_demod_ineq;res(** {3 Others} *)let_has_ratc=CCArray.existsLit.is_rat(C.litsc)moduleSimp=Simplex.MakeHelp(T)(* tautology check: take the linear system that is the negation
of all a≠b and a≤b, and check its satisfiability *)let_is_tautologyc=Util.enter_profprof_rat_semantic_tautology;(* convert a monome into a rational monome + Q constant *)letconvm=M.coeffsm,M.constmin(* create a list of constraints for some arith lits *)letconstraints=Lits.fold_rat~eligible:C.Eligible.arith(C.litsc)|>Iter.fold(funacc(lit,_)->(* negate the literal and make a constraint out of it *)matchlitwith|{AL.op=AL.Less;left=m1;right=m2}->(* m1 < m2 ----> m1-m2 > 0 ---> m1-m2 ≥ 0 by approx *)letm,c=conv(M.differencem1m2)in(Simp.GreaterEq,m,Q.negc)::acc|_->acc)[]inletsimplex=Simp.add_constraintsSimp.emptyconstraintsinUtil.exit_profprof_rat_semantic_tautology;matchSimp.ksolvesimplexwith|Simp.Unsatisfiable_->true(* negation unsatisfiable *)|Simp.Solution_->false(* cache the result because it's a bit expensive *)letis_tautologyc=ifC.get_flagflag_computed_tautocthenC.get_flagflag_tautocelse((* compute whether [c] is an arith tautology *)letres=_has_ratc&&_is_tautologycinC.set_flagflag_tautocres;C.set_flagflag_computed_tautoctrue;ifresthenUtil.debugf~section4"@[<2>clause@ @[%a@]@ is an arith tautology@]"(funk->kC.ppc);Util.incr_statstat_rat_semantic_tautology;res)(* look for negated literals *)letconvert_litc:C.tSimplM.t=lettype_okt=Type.equalType.rat(T.tyt)inletopenCCOpt.Infixinletconv_litilit=matchlitwith|Lit.Equation(l,r,false)whentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->i,[Lit.mk_rat_lessm1m2;Lit.mk_rat_lessm2m1]|Lit.Prop(f,sign)->beginmatchT.viewf,signwith|T.AppBuiltin(Builtin.Less,[_;l;r]),falsewhentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->(* ¬(l<r) --> l=r ∨ r<l *)i,[Lit.mk_rat_eqm1m2;Lit.mk_rat_lessm2m1]|T.AppBuiltin(Builtin.Less,[_;l;r]),truewhentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->i,[Lit.mk_rat_lessm1m2]|T.AppBuiltin(Builtin.Lesseq,[_;l;r]),truewhentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->(* l≤r --> l=r ∨ l<r *)i,[Lit.mk_rat_eqm1m2;Lit.mk_rat_lessm1m2]|T.AppBuiltin(Builtin.Lesseq,[_;l;r]),falsewhentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->(* ¬(l≤r) --> r<l *)i,[Lit.mk_rat_lessm2m1]|T.AppBuiltin(Builtin.Eq,[_;l;r]),truewhentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->i,[Lit.mk_rat_eqm1m2]|T.AppBuiltin(Builtin.Eq,[_;l;r]),falsewhentype_okl->Monome.Rat.of_terml>>=funm1->Monome.Rat.of_termr>|=funm2->(* ¬(l=r) --> l<r ∨ r<l *)i,[Lit.mk_rat_lessm1m2;Lit.mk_rat_lessm2m1]|_->Noneend|_->NoneinbeginmatchCCArray.findiconv_lit(C.litsc)with|None->SimplM.return_samec|Some(i,new_lits)->letlits=new_lits@CCArray.except_idx(C.litsc)iandproof=Proof.Step.simp~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mk"convert_lit")[C.proof_parentc]inletc'=C.create~trail:(C.trailc)~penalty:(C.penaltyc)litsproofinUtil.debugf~section5"(@[convert@ :from %a@ :to %a@])"(funk->kC.ppcC.ppc');SimplM.return_newc'end(** {6 Variable Elimination Procedure} *)letunshielded_varslits=Literals.unshielded_varslits~filter:(funvar->Type.equal(HVar.tyvar)Type.rat)let_negate_lits=List.mapLit.negatemoduleElim_var=structtypet={var:T.var;lower:Q.tM.tlist;(* monomes smaller than x *)upper:Q.tM.tlist;(* monomes bigger than x *)eq:Q.tM.tlist;(* monomes equal to x *)side_lits:Literal.tlist;(* other literals *)initial_lits:Literals.t;}letppout(e:t)=letpp_m=CCFormat.within"`""`"M.ppinletpp_m_loutl=Format.fprintfout"(@[<hv>%a@])"(Util.pp_list~sep:" "pp_m)linFormat.fprintfout"(@[<v>:var %a@ :lower %a@ :upper %a@ :eq %a@ :side (@[%a@])@])"T.pp_vare.varpp_m_le.lowerpp_m_le.upperpp_m_le.eq(Util.pp_list~sep:" "Literal.pp)e.side_lits(* quick and dirty CNF *)moduleForm=structtypeform=|AtomofLiteral.t|Andofformlist|Orofformlist|True|Falseletatoml=Atomlletor_=function[]->False|[t]->t|l->Orlletand_=function[]->True|[t]->t|l->Andlletreccnf(f:form):Lit.tlistlist=matchfwith|Atomlit->[[lit]]|True->[[Lit.mk_tauto]]|False->[[Lit.mk_absurd]]|And[]|Or[]->assertfalse|And[f]|Or[f]->cnff|Andl->CCList.flat_mapcnfl|Orl->Util.map_product~f:cnflletrecppout(f:form):unit=matchfwith|True->CCFormat.stringout"⊤"|False->CCFormat.stringout"⊥"|Atomlit->Lit.ppoutlit|Orl->Format.fprintfout"(@[<hv>or@ %a@])"(Util.pp_list~sep:" "pp)l|Andl->Format.fprintfout"(@[<hv>and@ %a@])"(Util.pp_list~sep:" "pp)lend(* variable elimination. First, build formula; then perform CNF. *)letto_clauses(e:t):Literal.tlistlist=(* let i range over e.eq
j range over e.lower
k range over e.upper.
produce:
[∨_j ∨_k (m_j < m_k ∨ ∨_i (m_i = m_j ∧ m_i = m_k))]
*)letform_l=letopenCCList.Infixine.lower>>=funm_j->e.upper>>=funm_k->letf_diseq=Form.atom(Lit.mk_rat_lessm_jm_k)inletf_eq=e.eq>|=funm_i->Form.and_[Form.atom(Lit.mk_rat_eqm_im_j);Form.atom(Lit.mk_rat_eqm_im_k);]inCCList.return(Form.or_(f_diseq::f_eq))inletform=Form.or_form_linUtil.debugf~section5"(@[<2>elim_var_non_cnf :var %a@ :clause %a@ :state %a@ :form %a@])"(funk->kT.pp_vare.varLits.ppe.initial_litsppeForm.ppform);beginForm.cnfform|>List.rev_map(funlits->List.rev_appende.side_litslits)endexceptionMake_err(* builder *)letmake_exn(lits:Literals.t)(x:T.var):t=assert(not(Literals.is_shieldedxlits));(* gather literals *)letlower=ref[]inletupper=ref[]inleteq=ref[]inletside=ref[]inletpush_l=CCList.Ref.pushinlett_x=T.varxinArray.iter(funlit->matchlitwith|Lit.Rat{Rat_lit.op;left=m1;right=m2}->beginmatchM.findm1t_x,M.findm2t_xwith|None,None->push_lsidelit|Some_,Some_->raiseMake_err(* cancellations are possible *)|Somec1,None->letm=M.Rat.divide(M.differencem2(M.removem1t_x))c1inbeginmatchopwith|Rat_lit.Equal->push_leqm|Rat_lit.Less->push_luppermend|None,Somec2->letm=M.Rat.divide(M.differencem1(M.removem2t_x))c2inbeginmatchopwith|Rat_lit.Equal->push_leqm|Rat_lit.Less->push_llowermendend|Lit.Equation(t,u,true)whenT.equaltt_x->push_leq(M.Rat.singletonQ.oneu)|Lit.Equation(t,u,true)whenT.equalut_x->push_leq(M.Rat.singletonQ.onet)|Lit.Equation(t,_,false)whenT.equaltt_x->raiseMake_err|Lit.Equation(_,u,false)whenT.equalut_x->raiseMake_err|_->assert(not(Lit.var_occursxlit));(* shielding *)push_lsidelit)lits;{var=x;lower=!lower;upper=!upper;eq=!eq;side_lits=!side;initial_lits=lits;}letmakelitsx:toption=trySome(make_exnlitsx)withMake_err->Noneendleteliminate_unshielded(c:C.t):C.tlistoption=letmoduleE=Elim_varinletnvars=unshielded_vars(C.litsc)inbeginmatchnvarswith|[]->None|x::_->beginmatchE.make(C.litsc)xwith|None->None|Somee->letclauses=E.to_clauseseinletproof=Proof.Step.simp[C.proof_parentc]~tags:[Proof.Tag.T_lra]~rule:(Proof.Rule.mkf"elim_var(%a)"T.pp_varx)inletnew_c=List.map(funlits->C.createlitsproof~penalty:(C.penaltyc)~trail:(C.trailc))clausesinUtil.debugf~section4"(@[<2>elim_var :var %a@ :clause %a@ :yields (@[<hv>%a@])@]@)"(funk->kT.pp_varxC.ppc(Util.pp_listC.pp)new_c);Somenew_cendend(** {2 Setup} *)(* print index into file *)let_print_idxfileidx=CCIO.with_outfile(funoc->letpp_leaf__=()inletout=Format.formatter_of_out_channelocinFormat.fprintfout"@[<2>%a@]@."(PS.TermIndex.to_dotpp_leaf)idx;flushoc)letsetup_dot_printers()=CCOpt.iter(funf->Signal.onceSignals.on_dot_output(fun()->_print_idxf!_idx_unit_eq))!dot_unit_;()letregister()=Util.debug~section2"rat-arith: setup env";(* add inference rules *)Env.add_binary_inf"rat_sup_active"canc_sup_active;Env.add_binary_inf"rat_sup_passive"canc_sup_passive;Env.add_unary_inf"rat_cancellation"cancellation;Env.add_unary_inf"rat_eq_factoring"canc_equality_factoring;Env.add_binary_inf"rat_ineq_chaining"canc_ineq_chaining;Env.add_unary_inf"rat_ineq_factoring"canc_ineq_factoring;Env.add_multi_simpl_ruleeliminate_unshielded;Env.add_unary_simplifycanc_demodulation;Env.add_backward_simplifycanc_backward_demodulation;Env.add_is_trivialis_tautology;Env.add_unary_simplifyconvert_lit;if!enable_trivial_ineq_then(Env.add_redundantis_redundant_by_ineq;);if!enable_demod_ineq_then(Env.add_active_simplifydemod_ineq;);Env.add_multi_simpl_ruleeliminate_unshielded;(* completeness? I don't think so *)Ctx.lost_completeness();(* enable AC-property of sum *)(* FIXME: currently AC doesn't handle builtins
if !_enable_ac then begin
let sum = ID.Arith.sum in
let ty = Signature.find_exn Signature.TPTP.Arith.full sum in
let module A = Env.flex_get AC.key_ac in
A.add sum ty;
end;
*)setup_dot_printers();()endletk_should_register=Flex_state.create_key()letk_has_rat=Flex_state.create_key()letextension=letenv_actionenv=letmoduleE=(valenv:Env.S)inifE.flex_getk_should_registerthen(letmoduleI=Make(E)inI.register())elseifE.flex_getk_has_ratthen((* arith not enabled, so we cannot solve the problem, do not answer "sat" *)E.Ctx.lost_completeness();)andpost_typing_actionstmtsstate=letmodulePT=TypedSTerminlethas_rat=CCVector.to_seqstmts|>Iter.flat_mapStmt.Seq.to_seq|>Iter.flat_map(function|`ID_->Iter.empty|`Tyty->Iter.returnty|`Formt|`Termt->PT.Seq.subtermst|>Iter.filter_mapPT.ty)|>Iter.exists(PT.Ty.equalPT.Ty.rat)inletshould_reg=!enable_rat_&&has_ratinUtil.debugf~section2"decision to register rat-arith: %B"(funk->kshould_reg);state|>Flex_state.addk_should_registershould_reg|>Flex_state.addk_has_rathas_ratin{Extensions.defaultwithExtensions.name="arith_rat";post_typing_actions=[post_typing_action];env_actions=[env_action];}let()=Params.add_opts["--rat-no-semantic-tauto",Arg.Clearenable_semantic_tauto_," disable rational arithmetic semantic tautology check";"--rat-arith",Arg.Setenable_rat_," enable axiomatic rational arithmetic";"--no-arith",Arg.Clearenable_rat_," disable axiomatic rational arithmetic";"--rat-ac",Arg.Setenable_ac_," enable AC axioms for rational arithmetic (sum)";"--dot-rat-unit",Arg.String(funs->dot_unit_:=Somes)," print arith-rat-unit index into file"];()