Sindbad~EG File Manager
/* M. Beeson, 11.4.03
Connections between Otter and external simplification routines.
Communication is via ASCII strings. Terms are converted to strings
and all the literals of a clause are passed at once to an external
simplification function. The result comes back, again as an array of
strings (and an array of signs of the literals), and is converted
back to a new Otter clause. The memory used for both arrays of intermediate
strings is freed.
7.1.04 added postProcess
7.24.05 modified postProcess
3.20.06 changed include filenames to lowercase
7.2.06 changed 2 to 1 in line 154
7.20.08 added EXTERNAL_SIMPLIFY
*/
// #define DIAGNOSTICS // to see diagnostic printout
#include <stdlib.h> // calloc and free
#include <assert.h>
#include "header.h"
#include "simplify.h"
#include "externalsimplify.h"
#include "bsym.h"
#include "proto.h"
#include "export.h"
#include "heap.h"
#include "heaps.h"
// #define EXTERNAL_SIMPLIFY // comment this out to cut the link with MathXpert so it can compile without MathXpert
// If you want to supply some other simplification code you can put this back in
static FILE *tfp = NULL;
/*___________________________________________________________*/
void out_of_space(void)
{ abend("Out of space in MathXpert heap: more than one megabyte used for a simplification.");
}
/*_________________________________________________________________*/
static int needs_translation(struct term *t)
// return 1 if t contains SUM, PRODUCT, LIMIT, DIFF, or INTEGRAL
{ struct rel *r;
if(t->type!= COMPLEX)
return 0;
if(t->sym_num == SUM || t->sym_num == PRODUCT || t->sym_num == LIMIT ||
t->sym_num == DIFF || t->sym_num == INTEGRAL
)
return 1;
for(r=t->farg;r;r=r->narg)
{ if(needs_translation(r->argval))
return 1;
}
return 0;
}
/*_________________________________________________________________*/
static struct term *Translate(struct term *t)
// return a completely new term that can shortly be destroyed
// the term should translate t to the form Mathpert wants to see
// doesn't handle LIMITS, DIFF, or indefinite integrals yet FINISH THIS
{ struct term *ans;
struct rel *r, *r2, *r3, *r4;
if(t == NULL)
assert(0);
ans = get_term();
ans->type = t->type;
ans->sym_num = t->sym_num;
ans->varnum = t->varnum;
if (t->type != COMPLEX)
return(ans);
r3 = NULL;
r = t->farg;
if(t->sym_num == SUM || t->sym_num == PRODUCT || t->sym_num == INTEGRAL)
{ // switch the first and second arguments
r2 = get_rel();
ans->farg = r2;
r3 = get_rel();
r2->narg = r3;
r3->argval = Translate(r->argval);
r = r->narg;
r2->argval = Translate(r->argval);
r = r->narg;
if(r == NULL)
{ r3->narg = NULL;
return ans;
}
while(r)
{ r4 = get_rel();
r3->narg = r4;
r4->argval = Translate(r->argval);
r3 = r4;
r = r->narg;
}
return ans;
}
while (r)
{ r2 = get_rel();
if (r3 == NULL)
ans->farg = r2;
else
r3->narg = r2;
r2->argval = Translate(r->argval);
r3 = r2;
r = r->narg;
}
return(ans);
}
static void postProcess(char *s)
/* change != to /= in s */
/* and when '+' is followed immediately by '-', replace '+' by ' '. */
{ char *marker;
int i;
for(marker = s; marker[1]; ++marker)
{ if(marker[0] == '!' && marker[1] == '=')
*marker = '/';
if(marker[0] == '+')
{ for(i=1;;i++)
{ if(marker[i] != ' ')
break;
}
if(marker[i] == '-')
marker[0] = ' ';
}
}
}
/*_________________________________________________________________*/
static char * term_str(struct term *in)
/* convert an Otter term to a string */
/* Otter has code to print terms to a FILE * but not directly to a string.
Here we imitate print_term_length in writing the term to a temporary file
and reading it back into a character array. However, unlike print_term_length,
we do not want to impose a fixed limit on the length of that character array.
The return string is allocated using calloc by this function, so it can be
freed later on.
A complication is that because Otter-lambda requires bound variables to
be the first argument, terms involving SUM, PRODUCT, LIMIT, INTEGRAL, and
DIFF have to be translated to be readable by Mathpert. It would be nice
if these translations could be user-specified in list(translations) to support
different external simplifiers. For now that is done by Translate().
*/
{ int i;
// int c;
int length; // the length of the term as a string
fpos_t Length;
char *s; // will be allocated as soon as we know length.
int translation = needs_translation(in);
struct term *t = translation ? Translate(in) : in;
if (!tfp)
tfp = tmpfile();
rewind(tfp);
print_term(tfp, t);
// print_term_nl(stdout,t); DEBUG
fprintf(tfp, "%c", '\0'); /* end marker */
fgetpos(tfp,&Length);
// Length is 64 bits long, but according to the definition of fpos_t in stdio.h,
// depending on the target platform it could be a 64 bit int
// or a structure type. Hence the following complicated line of code:
// c = *((int *)(&Length) + 1); // high bits of Length. */
// But this code doesn't work right on Unix. So, let's forget the whole idea of checking c
fflush(tfp);
rewind(tfp);
// if(c)
// abend("term too big, or other error, in calling external simplification.");
length = * ((int *)(&Length));
s = calloc(length,sizeof(char));
if(!s)
abend("term too big, or other error, in calling external simplification.");
for (i = 0, s[i]=getc(tfp); s[i] ; s[++i]=getc(tfp));
if(translation)
zap_term(t);
postProcess(s); // change != to /=
return s;
}
/*_________________________________________________________________*/
static void set_vars_clause(struct clause *ans)
// fill in the t->varnum fields everywhere, consistently.
// similar to set_vars but that only works on one term at a time.
{ char *varnames[MAX_VARS];
struct literal *L;
memset(varnames,0,MAX_VARS * sizeof(char *));
for(L=ans->first_lit;L;L=L->next_lit)
set_vars_term(L->atom,varnames);
}
/*_________________________________________________________________*/
struct clause * Simplify(struct clause *c)
/* call an external simplification function (from Mathpert, for example) to
simplify each literal in the clause. While simplifying each literal,
the external routine should assume the negations of the other literals.
If there was any simplification, it returns the new clause, leaving the
old clause unmodified, and adds 'SIMPLIFY_RULE' to the justification list.
If there was no simplification to be done it returns NULL.
Example 1: if the input clause is a unit
clause f(sqrt(a^2)) = g(a), then it would simplify to f(a) = g(a) | a < 0,
since the side condition for simplifying sqrt(a^2) to a is a >= 0, so we
add the negation of that condition to the output clause.
Example 2: If the input clause is sqrt(a^2) != a, then it simplifies to a != a | a < 0,
which in turn simplifies to a < 0. Hence if a >= 0 is already deduced, after
the Simplify step is complete one more step will finish the proof.
Example 3:
if the input clause is sqrt(a^2) = a | a < 0,
then the negation of a < 0 (namely a >= 0) is assumed before simplifying sqrt(a^2) = a,
so it simplifies to true. Then true | a < 0 is true, so the entire clause becomes
true and is dropped. This is correct as it contains no information beyond what
simplify can supply. The same is true if sqrt(a^2) = a simplifies to a >= 0;
then again the clause simplies to true.
Example 4:
if the input clause is sqrt(a^2) = a, then it simplifies to a >= 0 and this is
returned. This is a DIFFERENT RULE of simplification than just sqrt(a^2) = a
if a >= 0, as it also contains the information that sqrt(a^2) = a is false when a < 0.
If we only had the latter simplification rule (i.e. leaving open the possibility
that sqrt(a^2) might = a when a is negative) then simplify would yield 'true'
with the assumption a >= 0. The new clause is then true | a < 0 which is true.
This may not look right at first but it is: it means, if you can derive
a contradiction from 'true', then you can derive one from sqrt(a^2) = a.
Look how this works: if we also had a < 0, we'd be able to get a contradiction
using the first rule [that sqrt(a^2) = a reduces to a >= 0],
proving the theorem that if a < 0 then sqrt(a^2) != a.
But using the second rule, we would not be able to finish the proof, which is correct.
Domains: we need to assume the domain of the clause throughout. Then, at the end,
we need to add back in the negations of the literals whose conjunction is the domain.
Some of those assumptions might be 'or' terms; that will cause trouble in bringing the
output to clausal form.
*/
{ struct int_ptr *p1,*q1;
int err = 1;
int p = 0;
int nlits = 1;
char **lits, **NewLits;
int i,nNewLits;
struct literal *L, *last_lit;
struct clause *ans;
char *signs, *newSigns;
static int nospace_flag = 0;
if(!nospace_flag)
set_nospace_handler(out_of_space);
if(!c->first_lit)
return 0;
for(L=c->first_lit;L->next_lit;L=L->next_lit)
++nlits;
lits = (char **) calloc(nlits, sizeof(char *));
signs = (char *) calloc(nlits, sizeof(char));
for(i=0,L=c->first_lit;L;L=L->next_lit,++i)
{ lits[i] = term_str(L->atom); // prepare strings to pass to external simplifier
signs[i] = L->sign;
}
if(i != nlits)
assert(0);
nNewLits = 100 + nlits;
NewLits = (char **) calloc(nNewLits,sizeof(char*));
newSigns = (char *) calloc(nNewLits, sizeof(char));
#ifdef DIAGNOSTICS
for(i=0;i<nlits;i++) // use this if you need to see output before ExternalSimplify runs
fprintf(stdout,"Simplifying: %s\n", lits[i]);
#endif
#ifdef EXTERNALSIMPLIFY
err = ExternalSimplify(nlits,lits,signs, &nNewLits,NewLits,newSigns); // call external simplification code
#endif
#ifdef DIAGNOSTICS
if(err)
{ for(i=0;i<nlits;i++)
{ fprintf(stdout,"Did not simplify: %s\n", lits[i]);
}
}
if(!err && nlits == nNewLits)
{ for(i=0;i<nlits;i++)
{ if(signs[i])
fprintf(stdout,"Simplifying: %s\n", lits[i]);
else
fprintf(stdout,"Simplifying: -(%s)\n", lits[i]);
if(newSigns[i])
fprintf(stdout,"To : %s\n", NewLits[i]);
else
fprintf(stdout,"To : -(%s)\n", NewLits[i]);
}
}
else if(!err)
{ for(i=0;i<nlits;i++)
{ if(signs[i]) fprintf(stdout,"Simplifying: %s\n", lits[i]);
else fprintf(stdout,"Simplifying -(%s)\n", lits[i]);
}
for(i=0;i<nNewLits;i++)
{ if(newSigns[i]) fprintf(stdout, "To : %s\n", NewLits[i]);
else fprintf(stdout, "To : - (%s)\n", NewLits[i]);
}
}
#endif
for(i=0;i<nlits;i++)
free(lits[i]); // allocated by term_str before the call to ExternalSimplify
free(lits);
free(signs);
if(err == -1)
abend("Malloc could not allocate memory for the MathXpert heap.");
if(err)
return NULL; // no simplification;
// simplification succeeded
// construct new clause
ans = get_clause();
ans->first_lit = NULL; // in case nNewLits == 1 and the only literal is 'false'
for(i=0;i<nNewLits;i++,L = L->next_lit)
{ while( i<nNewLits &&
(
(!strcmp(NewLits[i],"false") && newSigns[i] == 1) ||
(!strcmp(NewLits[i],"true") && newSigns[i] == 0)
)
)
++i;
if(i==nNewLits)
break;
L = get_literal();
if(i==0)
ans->first_lit = L;
else
last_lit->next_lit = L;
last_lit = L;
L->sign = newSigns[i];
p = 0;
L->atom = str_to_term(NewLits[i],&p,0);
if(L->atom == NULL)
assert(0);
L->atom->occ.lit = L;
L->container = ans;
if(L->atom->sym_num == NE)
{ L->atom->sym_num = '=';
L->sign = L->sign ? 0 : 1;
}
if (L->atom->sym_num == '=')
{ L->atom->varnum = L->sign ? POS_EQ : NEG_EQ;
// vital, or it won't be indexed for use by paramodulation
}
else
L->atom->varnum = NORM_ATOM;
}
set_vars_clause(ans); // fill in the t->varnum fields everywhere, consistently.
// Finally, add SIMPLIFY_RULE to the list of justifications for this new clause
ans->parents = get_int_ptr();
// Now copy c->parents
q1 = ans->parents;
for(p1 = c->parents; p1; p1 = p1->next)
{ q1->i = p1->i;
q1->next = get_int_ptr();
q1 = q1->next;
}
q1->i = SIMPLIFY_RULE; // Now add SIMPLIFY to the list of justifications
q1->next = get_int_ptr();
q1->next->i = c->id;
q1->next->next = NULL;
free(NewLits);
free(newSigns);
#ifdef DIAGNOSTICS
fprintf(stdout,"Input to Simplify\n"); // DEBUG
print_clause(stdout,c); // DEBUG
fprintf(stdout,"Simplified Otter clause:\n"); // DEBUG
print_clause(stdout,ans); // DEBUG
#endif
return ans; // success
}
/*_________________________________________________________________*/
int SimplifyInPlace(struct clause *c)
/* Like Simplify, but in case there is simplification to be done,
it replaces the literals of *c with the new literals and appends
SIMPLIFY_RULE to the list of justifications of c, rather than
using a new clause and list of justifications. Return 0 if
some simplification took place, and 1 if no simplification took place.
*/
{ struct clause *ans;
struct literal *l;
struct int_ptr *p,*q;
ans = Simplify(c);
if(!ans)
return 1;
// should decrease reference counts of the literals in c
for(l=ans->first_lit;l;l=l->next_lit)
l->container = c;
c->first_lit = ans->first_lit;
p = c->parents;
if(p == NULL)
{ c->parents = get_int_ptr();
c->parents->i = SIMPLIFY_RULE;
c->parents->next = NULL;
}
while(p->next)
{ q = p;
p = p->next;
}
p->next = get_int_ptr();
p->next->i = SIMPLIFY_RULE;
p->next->next = NULL;
return 0;
}
/*_____________________________________________________________________*/
struct clause *Solve(struct clause *c)
// similar to Simplify, but tries to find a value of the variables
// return NULL if clause can't be solved. Otherwise return a new
// clause, leaving c undisturbed.
{ struct clause *ans;
ans = c; // stub for now
return ans;
}
/*_____________________________________________________________________*/
int SolveInPlace(struct clause *c)
// similar to Simplify, but tries to find a value of the variables
// puts the solved clause where c was, destroying old literals.
// Return 0 for success, 1 for failure to solve.
{ struct clause *ans;
ans = c; // stub for now
return 1;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists