123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383(* This file is free software, part of Logtk. See file "license" for more details. *)(** {1 Trace of a TSTP prover} *)openLogtktypeid=Ast_tptp.namemoduleT=STermmoduleA=Ast_tptpmoduleErr=CCResulttypeterm=STerm.ttypeform=STerm.ttypeclause=termSLiteral.tlisttypet=|Axiomofstring*string(* filename, axiom name *)|Theoryofstring(* a theory used to do an inference *)|InferFormofform*steplazy_t|InferClauseofclause*steplazy_tandstep={id:id;rule:string;parents:tarray;esa:bool;(** Equisatisfiable step? *)}typeproof=tletequalp1p2=matchp1,p2with|Axiom(f1,n1),Axiom(f2,n2)->f1=f2&&n1=n2|Theorys1,Theorys2->s1=s2|InferForm(f1,lazystep1),InferForm(f2,lazystep2)->step1.id=step2.id&&T.equalf1f2|InferClause(c1,lazystep1),InferClause(c2,lazystep2)->begintrystep1.id=step2.id&&List.for_all2(SLiteral.equalT.equal)c1c2withInvalid_argument_->falseend|_->falselethashp=matchpwith|Axiom_|Theory_->Hashtbl.hashp|InferForm(_,lazys)|InferClause(_,lazys)->Hashtbl.hashs.idletcomparep1p2=Pervasives.comparep1p2(* FIXME *)(** {2 Constructors and utils} *)letmk_f_axiom~idf~file~name=letstep={id;rule="axiom";parents=[|Axiom(file,name)|];esa=false;}inInferForm(f,Lazy.from_valstep)letmk_c_axiom~idc~file~name=letstep={id;rule="axiom";parents=[|Axiom(file,name)|];esa=false;}inInferClause(c,Lazy.from_valstep)letmk_f_step?(esa=false)~idf~ruleparents=assert(rule<>"axiom");letstep={id;rule;parents=Array.of_listparents;esa;}inInferForm(f,Lazy.from_valstep)letmk_c_step?(esa=false)~idc~ruleparents=assert(rule<>"axiom");letstep={id;rule;parents=Array.of_listparents;esa;}inInferClause(c,Lazy.from_valstep)letis_axiom=function|Axiom_->true|_->falseletis_theory=function|Theory_->true|_->falseletis_step=function|InferClause_|InferForm_->true|Axiom_|Theory_->falseletis_proof_of_false=function|InferForm(form,_)whenT.equalformT.false_->true|InferClause([],_)->true|InferClause(l,_)->List.for_allSLiteral.is_falsel|_->falseletget_id=function|InferClause(_,lazystep)|InferForm(_,lazystep)->step.id|Axiom(_,name)->A.NameStringname|Theory_->invalid_arg"Trace_tstp: Theory does not have an ID"letforce=function|InferForm(_,step)|InferClause(_,step)->ignore(Lazy.forcestep)|Axiom_|Theory_->()(** {2 Proof traversal} *)moduleStepTbl=Hashtbl.Make(structtypet=proofletequal=equallethash=hashend)typeproof_set=unitStepTbl.tletis_dagproof=(* steps currently being explored *)letcurrent=StepTbl.create10in(* steps totally explored *)letclosed=StepTbl.create10in(* recursive DFS traversal *)letreccheck_proofproof=ifStepTbl.memclosedproofthen()(* ok *)elseifStepTbl.memcurrentproofthenraiseExit(* we followed a back link! *)elsebeginStepTbl.addcurrentproof();beginmatchproofwith|InferClause(_,lazystep)|InferForm(_,lazystep)->Array.itercheck_proofstep.parents|Axiom_|Theory_->()end;(* proof is now totally explored *)StepTbl.removecurrentproof;StepTbl.addclosedproof();endintrycheck_proofproof;(* check from root *)truewithExit->false(* loop detected *)(** Traverse the proof. Each proof node is traversed only once. *)lettraverse?(traversed=StepTbl.create11)proofk=(* set of already traversed proof nodes; queue of proof nodes
yet to traverse *)letqueue=Queue.create()inQueue.pushproofqueue;whilenot(Queue.is_emptyqueue)doletproof=Queue.takequeueinifStepTbl.memtraversedproofthen()elsebeginStepTbl.addtraversedproof();(* traverse premises first *)beginmatchproofwith|Axiom_|Theory_->()|InferForm(_,lazystep)|InferClause(_,lazystep)->Array.iter(funproof'->Queue.pushproof'queue)step.parentsend;(* call [k] on the proof *)kproof;enddoneletto_iterproof=Iter.from_iter(funk->traverseproofk)(** Depth of a proof, ie max distance between the root and any axiom *)letdepthproof=letexplored=StepTbl.create11inletdepth=ref0inletq=Queue.create()inQueue.push(proof,0)q;whilenot(Queue.is_emptyq)dolet(p,d)=Queue.popqinifStepTbl.memexploredproofthen()elsebeginStepTbl.addexploredproof();matchpwith|Axiom_->depth:=maxd!depth|Theory_->()|InferForm(_,lazystep)|InferClause(_,lazystep)->(* explore parents *)Array.iter(funp->Queue.push(p,d+1)q)step.parentsenddone;!depthletsizeproof=Iter.length(to_iterproof)(** {2 IO} *)type'aor_error=('a,string)CCResult.tletof_declsdecls=letsteps=Hashtbl.create16in(* maps names to steps *)letroot=refNonein(* (one) root of proof *)(* find a proof name *)letfind_stepname=tryHashtbl.findstepsnamewithNot_found->failwith(CCFormat.sprintf"proof step %a not found in derivation"A.pp_namename)in(* read information about the source of the clause/formula *)letread_infoinfo=matchinfowith|A.GList[A.GString("'proof'"|"proof")]->`Proof|A.GNode("inference",[A.GStringrule;A.GList[A.GNode("status",[A.GStringstatus])];A.GListparents])->(* lazily lookup parent steps by their name in the derivation *)letparents=lazy(Array.map(fundata->matchdatawith|A.GInti->find_step(A.NameInti)|A.GStrings->find_step(A.NameStrings)|A.GNode("theory",[A.GStringth])->Theoryth|_->failwith(CCFormat.sprintf"not a valid parent: %a"A.pp_generaldata))(Array.of_listparents))inletesa=status<>"thm"in`Parents(rule,esa,parents)|A.GNode("file",(A.GStringfile::A.GStringname::_))->letparents=Lazy.from_val[|(Axiom(file,name))|]in`Parents("axiom",false,parents)|A.GNode("trivial",_)->`Parents("trivial",false,Lazy.from_val[||])|A.GInti->letparent=find_step(A.NameInti)in`Parents("trivial",false,Lazy.from_val[|parent|])|A.GStrings->letparent=find_step(A.NameStrings)in`Parents("trivial",false,Lazy.from_val[|parent|])|_->Util.debugf1"not a valid proof step: %a"(funk->kA.pp_general_debugfinfo);`NoIdeain(* what to do if a step is read *)letadd_stepidstep=matchstepwith|InferForm_|InferClause_->ifis_proof_of_falsestep&&!root=Nonethenroot:=Somestep;Util.debugf3"add step %a (root? %B)"(funk->kA.pp_nameid(is_proof_of_falsestep));Hashtbl.replacestepsidstep;|Axiom_|Theory_->()in(* traverse declarations *)Iter.iterbeginfundecl->matchdeclwith|A.CNF(_name,_role,_c,info::_)->Util.debugf3"@[<2>convert step@ @[%a@]@]"(funk->k(A.ppT.pp)decl);beginmatchread_infoinfowith|`Proof|`NoIdea->()|`Parents(_rule,_esa,_parents)->assertfalse(* FIXME
let step = lazy {id=name; esa; rule; parents=Lazy.force parents} in
let c = List.map SLiteral.of_form c in
let p = InferClause (c, step) in
add_step name p
*)end|A.FOF(name,_role,f,info::_)|A.TFF(name,_role,f,info::_)->Util.debugf3"convert step %a"(funk->k(A.ppT.pp)decl);beginmatchread_infoinfowith|`Proof|`NoIdea->()|`Parents(rule,esa,parents)->letstep=lazy{id=name;esa;rule;parents=Lazy.forceparents}inletp=InferForm(f,step)inadd_stepnamepend|A.TypeDecl_|A.FOF_|A.CNF_|A.TFF_|A.THF_|A.Include_|A.IncludeOnly_|A.NewType_->()enddecls;match!rootwith|None->Err.fail"could not find the root of the TSTP proof"|Somep->try(* force proofs to trigger errors here *)traversepforce;Err.returnpwithFailuremsg->Err.failmsgletparse?(recursive=true)filename=Err.(Util_tptp.parse_file~recursivefilename>>=fundecls->Util.debugf1"@[<2>decls:@ @[<hv>%a@]@]"(funk->k(Util.pp_iter~sep:""(A.ppT.pp))decls);of_declsdecls)let_extract_axiomproof=matchproofwith|Axiom(f,n)->f,n|_->assertfalselet_pp_clauseoutc=matchcwith|[]->T.ppoutT.false_|_->Format.fprintfout"@[%a@]"(Util.pp_list~sep:" | "(SLiteral.ppT.pp))clet_print_parentp=matchpwith|InferForm_|InferClause_->CCFormat.to_stringA.pp_name(get_idp)|Theorys->CCFormat.sprintf"theory(%s)"s|Axiom(f,n)->CCFormat.sprintf"file('%s', %s)"fn(* pure fof? (no types but $i/$o) *)let_form_is_fof_f=false(* FIXME *)letpp_tstpoutproof=traverseproof(funp->matchpwith|Axiom_->()|Theorys->CCFormat.fprintfout"theory(%s)"s|InferClause(c,lazy({rule="axiom";_}asstep))whenis_axiomstep.parents.(0)->letid=get_idpinletf,n=_extract_axiomstep.parents.(0)inFormat.fprintfout"cnf(%a, axiom, (%a), file('%s', %s)).\n"A.pp_nameid_pp_clausecfn|InferForm(f,lazy({rule="axiom";_}asstep))whenis_axiomstep.parents.(0)->letid=get_idpinletfile,n=_extract_axiomstep.parents.(0)inFormat.fprintfout"tff(%a, axiom, %a, file('%s', %s)).\n"A.pp_nameidT.ppffilen|InferForm(f,lazystep)->letid=get_idpinletids=Array.map_print_parentstep.parentsinletstatus=ifstep.esathen"esa"else"thm"inletkind=if_form_is_foffthen"fof"else"tff"inFormat.fprintfout"%s(%a, plain, %a, inference('%s', [status(%s)], [%a])).\n"kindA.pp_nameidT.pp(T.close_allBinder.Forallf)step.rulestatus(CCFormat.arrayCCFormat.string)ids|InferClause(c,lazystep)->letid=get_idpinletids=Array.map_print_parentstep.parentsinletstatus=ifstep.esathen"esa"else"thm"inFormat.fprintfout"cnf(%a, plain, %a, inference('%s', [status(%s)], [%a])).\n"A.pp_nameid_pp_clausecstep.rulestatus(CCFormat.arrayCCFormat.string)ids)letpp0outproof=matchproofwith|Axiom(f,n)->Format.fprintfout"axiom(%s, %s)"fn|Theorys->Format.fprintfout"theory(%s)"s|InferClause(c,_)->Format.fprintfout"proof for %a (id %a)"_pp_clausecA.pp_name(get_idproof)|InferForm(f,_)->Format.fprintfout"proof for %a (id %a)"T.ppfA.pp_name(get_idproof)letpp1outproof=matchproofwith|Axiom(f,n)->Format.fprintfout"axiom(%s, %s)"fn|Theorys->Format.fprintfout"theory(%s)"s|InferClause(c,lazystep)->Format.fprintfout"proof for %a (id %a) from\n %a"_pp_clausecA.pp_name(get_idproof)CCFormat.(array~sep:(return"@. ")pp0)step.parents|InferForm(f,lazystep)->Format.fprintfout"proof for %a (id %a) from\n %a"T.ppfA.pp_name(get_idproof)CCFormat.(array~sep:(return"@. ")pp0)step.parentsletpp=pp0letto_string=CCFormat.to_stringpp