1 /*-----------------------------------------------------------------------
2 
3 File  : pcl_proofcheck.c
4 
5 Author: Stephan Schulz
6 
7 Contents
8 
9   Data types and algorithms to realize proof checking for PCL2
10   protocols.
11 
12   Copyright 1998, 1999 by the author.
13   This code is released under the GNU General Public Licence and
14   the GNU Lesser General Public License.
15   See the file COPYING in the main E directory for details..
16   Run "eprover -h" for contact information.
17 
18 Changes
19 
20 <1> Mon Apr  3 23:02:28 GMT 2000
21     New
22 
23 -----------------------------------------------------------------------*/
24 
25 #include "pcl_proofcheck.h"
26 
27 
28 
29 /*---------------------------------------------------------------------*/
30 /*                        Global Variables                             */
31 /*---------------------------------------------------------------------*/
32 
33 
34 /*---------------------------------------------------------------------*/
35 /*                      Forward Declarations                           */
36 /*---------------------------------------------------------------------*/
37 
38 
39 /*---------------------------------------------------------------------*/
40 /*                         Internal Functions                          */
41 /*---------------------------------------------------------------------*/
42 
43 /*-----------------------------------------------------------------------
44 //
45 // Function: pcl_run_prover()
46 //
47 //   Execute command and scan the output for success. If found, return
48 //   true, else return false;
49 //
50 // Global Variables:
51 //
52 // Side Effects    :
53 //
54 /----------------------------------------------------------------------*/
55 
pcl_run_prover(char * command,char * success)56 static bool pcl_run_prover(char* command, char*success)
57 {
58    bool   res=false;
59    char   line[180],*l;
60    FILE*  ppipe;
61 
62    if(OutputLevel>1)
63    {
64       fprintf(GlobalOut, "# Running %s\n", command);
65    }
66    ppipe=popen(command, "r");
67    if(!ppipe)
68    {
69       TmpErrno = errno;
70       SysError("Cannot open pipe", SYS_ERROR);
71    }
72    while((l=fgets(line, 180, ppipe)))
73    {
74       if(strstr(line,success))
75       {
76     res = true;
77       }
78       if(OutputLevel >= 3)
79       {
80     fprintf(GlobalOut, "#> %s", line);
81       }
82    }
83    pclose(ppipe);
84 
85    return res;
86 }
87 
88 /*-----------------------------------------------------------------------
89 //
90 // Function: pcl_verify_eprover()
91 //
92 //   Run E on the problem, return true if a proof is found.
93 //
94 // Global Variables: -
95 //
96 // Side Effects    : Memory operations, by subroutines.
97 //
98 /----------------------------------------------------------------------*/
99 
pcl_verify_eprover(ClauseSet_p problem,char * executable,long time_limit)100 static bool pcl_verify_eprover(ClauseSet_p problem,char *executable,
101                 long time_limit)
102 {
103    bool   res;
104    DStr_p command = DStrAlloc();
105    char*  name=TempFileName();
106    FILE*  problemfile;
107 
108    assert(OutputFormat == TPTPFormat);
109 
110    if(!executable)
111    {
112       executable=E_EXEC_DEFAULT;
113    }
114    problemfile = OutOpen(name);
115    ClauseSetPrint(problemfile, problem, true);
116    OutClose(problemfile);
117 
118    DStrAppendStr(command, executable);
119    DStrAppendStr(command, " --tptp-in --prefer-initial-clauses --ac-handling=None"
120        " --cpu-limit=");
121    DStrAppendInt(command, time_limit);
122    DStrAppendChar(command, ' ');
123    DStrAppendStr(command, name);
124 
125    res = pcl_run_prover(DStrView(command),"# Proof found!");
126 
127    if(!res)
128    {
129       fprintf(GlobalOut, "# ------------Problem begin--------------\n");
130       FilePrint(GlobalOut, name);
131       fprintf(GlobalOut, "# ------------Problem end----------------\n");
132    }
133 
134    TempFileRemove(name);
135    FREE(name);
136    DStrFree(command);
137    return res;
138 }
139 
140 
141 /*-----------------------------------------------------------------------
142 //
143 // Function: eqn_print_otter()
144 //
145 //   Print a literal in Otter format.
146 //
147 // Global Variables: -
148 //
149 // Side Effects    : Output
150 //
151 /----------------------------------------------------------------------*/
152 
eqn_print_otter(FILE * out,Eqn_p eqn)153 static void eqn_print_otter(FILE* out, Eqn_p eqn)
154 {
155    assert(OutputFormat == LOPFormat);
156 
157    if(EqnIsEquLit(eqn))
158    {
159       if(EqnIsPositive(eqn))
160       {
161     EqnPrint(out, eqn, false, true);
162       }
163       else
164       {
165     fputc('-', out);
166     EqnPrint(out, eqn, true, true);
167       }
168    }
169    else
170    {
171       if(eqn->lterm==eqn->bank->true_term)
172       {
173     assert(eqn->rterm==eqn->bank->true_term);
174     /* Special case, one more hack */;
175     if(EqnIsPositive(eqn))
176     {
177        fputs("$T", out);
178     }
179     else
180     {
181        fputs("$F", out);
182     }
183       }
184       else
185       {
186     /* Nonequational literal */
187     if(EqnIsNegative(eqn))
188     {
189        fputc('-', out);
190     }
191     else
192     {
193        fputc(' ', out);
194     }
195     TBPrintTerm(out, eqn->bank, eqn->lterm, true);
196       }
197    }
198 }
199 
200 /*-----------------------------------------------------------------------
201 //
202 // Function: clause_print_otter()
203 //
204 //   Print a clause in Otter format.
205 //
206 // Global Variables: -
207 //
208 // Side Effects    : Output
209 //
210 /----------------------------------------------------------------------*/
211 
clause_print_otter(FILE * out,Clause_p clause)212 static void clause_print_otter(FILE* out, Clause_p clause)
213 {
214    Eqn_p handle;
215 
216    if(ClauseIsEmpty(clause))
217    {
218       /* For otter, the empty clause is propositional false: */
219 
220       fputs("$F.", out);
221    }
222    else
223    {
224       handle=clause->literals;
225       eqn_print_otter(out, handle);
226       handle=handle->next;
227       while(handle)
228       {
229     fputs("|\n", out);
230     eqn_print_otter(out, handle);
231     handle=handle->next;
232       }
233       fputs(".\n", out);
234    }
235 }
236 
237 /*-----------------------------------------------------------------------
238 //
239 // Function: clause_set_print_otter()
240 //
241 //   Print a set of clauses in Otter format (with prolog-variables).
242 //
243 // Global Variables: -
244 //
245 // Side Effects    : Output
246 //
247 /----------------------------------------------------------------------*/
248 
clause_set_print_otter(FILE * out,ClauseSet_p set)249 static void clause_set_print_otter(FILE* out, ClauseSet_p set)
250 {
251    Clause_p handle;
252 
253    for(handle = set->anchor->succ; handle!=set->anchor; handle =
254      handle->succ)
255    {
256       clause_print_otter(out, handle);
257       fputc('\n', out);
258    }
259 }
260 
261 
262 /*-----------------------------------------------------------------------
263 //
264 // Function: pcl_verify_otter()
265 //
266 //   Run Otter on the problem, return true if a proof is found.
267 //
268 // Global Variables: -
269 //
270 // Side Effects    : Memory operations, by subroutines.
271 //
272 /----------------------------------------------------------------------*/
273 
pcl_verify_otter(ClauseSet_p problem,char * executable,long time_limit)274 static bool pcl_verify_otter(ClauseSet_p problem,char *executable,
275               long time_limit)
276 {
277    bool   res;
278    DStr_p command = DStrAlloc();
279    char*  name=TempFileName();
280    FILE*  problemfile;
281 
282    if(!executable)
283    {
284       executable=OTTER_EXEC_DEFAULT;
285    }
286    problemfile = OutOpen(name);
287    fprintf(problemfile,
288       "set(prolog_style_variables).\n"
289       "clear(print_kept).\n"
290       "clear(print_new_demod).\n"
291       "clear(print_back_demod).\n"
292       "clear(print_back_sub).\n"
293       "set(auto).\n"
294       "set(input_sos_first).\n"
295       /* "set(para_from_vars).\n" */
296       "assign(max_seconds, %ld).\n\n"
297       "assign(max_mem, 100000).\n\n"
298       "list(usable).\n\n"
299       "equal(X,X).\n",time_limit);
300    clause_set_print_otter(problemfile, problem);
301    fprintf(problemfile,
302       "end_of_list.\n");
303    OutClose(problemfile);
304 
305    DStrAppendStr(command, executable);
306    DStrAppendStr(command, " < ");
307    DStrAppendStr(command, name);
308    DStrAppendStr(command, " 2> /dev/null");
309 
310    res = pcl_run_prover(DStrView(command), "-------- PROOF --------");
311 
312    if(!res)
313    {
314       fprintf(GlobalOut, "# ------------Problem begin--------------\n");
315       FilePrint(GlobalOut, name);
316       fprintf(GlobalOut, "# ------------Problem end----------------\n");
317    }
318 
319    TempFileRemove(name);
320    FREE(name);
321    DStrFree(command);
322    return res;
323 }
324 
325 
326 /*-----------------------------------------------------------------------
327 //
328 // Function: sig_print_dfg()
329 //
330 //   Collect function symbols from set and print them in DFG syntax.
331 //
332 // Global Variables: -
333 //
334 // Side Effects    : Output
335 //
336 /----------------------------------------------------------------------*/
337 
sig_print_dfg(FILE * out,ClauseSet_p set,Sig_p sig)338 static void sig_print_dfg(FILE* out, ClauseSet_p set, Sig_p sig)
339 {
340    long     *symbol_distrib;
341    FunCode  i;
342 
343    symbol_distrib = SizeMalloc((sig->size)*sizeof(long));
344    for(i=0; i< sig->size; i++)
345    {
346       symbol_distrib[i] = 0;
347    }
348    ClauseSetAddSymbolDistribution(set, symbol_distrib);
349 
350    fprintf(out,"list_of_symbols.\nfunctions[(spass_hack,0)");
351    for(i=sig->internal_symbols+1; i<sig->size; i++)
352    {
353       if(symbol_distrib[i]&&!SigIsPredicate(sig,i))
354       {
355     fprintf(out, ",(%s,%d)",
356        SigFindName(sig, i),
357        SigFindArity(sig, i));
358       }
359    }
360    fprintf(out,"].\npredicates[(spass_pred_dummy,0)");
361    for(i=sig->internal_symbols+1; i<sig->size; i++)
362    {
363       if(symbol_distrib[i]&&SigIsPredicate(sig,i))
364       {
365     fprintf(out, ",(%s,%d)",
366        SigFindName(sig, i),
367        SigFindArity(sig, i));
368       }
369    }
370    fprintf(out,"].\nend_of_list.\n");
371 }
372 
373 
374 /*-----------------------------------------------------------------------
375 //
376 // Function: eqn_print_dfg()
377 //
378 //   Print an equation in DFG syntax.
379 //
380 // Global Variables: -
381 //
382 // Side Effects    : Output
383 //
384 /----------------------------------------------------------------------*/
385 
eqn_print_dfg(FILE * out,Eqn_p eqn)386 static void eqn_print_dfg(FILE* out, Eqn_p eqn)
387 {
388    assert(OutputFormat == LOPFormat);
389    assert(!EqnUseInfix);
390 
391    if(EqnIsNegative(eqn))
392    {
393       fputs("not(", out);
394    }
395    if(eqn->lterm==eqn->bank->true_term)
396    {
397       assert(eqn->rterm==eqn->bank->true_term);
398       /* Special case, one more hack */;
399       fputs("equal(spass_hack,spass_hack)", out);
400    }
401    else
402    {
403       EqnPrint(out,eqn,EqnIsNegative(eqn),true);
404    }
405    if(EqnIsNegative(eqn))
406    {
407       fputc(')', out);
408    }
409 }
410 
411 
412 /*-----------------------------------------------------------------------
413 //
414 // Function: clause_print_dfg()
415 //
416 //   Print a clause in dfg format.
417 //
418 // Global Variables: -
419 //
420 // Side Effects    : Output
421 //
422 /----------------------------------------------------------------------*/
423 
clause_print_dfg(FILE * out,Clause_p clause)424 static void clause_print_dfg(FILE* out, Clause_p clause)
425 {
426    Eqn_p    handle;
427    PTree_p  variables = NULL, cell;
428    PStack_p stack;
429    Term_p   var;
430    long     var_no;
431 
432    fprintf(out, "clause(");
433    var_no = ClauseCollectVariables(clause, &variables);
434    if(var_no)
435    {
436       fprintf(out, "forall([");
437       stack=PTreeTraverseInit(variables);
438       cell = PTreeTraverseNext(stack);
439       if(cell)
440       {
441     assert(clause->literals);
442     var = cell->key;
443     TBPrintTerm(out, clause->literals->bank, var, true);
444     while((cell=PTreeTraverseNext(stack)))
445     {
446        fputc(',', out);
447        var = cell->key;
448        TBPrintTerm(out, clause->literals->bank, var, true);
449     }
450       }
451       PTreeTraverseExit(stack);
452       PTreeFree(variables);
453       fprintf(out, "],");
454    }
455    fprintf(out, "or(");
456 
457    handle=clause->literals;
458    if(handle)
459    {
460       eqn_print_dfg(out, handle);
461       handle=handle->next;
462       while(handle)
463       {
464     fputs(",", out);
465     eqn_print_dfg(out, handle);
466     handle=handle->next;
467       }
468    }
469    else
470    {
471       fputs("not(equal(spass_hack,spass_hack))",out);
472    }
473    fprintf(out, ")%c, c%ld ).", (var_no?')':' '), clause->ident);
474 }
475 
476 
477 /*-----------------------------------------------------------------------
478 //
479 // Function: clause_set_print_dfg()
480 //
481 //   Print a set of clauses in DFG format.
482 //
483 // Global Variables: -
484 //
485 // Side Effects    : Output
486 //
487 /----------------------------------------------------------------------*/
488 
clause_set_print_dfg(FILE * out,ClauseSet_p set)489 static void clause_set_print_dfg(FILE* out, ClauseSet_p set)
490 {
491    Clause_p handle;
492 
493    for(handle = set->anchor->succ; handle!=set->anchor; handle =
494      handle->succ)
495    {
496       clause_print_dfg(out, handle);
497       fputc('\n', out);
498    }
499 }
500 
501 
502 /*-----------------------------------------------------------------------
503 //
504 // Function: pcl_verify_spass()
505 //
506 //   Run SPASS on the problem, return true if a proof is found.
507 //
508 // Global Variables: -
509 //
510 // Side Effects    : Memory operations, by subroutines.
511 //
512 /----------------------------------------------------------------------*/
513 
pcl_verify_spass(ClauseSet_p problem,char * executable,long time_limit,Sig_p sig)514 static bool pcl_verify_spass(ClauseSet_p problem,char *executable,
515               long time_limit, Sig_p sig)
516 {
517    bool   res;
518    DStr_p command = DStrAlloc();
519    char*  name=TempFileName();
520    FILE*  problemfile;
521 
522    if(!executable)
523    {
524       executable=SPASS_EXEC_DEFAULT;
525    }
526    problemfile = OutOpen(name);
527    fprintf(problemfile,
528       "begin_problem(Unknown).\n");
529 
530    sig_print_dfg(problemfile, problem, sig);
531 
532    fprintf(problemfile, "list_of_clauses(axioms,cnf).\n");
533    clause_set_print_dfg(problemfile, problem);
534    fprintf(problemfile, "end_of_list.\n"
535       "list_of_settings(SPASS).\n"
536       "set_flag(TimeLimit, %ld).\n"
537       "end_of_list.\n"
538       "end_problem.\n", time_limit);
539    OutClose(problemfile);
540 
541    DStrAppendStr(command, executable);
542    DStrAppendStr(command, " ");
543    DStrAppendStr(command, name);
544 
545    res = pcl_run_prover(DStrView(command), "Proof found.");
546    if(!res)
547    {
548       fprintf(GlobalOut, "# ------------Problem begin--------------\n");
549       FilePrint(GlobalOut, name);
550       fprintf(GlobalOut, "# ------------Problem end----------------\n");
551    }
552 
553    TempFileRemove(name);
554    FREE(name);
555    DStrFree(command);
556    return res;
557 }
558 
559 
560 /*---------------------------------------------------------------------*/
561 /*                         Exported Functions                          */
562 /*---------------------------------------------------------------------*/
563 
564 /*-----------------------------------------------------------------------
565 //
566 // Function: PCLCollectPreconds()
567 //
568 //   Collect copies of all clauses quoted in the justification of step
569 //   in set. Return number of clauses.
570 //
571 // Global Variables: -
572 //
573 // Side Effects    : Memory operations, changes set.
574 //
575 /----------------------------------------------------------------------*/
576 
PCLCollectPreconds(PCLProt_p prot,PCLStep_p step,ClauseSet_p set)577 long PCLCollectPreconds(PCLProt_p prot, PCLStep_p step, ClauseSet_p
578          set)
579 {
580    PTree_p   tree = NULL;
581    PCLStep_p handle;
582    Clause_p  clause;
583    long      res = 0;
584 
585    assert(prot && step && set);
586 
587    PCLExprCollectPreconds(prot, step->just, &tree);
588    while(tree)
589    {
590       handle = PTreeExtractRootKey(&tree);
591       if(PCLStepIsClausal(handle))
592       {
593          clause = ClauseCopy(handle->logic.clause, prot->terms);
594          ClauseSetInsert(set, clause);
595          res++;
596       }
597       else
598       {
599          Warning("Cannot currently handle full first-order format!");
600       }
601    }
602    return res;
603 }
604 
605 
606 /*-----------------------------------------------------------------------
607 //
608 // Function: PCLNegSkolemizeClause()
609 //
610 //   Add the clauses resulting from negating and skolemizing
611 //   step->clause to set. The implementation is not very efficient,
612 //   but that should not matter for our application.
613 //
614 // Global Variables: -
615 //
616 // Side Effects    : Memory operations.
617 //
618 /----------------------------------------------------------------------*/
619 
PCLNegSkolemizeClause(PCLProt_p prot,PCLStep_p step,ClauseSet_p set)620 long PCLNegSkolemizeClause(PCLProt_p prot, PCLStep_p step, ClauseSet_p
621             set)
622 {
623    long     res = 0;
624    Clause_p clause, new_clause;
625    Eqn_p    handle, copy;
626 
627    if(PCLStepIsClausal(step))
628    {
629       clause = ClauseSkolemize(step->logic.clause, prot->terms);
630 
631       for(handle=clause->literals; handle; handle=handle->next)
632       {
633          copy = EqnCopy(handle,prot->terms);
634          EqnFlipProp(copy, EPIsPositive);
635          new_clause=ClauseAlloc(copy);
636          ClauseSetTPTPType(new_clause, CPTypeHypothesis);
637          ClauseSetInsert(set, new_clause);
638          res++;
639       }
640       ClauseFree(clause);
641    }
642    else
643    {
644       Warning("Cannot currently handle full first-order format!");
645    }
646    return res;
647 }
648 
649 
650 /*-----------------------------------------------------------------------
651 //
652 // Function: PCLGenerateCheck()
653 //
654 //   Generate a clause set that is unsatisfiable if the clause in step
655 //   is a logical conclusion of the precondition. For initial steps,
656 //   return NULL.
657 //
658 // Global Variables: -
659 //
660 // Side Effects    : Memory operations.
661 //
662 /----------------------------------------------------------------------*/
663 
PCLGenerateCheck(PCLProt_p prot,PCLStep_p step)664 ClauseSet_p PCLGenerateCheck(PCLProt_p prot, PCLStep_p step)
665 {
666    ClauseSet_p set = ClauseSetAlloc();
667 
668    if(PCLCollectPreconds(prot, step, set))
669    {
670       PCLNegSkolemizeClause(prot, step, set);
671    }
672    else
673    {
674       ClauseSetFree(set);
675       set = NULL;
676    }
677    return set;
678 }
679 
680 
681 /*-----------------------------------------------------------------------
682 //
683 // Function: PCLStepCheck()
684 //
685 //   Check the validity of a single PCL step. Return true if it checks
686 //   ok, false otherwise. At the moment, just print the generated
687 //   problem.
688 //
689 // Global Variables: -
690 //
691 // Side Effects    : By called functions, may print output.
692 //
693 /----------------------------------------------------------------------*/
694 
PCLStepCheck(PCLProt_p prot,PCLStep_p step,ProverType prover,char * executable,long time_limit)695 PCLCheckType PCLStepCheck(PCLProt_p prot, PCLStep_p step, ProverType
696            prover, char* executable, long time_limit)
697 {
698    ClauseSet_p problem;
699    PCLCheckType res=CheckFail;
700 
701    if(step->just->op==PCLOpSplitClause)
702    {
703       return CheckNotImplemented;
704    }
705    problem = PCLGenerateCheck(prot, step);
706    if(!problem)
707    {
708       return CheckByAssumption;
709    }
710    else
711    {
712       switch(prover)
713       {
714       case EProver:
715        if(pcl_verify_eprover(problem,executable,time_limit))
716        {
717           res = CheckOk;
718        }
719        break;
720       case Otter:
721        if(pcl_verify_otter(problem,executable,time_limit))
722        {
723           res = CheckOk;
724        }
725        break;
726       case Spass:
727        if(pcl_verify_spass(problem,executable,time_limit,prot->terms->sig))
728        {
729           res = CheckOk;
730        }
731        break;
732       default:
733        assert(false && "Not yet implemented");
734        break;
735       }
736       ClauseSetFree(problem);
737    }
738    return res;
739 }
740 
741 
742 /*-----------------------------------------------------------------------
743 //
744 // Function: PCLProtCheck()
745 //
746 //   Check all steps in a PCL listing. Return number of successful
747 //   steps.
748 //
749 // Global Variables: -
750 //
751 // Side Effects    : -
752 //
753 /----------------------------------------------------------------------*/
754 
PCLProtCheck(PCLProt_p prot,ProverType prover,char * executable,long time_limit,long * unchecked)755 long PCLProtCheck(PCLProt_p prot, ProverType prover, char* executable,
756         long time_limit, long *unchecked)
757 {
758    PStack_p      trav_stack,stack = PStackAlloc();
759    PStackPointer i;
760    PTree_p       cell;
761    PCLStep_p     step;
762    long          res=0;
763    PCLCheckType  check;
764 
765    *unchecked = 0;
766    trav_stack = PTreeTraverseInit(prot->steps);
767    while((cell=PTreeTraverseNext(trav_stack)))
768    {
769       step=cell->key;
770       PStackPushP(stack, step);
771    }
772    PTreeTraverseExit(trav_stack);
773 
774    for(i=0; i<PStackGetSP(stack); i++)
775    {
776       step = PStackElementP(stack,i);
777       if(OutputLevel)
778       {
779     fprintf(GlobalOut, "# Checking ");
780     PCLStepPrint(GlobalOut, step);
781     fputc('\n', GlobalOut);
782       }
783       check = PCLStepCheck(prot, step, prover, executable,
784             time_limit);
785       switch(check)
786       {
787       case CheckByAssumption:
788        OUTPRINT(1,"# Checked (by assumption)\n\n");
789        res++;
790        break;
791       case CheckOk:
792        OUTPRINT(1,"# Checked (by prover)\n\n");
793        res++;
794        break;
795       case CheckFail:
796        OUTPRINT(1,"# FAILED\n\n");
797        break;
798       case CheckNotImplemented:
799        OUTPRINT(1,"# Check not implemented, assuming true!\n\n");
800        (*unchecked)++;
801        break;
802       default:
803        assert(false);
804        break;
805       }
806    }
807    PStackFree(stack);
808    return res;
809 }
810 
811 
812 /*---------------------------------------------------------------------*/
813 /*                        End of File                                  */
814 /*---------------------------------------------------------------------*/
815 
816 
817 
818 
819 
820