Ticket #3431: trac772-qepcad-interface.patch

File trac772-qepcad-interface.patch, 94.7 kB (added by cwitty, 7 months ago)
  • a/sage/interfaces/all.py

    old new  
    2121from mupad import mupad, mupad_console, Mupad  # NOT functional yet 
    2222from mwrank import mwrank, Mwrank, mwrank_console 
    2323from octave import octave, octave_console, octave_version, Octave 
     24from qepcad import qepcad, qepcad_console, qepcad_version, qepcad_formula 
    2425from qsieve import qsieve 
    2526from singular import singular, singular_console, singular_version, is_SingularElement, Singular 
    2627from sage0 import sage0 as sage0, sage0_console, sage0_version, Sage 
  • /dev/null

    old new  
     1r""" 
     2Interface to QEPCAD B 
     3 
     4The basic function of QEPCAD is to construct cylindrical algebraic 
     5decompositions (CADs) of $\RR^k$, given a list of polynomials.  Using 
     6this CAD, it is possible to perform quantifier elimination and formula 
     7simplification. 
     8 
     9A CAD for a set $A$ of $k$-variate polynomials decomposes $\RR^j$ into 
     10disjoint cells, for each $j$ in $0 \leq j \leq k$.  The sign of each 
     11polynomial in $A$ is constant in each cell of $\RR^k$, and for each 
     12cell in $\RR^j$ ($j > 1$), the projection of that cell into 
     13$\RR^{j-1}$ is a cell of $\RR^{j-1}$.  (This property makes the 
     14decomposition ``cylindrical''.) 
     15 
     16Given a formula $\exists x. P(a,b,x) = 0$ (for a polynomial $P$), and 
     17a cylindrical algebraic decomposition for $P$, we can eliminate the 
     18quantifier (find an equivalent formula in the two variables $a$, $b$ 
     19without the quantifier $\exists$) as follows.  For each cell $C$ in 
     20$\RR^2$, find the cells of $\RR^3$ which project to $C$.  (This 
     21collection is called the \emph{stack} over $C$.)  Mark $C$ as true if 
     22some member of the stack has sign $= 0$; otherwise, mark $C$ as false. 
     23Then, construct a polynomial formula in $a$, $b$ which specifies 
     24exactly the true cells (this is always possible).  The same technique 
     25works if the body of the quantifier is any boolean combination of 
     26polynomial equalities and inequalities. 
     27 
     28Formula simplification is a similar technique.  Given a formula which 
     29describes a simple set of $\RR^k$ in a complicated way as a boolean 
     30combination of polynomial equalities and inequalities, QEPCAD can 
     31construct a CAD for the polynomials and recover a simple equivalent 
     32formula. 
     33 
     34Note that while the following documentation is tutorial in nature, and 
     35is written for people who may not be familiar with QEPCAD, it is 
     36documentation for the \sage interface rather than for QEPCAD.  As 
     37such, it does not cover several issues that are very important to use 
     38QEPCAD efficiently, such as variable ordering, the efficient use of 
     39the alternate quantifiers and \code{_root_} expressions, the 
     40\code{measure-zero-error} command, etc.  For more information on 
     41QEPCAD, see the online documentation at 
     42\url{http://www.cs.usna.edu/~qepcad/B/QEPCAD.html} and Chris Brown's 
     43tutorial handout and slides from 
     44\url{http://www.cs.usna.edu/~wcbrown/research/ISSAC04/Tutorial.html}. 
     45(Several of the examples in this documentation came from these 
     46sources.) 
     47 
     48The examples below require that the optional qepcad package is installed. 
     49 
     50QEPCAD can be run in a fully automatic fashion, or interactively. 
     51We first demonstrate the automatic use of QEPCAD. 
     52 
     53Since \sage has no built-in support for quantifiers, this interface 
     54provides \code{qepcad_formula} which helps construct quantified 
     55formulas in the syntax QEPCAD requires. 
     56 
     57sage: var('a,b,c,d,x,y,z') 
     58(a, b, c, d, x, y, z) 
     59sage: qf = qepcad_formula 
     60 
     61We start with a simple example.  Consider an arbitrarily-selected 
     62ellipse: 
     63 
     64sage: ellipse = 3*x^2 + 2*x*y + y^2 - x + y - 7 
     65 
     66What is the projection onto the $x$ axis of this ellipse?  First we  
     67construct a formula asking this question. 
     68 
     69sage: F = qf.exists(y, ellipse == 0); F 
     70(E y)[y^2 + 2 x y + y + 3 x^2 - x - 7 = 0] 
     71 
     72Then we run qepcad to get the answer. 
     73 
     74sage: qepcad(F) 
     758 x^2 - 8 x - 29 <= 0 
     76 
     77How about the projection onto the $y$ axis? 
     78 
     79sage: qepcad(qf.exists(x, ellipse == 0)) 
     808 y^2 + 16 y - 85 <= 0 
     81 
     82QEPCAD deals with more quantifiers than just ``exists'', of course. 
     83Besides the standard ``forall'', there are also ``for infinitely 
     84many'', ``for all but finitely many'', ``for a connected subset'', and 
     85``for exactly $k$''.  The \function{qepcad} documentation has examples 
     86of all of these; here we'll just give one example.   
     87 
     88First we construct a circle: 
     89 
     90sage: circle = x^2 + y^2 - 3 
     91 
     92For what values $k$ does a vertical line $x=k$ intersect the combined 
     93figure of the circle and ellipse exactly three times? 
     94 
     95sage: F = qf.exactly_k(3, y, circle * ellipse == 0); F 
     96(X3 y)[(y^2 + x^2 - 3) (y^2 + 2 x y + y + 3 x^2 - x - 7) = 0] 
     97sage: qepcad(F) 
     988 x^2 - 8 x - 29 <= 0 /\ x^2 - 3 <= 0 /\ 8 x^4 - 26 x^2 - 4 x + 13 >= 0 /\ [ 8 x^4 - 26 x^2 - 4 x + 13 = 0 \/ x^2 - 3 = 0 \/ 8 x^2 - 8 x - 29 = 0 ] 
     99 
     100Here we see that the solutions are among the eight ($4 + 2 + 2$) roots 
     101of the three polynomials inside the brackets, but not all of these 
     102roots are solutions; the polynomial inequalities outside the brackets 
     103are needed to select those roots that are solutions. 
     104 
     105QEPCAD also supports an extended formula language, where 
     106$\mbox{_root_}k \quad P(\bar x, y)$ refers to a particular zero of 
     107$P(\bar x, y)$ (viewed as a polynomial in $y$).  If there are $n$ roots, 
     108then _root_$1$ refers to the least root and _root_$n$ refers to the greatest;  
     109also, _root_$-n$ refers to the least root and _root_$-1$ refers to the  
     110greatest. 
     111 
     112This extended language is available both on input and output; see the 
     113QEPCAD documentation for more information on how to use this syntax on 
     114input.  We can request output that's intended to be easy to interpret 
     115geometrically; then QEPCAD will use the extended language to produce 
     116a solution formula without the selection polynomials. 
     117 
     118sage: qepcad(F, solution='geometric') 
     119x = _root_1 8 x^2 - 8 x - 29 
     120\/ 
     1218 x^4 - 26 x^2 - 4 x + 13 = 0 
     122\/ 
     123x = _root_-1 x^2 - 3 
     124 
     125We then see that the 6 solutions correspond to the vertical tangent on 
     126the left side of the ellipse, the four intersections between the 
     127ellipse and the circle, and the vertical tangent on the right side of 
     128the circle. 
     129 
     130Let's do some basic formula simplification and visualization. 
     131We'll look at the region which is inside both the ellipse and the circle: 
     132 
     133sage: F = qf.and_(ellipse < 0, circle < 0); F 
     134[y^2 + 2 x y + y + 3 x^2 - x - 7 < 0 /\ y^2 + x^2 - 3 < 0] 
     135sage: qepcad(F) 
     136y^2 + 2 x y + y + 3 x^2 - x - 7 < 0 /\ y^2 + x^2 - 3 < 0 
     137 
     138We get back the same formula we put in.  This is not surprising (we 
     139started with a pretty simple formula, after all), but it's not very 
     140enlightening either.  Again, if we ask for a 'geometric' output, then we see 
     141an output that lets us understand something about the shape of the solution  
     142set. 
     143 
     144sage: qepcad(F, solution='geometric') 
     145[ 
     146  [ 
     147    x = _root_-2 8 x^4 - 26 x^2 - 4 x + 13 
     148    \/ 
     149    x = _root_2 8 x^4 - 26 x^2 - 4 x + 13 
     150    \/ 
     151    8 x^4 - 26 x^2 - 4 x + 13 < 0 
     152  ] 
     153  /\ 
     154  y^2 + 2 x y + y + 3 x^2 - x - 7 < 0 
     155  /\ 
     156  y^2 + x^2 - 3 < 0 
     157] 
     158\/ 
     159[ 
     160  x > _root_2 8 x^4 - 26 x^2 - 4 x + 13 
     161  /\ 
     162  x < _root_-2 8 x^4 - 26 x^2 - 4 x + 13 
     163  /\ 
     164  y^2 + x^2 - 3 < 0 
     165] 
     166 
     167There's another reason to prefer output using _root_ expressions; not 
     168only does it sometimes give added insight into the geometric 
     169structure, it also can be more efficient to construct.  Consider this 
     170formula for the projection of a particular semicircle onto the $x$ 
     171axis: 
     172 
     173sage: F = qf.exists(y, qf.and_(circle == 0, x + y > 0)); F 
     174(E y)[y^2 + x^2 - 3 = 0 /\ y + x > 0] 
     175sage: qepcad(F) 
     176x^2 - 3 <= 0 /\ [ x > 0 \/ 2 x^2 - 3 < 0 ] 
     177 
     178Here, the formula $x > 0$ had to be introduced in order to get a 
     179solution formula; the original CAD of F did not include the  
     180polynomial $x$.  To avoid having QEPCAD do the extra work to come up  
     181with a solution formula, we can tell it to use the extended language;  
     182it's always possible to construct a solution formula in the extended  
     183language without introducing new polynomials. 
     184 
     185sage: qepcad(F, solution='extended') 
     186x^2 - 3 <= 0 /\ x > _root_1 2 x^2 - 3 
     187 
     188Up to this point, all the output we've seen has basically been in the 
     189form of strings; there is no support (yet) for parsing these outputs 
     190back into \sage polynomials (partly because \sage does not yet have 
     191support for symbolic conjunctions and disjunctions).  The function  
     192\function{qepcad} supports three more output types that give numbers which 
     193can be manipulated in \sage: any-point, all-points, and cell-points. 
     194 
     195These output types give dictionaries mapping variable names to values. 
     196With any-point, \function{qepcad} either produces a single dictionary 
     197specifying a point where the formula is true, or raises an exception 
     198if the formula is false everywhere.  With all-points, 
     199\function{qepcad} either produces a list of dictionaries for all 
     200points where the formula is true, or raises an exception if the 
     201formula is true on infinitely many points.  With cell-points, 
     202\function{qepcad} produces a list of dictionaries with one point for 
     203each cell where the formula is true.  (This means you will have at least 
     204one point in each connected component of the solution, although you will 
     205often have many more points than that.) 
     206 
     207Let's revisit some of the above examples and get some points to play 
     208with.  We'll start by finding a point on our ellipse. 
     209 
     210sage: p = qepcad(ellipse == 0, solution='any-point'); p 
     211{'y': [0.96850196850295267 .. 0.96850196850295279], 'x': [-1.4685019685029528 .. -1.4685019685029525]} 
     212 
     213(Note that despite appearances, these are really exact numbers.) 
     214 
     215We can verify that this point is a solution.  To do so, we create 
     216a copy of ellipse as a polynomial over QQ (instead of a symbolic  
     217expresision). 
     218 
     219sage: pellipse = QQ['x,y'](ellipse) 
     220sage: pellipse(**p) == 0 
     221True 
     222 
     223For cell-points, let's look at points \emph{not} on the ellipse. 
     224 
     225sage: pts = qepcad(ellipse != 0, solution='cell-points'); pts 
     226[{'y': 0, 'x': 4}, {'y': 1, 'x': [2.4685019685029523 .. 2.4685019685029528]}, {'y': -9, 'x': [2.4685019685029523 .. 2.4685019685029528]}, {'y': 9, 'x': 1/2}, {'y': -1, 'x': 1/2}, {'y': -5, 'x': 1/2}, {'y': 3, 'x': [-1.4685019685029528 .. -1.4685019685029525]}, {'y': -1, 'x': [-1.4685019685029528 .. -1.4685019685029525]}, {'y': 0, 'x': -3}] 
     227 
     228For the points here which are in full-dimensional cells, QEPCAD has the 
     229freedom to choose rational sample points, and it does so. 
     230 
     231And, of course, all these points really are not on the ellipse. 
     232 
     233sage: [pellipse(**p) != 0 for p in pts] 
     234[True, True, True, True, True, True, True, True, True] 
     235 
     236Finally, for all-points, let's look again at finding vertical lines that  
     237intersect the union of the circle and the ellipse exactly three times. 
     238 
     239sage: F = qf.exactly_k(3, y, circle * ellipse == 0); F 
     240(X3 y)[(y^2 + x^2 - 3) (y^2 + 2 x y + y + 3 x^2 - x - 7) = 0] 
     241sage: pts = qepcad(F, solution='all-points'); pts 
     242[{'x': [1.7320508075688771 .. 1.7320508075688775]}, {'x': [1.7310549134625334 .. 1.7310549134625338]}, {'x': [0.67891138420800389 .. 0.67891138420800401]}, {'x': [-0.94177273774171678 .. -0.94177273774171665]}, {'x': [-1.4681935599288210 .. -1.4681935599288207]}, {'x': [-1.4685019685029528 .. -1.4685019685029525]}] 
     243 
     244Since $y$ is bound by the quantifier, the solutions only refer to $x$. 
     245 
     246We can substitute one of these solutions into the original equation: 
     247 
     248sage: pt = pts[0] 
     249sage: pcombo = QQ['x,y'](circle * ellipse) 
     250sage: intersections = pcombo(y=polygen(AA, 'y'), **pt); intersections 
     251y^4 + [4.4641016151377543 .. 4.4641016151377553]*y^3 + [0.26794919243112269 .. 0.26794919243112276]*y^2 
     252 
     253and verify that it does have three roots: 
     254 
     255sage: intersections.roots() 
     256[([-4.4032490056009586 .. -4.4032490056009576], 1), ([-0.060852609536796533 .. -0.060852609536796525], 1), ([0.00000000000000000 .. 0.00000000000000000], 2)] 
     257 
     258Let's check all six solutions. 
     259 
     260sage: [len(pcombo(y=polygen(AA, 'y'), **p).roots()) for p in pts] 
     261[3, 3, 3, 3, 3, 3] 
     262 
     263We said earlier that we can run QEPCAD either automatically or  
     264interactively.  Now that we've discussed the automatic modes, let's turn  
     265to interactive uses. 
     266 
     267If the \function{qepcad} function is passed \code{interact=True}, then 
     268instead of returning a result, it returns an object of class 
     269\class{Qepcad} representing a running instance of QEPCAD that you can 
     270interact with.  For example: 
     271 
     272sage: qe = qepcad(qf.forall(x, x^2 + b*x + c > 0), interact=True); qe 
     273QEPCAD object in phase 'Before Normalization' 
     274 
     275This object is a fairly thin wrapper over QEPCAD; most QEPCAD commands 
     276are available as methods on the \class{Qepcad} object.  Given a 
     277\class{Qepcad} object \var{qe}, you can type \code{qe.[tab]} to see 
     278the available QEPCAD commands; to see the documentation for an 
     279individual QEPCAD command, for example \code{d_setting}, you can type 
     280\code{qe.d_setting?}.  (In QEPCAD, this command is called 
     281\code{d-setting}; we systematically replace hyphens with underscores 
     282for this interface.) 
     283 
     284The execution of QEPCAD is divided into four phases; most commands are 
     285not available during all phases.  We saw above that QEPCAD starts out 
     286in phase `Before Normalization'; we see that the \code{d_cell} command 
     287is not available in this phase: 
     288 
     289sage: qe.d_cell() 
     290Error GETCID: This command is not active here. 
     291 
     292We will focus here on the fourth (and last) phase, `Before Solution', 
     293because this interface has special support for some operations in this 
     294phase.  Consult the QEPCAD documentation for information on the other 
     295phases. 
     296 
     297We can tell QEPCAD to finish off the current phase and move to the 
     298next with its \code{go} command.  (There is also the \code{step} 
     299command, which partially completes a phase for phases that have 
     300multiple steps, and the \code{finish} command, which runs QEPCAD to 
     301completion.) 
     302 
     303sage: qe.go() 
     304QEPCAD object has moved to phase 'Before Projection (x)' 
     305sage: qe.go() 
     306QEPCAD object has moved to phase 'Before Choice' 
     307sage: qe.go() 
     308QEPCAD object has moved to phase 'Before Solution' 
     309 
     310Note that the \class{Qepcad} object returns the new phase whenever the 
     311phase changes, as a convenience for interactive use; except that when 
     312the new phase is `EXITED', the solution formula printed by QEPCAD is 
     313returned instead. 
     314 
     315sage: qe.go() 
     3164 c - b^2 > 0 
     317sage: qe 
     318QEPCAD object in phase 'EXITED' 
     319 
     320Let's pick a nice, simple example, return to phase 4, and explore the 
     321resulting \var{qe} object. 
     322 
     323sage: qe = qepcad(circle == 0, interact=True); qe 
     324QEPCAD object in phase 'Before Normalization' 
     325sage: qe.go(); qe.go(); qe.go() 
     326QEPCAD object has moved to phase 'Before Projection (y)' 
     327QEPCAD object has moved to phase 'Before Choice' 
     328QEPCAD object has moved to phase 'Before Solution' 
     329 
     330We said before that QEPCAD creates ``cylindrical algebraic 
     331decompositions''; since we have a bivariate polynomial, we get 
     332decompositions of $\RR^0$, $\RR^1$, and $\RR^2$.  In this case, where 
     333our example is a circle of radius $\sqrt{3}$ centered on the origin, 
     334these decompositions are as follows: 
     335 
     336The decomposition of $\RR^0$ is trivial (of course).  The 
     337decomposition of $\RR^1$ has five cells: $x < -\sqrt{3}$, $x = 
     338-\sqrt{3}$, $-\sqrt{3} < x < \sqrt{3}$, $x = \sqrt{3}$, and $x > 
     339\sqrt{3}$.  These five cells comprise the \emph{stack} over the single 
     340cell in the trivial decomposition of $\RR^0$. 
     341 
     342These five cells give rise to five stacks in $\RR^2$.  The first and 
     343fifth stack have just one cell apiece.  The second and fourth stacks 
     344have three cells: $y < 0$, $y = 0$, and $y > 0$.  The third stack has 
     345five cells: below the circle, the lower semicircle, the interior of 
     346the circle, the upper semicircle, and above the circle. 
     347 
     348QEPCAD (and this QEPCAD interface) number the cells in a stack 
     349starting with 1.  Each cell has an \emph{index}, which is a tuple of 
     350integers describing the path to the cell in the tree of all cells. 
     351For example, the cell ``below the circle'' has index (3,1) (the first 
     352cell in the stack over the third cell of $\RR^1$) and the interior of 
     353the circle has index (3,3). 
     354 
     355We can view these cells with the QEPCAD command \code{d_cell}.  For 
     356instance, let's look at the cell for the upper semicircle: 
     357 
     358sage: qe.d_cell(3, 4) 
     359---------- Information about the cell (3,4) ---------- 
     360Level                       : 2 
     361Dimension                   : 1 
     362Number of children          : 0 
     363Truth value                 : T    by trial evaluation. 
     364Degrees after substitution  : Not known yet or No polynomial. 
     365Multiplicities              : ((1,1)) 
     366Signs of Projection Factors 
     367Level 1  : (-) 
     368Level 2  : (0) 
     369----------   Sample point  ----------  
     370The sample point is in a PRIMITIVE representation. 
     371<BLANKLINE> 
     372alpha = the unique root of x^2 - 3 between 0 and 4 
     373      = 1.7320508076- 
     374<BLANKLINE> 
     375Coordinate 1 = 0 
     376             = 0.0000000000 
     377Coordinate 2 = alpha 
     378             = 1.7320508076- 
     379---------------------------------------------------- 
     380 
     381We see that, the level of this cell is 2, meaning that it is part of 
     382the decomposition of $\RR^2$.  The dimension is 1, meaning that the 
     383cell is homeomorphic to a line (rather than a plane or a point).  The 
     384sample point gives the coordinates of one point in the cell, both 
     385symbolically and numerically. 
     386 
     387For programmatic access to cells, we've defined a \sage wrapper class 
     388\class{QepcadCell}.  These cells can be created with the \method{cell} 
     389method; for example: 
     390 
     391sage: c = qe.cell(3, 4); c 
     392QEPCAD cell (3, 4) 
     393 
     394A \class{QepcadCell} has accessor methods for the important state held 
     395within a cell.  For instance: 
     396 
     397sage: c.level() 
     3982 
     399sage: c.index() 
     400(3, 4) 
     401sage: qe.cell(3).number_of_children() 
     4025 
     403sage: len(qe.cell(3)) 
     4045 
     405 
     406One particularly useful thing we can get from a cell is its sample point, 
     407as \sage algebraic real numbers. 
     408 
     409sage: c.sample_point() 
     410(0, [1.7320508075688771 .. 1.7320508075688775]) 
     411sage: c.sample_point_dict() 
     412{'y': [1.7320508075688771 .. 1.7320508075688775], 'x': 0} 
     413 
     414We've seen that we can get cells using the \method{cell} method. 
     415There are several QEPCAD commands that print lists of cells; we can 
     416also get cells using the \method{make_cells} method, passing it the 
     417output of one of these commands. 
     418 
     419sage: qe.make_cells(qe.d_true_cells()) 
     420[QEPCAD cell (4, 2), QEPCAD cell (3, 4), QEPCAD cell (3, 2), QEPCAD cell (2, 2)] 
     421 
     422Also, the cells in the stack over a given cell can be accessed using 
     423array subscripting or iteration.  (Remember that cells in a stack are 
     424numbered starting with one; we preserve this convention in the 
     425array-subscripting syntax.) 
     426 
     427sage: c = qe.cell(3) 
     428sage: c[1] 
     429QEPCAD cell (3, 1) 
     430sage: [c2 for c2 in c] 
     431[QEPCAD cell (3, 1), QEPCAD cell (3, 2), QEPCAD cell (3, 3), QEPCAD cell (3, 4), QEPCAD cell (3, 5)] 
     432 
     433We can do one more thing with a cell: we can set its truth value. 
     434Once the truth values of the cells have been set, we can get QEPCAD to 
     435produce a formula which is true in exactly the cells we have selected. 
     436This is useful if QEPCAD's quantifier language is insufficient to 
     437express your problem. 
     438 
     439For example, consider again our combined figure of the circle and the 
     440ellipse.  Suppose you want to find all vertical lines that intersect 
     441the circle twice, and also intersect the ellipse twice.  The vertical 
     442lines that intersect the circle twice can be found by simplifying: 
     443 
     444sage: F = qf.exactly_k(2, y, circle == 0); F 
     445(X2 y)[y^2 + x^2 - 3 = 0] 
     446 
     447and the vertical lines that intersect the ellipse twice are expressed by: 
     448 
     449sage: G = qf.exactly_k(2, y, ellipse == 0); G 
     450(X2 y)[y^2 + 2 x y + y + 3 x^2 - x - 7 = 0] 
     451 
     452and the lines that intersect both figures would be: 
     453 
     454sage: qf.and_(F, G) 
     455Traceback (most recent call last): 
     456... 
     457ValueError: QEPCAD formulas must be in prenex (quantifiers outermost) form 
     458 
     459...except that QEPCAD does not support formulas like this; in QEPCAD 
     460input, all logical connectives must be inside all quantifiers. 
     461 
     462Instead, we can get QEPCAD to construct a CAD for our combined figure 
     463and set the truth values ourselves.  (The exact formula we use doesn't 
     464matter, since we're going to replace the truth values in the cells; we 
     465just need to use a formula that uses both polynomials.) 
     466 
     467sage: qe = qepcad(qf.and_(ellipse == 0, circle == 0), interact=True) 
     468sage: qe.go(); qe.go(); qe.go() 
     469QEPCAD object has moved to phase 'Before Projection (y)' 
     470QEPCAD object has moved to phase 'Before Choice' 
     471QEPCAD object has moved to phase 'Before Solution' 
     472 
     473Now we want to find all cells $c$ in the decomposition of $\RR^1$ such 
     474that the stack over $c$ contains exactly two cells on the ellipse, and 
     475also contains exactly two cells on the circle. 
     476 
     477Our input polynomials are ``level-2 projection factors'', we see: 
     478 
     479sage: qe.d_proj_factors() 
     480P_1,1  = fac(J_1,1) = fac(dis(A_2,1)) 
     481       = 8 x^2 - 8 x - 29 
     482P_1,2  = fac(J_1,2) = fac(dis(A_2,2)) 
     483       = x^2 - 3 
     484P_1,3  = fac(J_1,3) = fac(res(A_2,1|A_2,2)) 
     485       = 8 x^4 - 26 x^2 - 4 x + 13 
     486A_2,1  = input 
     487       = y^2 + 2 x y + y + 3 x^2 - x - 7 
     488A_2,2  = input 
     489       = y^2 + x^2 - 3 
     490 
     491so we can test whether a cell is on the ellipse by checking that the 
     492sign of the corresponding projection factor is 0 in our cell. 
     493For instance, the cell (12,2) is on the ellipse: 
     494 
     495sage: qe.cell(12,2).signs()[1][0] 
     4960 
     497 
     498So we can update the truth values as desired like this: 
     499 
     500sage: for c in qe.cell(): 
     501...       count_ellipse = 0 
     502...       count_circle = 0 
     503...       for c2 in c: 
     504...           count_ellipse += (c2.signs()[1][0] == 0) 
     505...           count_circle += (c2.signs()[1][1] == 0) 
     506...       c.set_truth(count_ellipse == 2 and count_circle == 2) 
     507 
     508and then we can get our desired solution formula.  (The 'G' stands for 
     509'geometric', and gives solutions using the same rules as 
     510\code{solution='geometric'} described above.) 
     511 
     512sage: qe.solution_extension('G') 
     5138 x^2 - 8 x - 29 < 0 
     514/\ 
     515x^2 - 3 < 0 
     516 
     517AUTHORS: 
     518    -- Carl Witty (2008-03): initial version 
     519""" 
     520 
     521########################################################################## 
     522# 
     523#       Copyright (C) 2008 Carl Witty <Carl.Witty@gmail.com> 
     524# 
     525#  Distributed under the terms of the GNU General Public License (GPL) 
     526# 
     527#                  http://www.gnu.org/licenses/ 
     528# 
     529########################################################################## 
     530 
     531from __future__ import with_statement 
     532 
     533import sage.misc.misc 
     534import pexpect 
     535import re 
     536import sys 
     537 
     538from sage.misc.flatten import flatten 
     539from sage.misc.sage_eval import sage_eval 
     540from sage.misc.preparser import implicit_mul 
     541 
     542from expect import Expect, ExpectFunction, AsciiArtString 
     543 
     544def _qepcad_cmd(memcells=None): 
     545    r""" 
     546    Construct a QEPCAD command line.  Optionally set the 
     547    number of memory cells to use. 
     548 
     549    EXAMPLES: 
     550        sage: from sage.interfaces.qepcad import _qepcad_cmd 
     551        sage: _qepcad_cmd() 
     552        'env qe=.../local/ qepcad ' 
     553        sage: _qepcad_cmd(memcells=8000000) 
     554        'env qe=.../local/ qepcad +N8000000' 
     555    """ 
     556    if memcells is not None: 
     557        memcells_arg = '+N%s' % memcells 
     558    else: 
     559        memcells_arg = '' 
     560    return "env qe=%s qepcad %s"%(sage.misc.misc.SAGE_LOCAL, memcells_arg) 
     561 
     562_command_info_cache = None 
     563 
     564def _update_command_info(): 
     565    r""" 
     566    Read the file \file{qepcad.help} to find the list of commands 
     567    supported by QEPCAD.  Used for tab-completion and documentation. 
     568 
     569    EXAMPLES: 
     570        sage: from sage.interfaces.qepcad import _update_command_info, _command_info_cache 
     571        sage: _update_command_info() # optional 
     572        sage: _command_info_cache['approx_precision'] # optional 
     573        ('46', 'abcde', 'm', 'approx-precision N\n\nApproximate algeraic numbers to N decimal places.\n', None) 
     574    """ 
     575    global _command_info_cache 
     576    if _command_info_cache is not None: 
     577        return 
     578 
     579    cache = {} 
     580 
     581    with open('%s/bin/qepcad.help'%sage.misc.misc.SAGE_LOCAL) as help: 
     582        assert(help.readline().strip() == '@') 
     583 
     584        while True: 
     585            cmd_line = help.readline() 
     586            while len(cmd_line.strip()) == 0: 
     587                cmd_line = help.readline() 
     588            cmd_line = cmd_line.strip() 
     589            if cmd_line == '@@@': 
     590                break 
     591 
     592            (cmd, id, phases, kind) = cmd_line.split() 
     593            assert(help.readline().strip() == '@') 
     594 
     595            help_text = '' 
     596            help_line = help.readline() 
     597            while help_line.strip() != '@': 
     598                help_text += help_line 
     599                help_line = help.readline() 
     600 
     601            # I went through qepcad.help and picked out commands that 
     602            # I thought might be worth a little extra tweaking... 
     603            special = None 
     604             
     605            # These commands have been tweaked. 
     606            if cmd in ['d-all-cells-in-subtree', 'd-cell', 'd-pcad', 
     607                       'd-pscad', 'd-stack', 'manual-choose-cell']: 
     608                special = 'cell' 
     609            if cmd in ['ipfzt', 'rational-sample', 'triv-convert', 'use-db', 
     610                       'use-selected-cells-cond', 'verbose']: 
     611                special = 'yn' 
     612 
     613            # The tweaking for these commands has not been implemented yet. 
     614            if cmd in ['selected-cells-cond']: 
     615                special = 'formula' 
     616            if cmd in ['ch-pivot', 'rem-pf', 'rem-pj']: 
     617                special = 'i,j' 
     618            if cmd in ['d-2d-cad', 'set-truth-value', 'solution-extension']: 
     619                special = 'interactive' 
     620            if cmd in ['p-2d-cad', 'trace-alg', 'trace-data']: 
     621                special = 'optional' 
     622 
     623            cmd = cmd.replace('-', '_') 
     624 
     625            cache[cmd] = (id, phases, kind, help_text, special) 
     626 
     627    _command_info_cache = cache 
     628             
     629_rewrote_qepcadrc = False 
     630def _rewrite_qepcadrc(): 
     631    r""" 
     632    Write the file \file{default.qepcadrc} to specify a path to Singular 
     633    (which QEPCAD uses for Gr\"obner basis computations). 
     634 
     635    EXAMPLES: 
     636        sage: from sage.interfaces.qepcad import _rewrite_qepcadrc 
     637        sage: _rewrite_qepcadrc() 
     638        sage: from sage.misc.misc import SAGE_LOCAL 
     639        sage: open('%s/default.qepcadrc'%SAGE_LOCAL).readlines()[-1] 
     640        'SINGULAR .../local//bin' 
     641    """ 
     642    global _rewrote_qepcadrc 
     643    if _rewrote_qepcadrc: return 
     644 
     645    SL = sage.misc.misc.SAGE_LOCAL 
     646    fn = '%s/default.qepcadrc'%SL 
     647    text = \ 
     648"""# THIS FILE IS AUTOMATICALLY GENERATED -- DO NOT EDIT 
     649 
     650##################################################### 
     651# QEPCAD rc file. 
     652# This file allows for some customization of QEPCADB. 
     653# Right now, the ability to give a path to Singular, 
     654# so that it gets used for some computer algebra 
     655# computations is the only feature. 
     656##################################################### 
     657SINGULAR %s/bin""" % SL 
     658 
     659    open(fn, 'w').write(text) 
     660 
     661# QEPCAD does not have a typical "computer algebra system" interaction 
     662# model.  Instead, you run QEPCAD once for each problem you wish to solve, 
     663# then interact with it while you solve that problem. 
     664 
     665# For this reason, most of the methods on the Expect class are 
     666# inapplicable.  To avoid confusing the user with useless commands 
     667# in tab completion, we split the control into two classes:  
     668# Qepcad_expect is a subclass of Expect, and is never seen by the user; 
     669# Qepcad is a wrapper for Qepcad_expect, and is what the user interacts with. 
     670 
     671class Qepcad_expect(Expect): 
     672    r""" 
     673    The low-level wrapper for QEPCAD. 
     674    """ 
     675    def __init__(self, memcells=None, 
     676                 maxread=100000, 
     677                 logfile=None, 
     678                 server=None): 
     679        r""" 
     680        Initialize a low-level wrapper for QEPCAD.  You can specify 
     681        the number of memory cells that QEPCAD allocates on startup 
     682        (which controls the maximum problem size QEPCAD can handle), 
     683        and specify a logfile.  You can also specify a server, and the 
     684        interface will run QEPCAD on that server, using ssh.  (UNTESTED) 
     685 
     686        EXAMPLES: 
     687            sage: from sage.interfaces.qepcad import Qepcad_expect 
     688            sage: Qepcad_expect(memcells=100000, logfile=sys.stdout) 
     689            Qepcad 
     690        """ 
     691        _rewrite_qepcadrc() 
     692        Expect.__init__(self, 
     693                        name="QEPCAD", 
     694                        # yuck: when QEPCAD first starts, 
     695                        # it doesn't give prompts 
     696                        prompt="\nEnter an .*:\r", 
     697                        command=_qepcad_cmd(memcells), 
     698                        maxread=maxread, 
     699                        server=server, 
     700                        restart_on_ctrlc=False, 
     701                        verbose_start=False, 
     702                        logfile=logfile) 
     703 
     704class Qepcad: 
     705    r""" 
     706    The wrapper for QEPCAD. 
     707    """ 
     708 
     709    def __init__(self, formula, 
     710                 vars=None, logfile=None, verbose=False, 
     711                 memcells=None, server=None): 
     712        r""" 
     713        Construct a QEPCAD wrapper object.  Requires a formula, which 
     714        may be a \class{qformula} as returned by the methods of 
     715        \code{qepcad_formula}, a symbolic equality or inequality, a 
     716        polynomial $p$ (meaning $p = 0$), or a string, which is passed 
     717        straight to QEPCAD. 
     718 
     719        \var{vars} specifies the variables to use; this gives the variable 
     720        ordering, which may be very important.  If \var{formula} is 
     721        given as a string, then \var{vars} is required; otherwise, 
     722        if \var{vars} is omitted, then a default ordering is used 
     723        (alphabetical ordering for the free variables). 
     724 
     725        A logfile can be specified with \var{logfile}. 
     726        If \code{verbose=True} is given, then the logfile is automatically 
     727        set to \code{sys.stdout}, so all QEPCAD interaction is echoed to 
     728        the terminal. 
     729 
     730        You can set the amount of memory that QEPCAD allocates with 
     731        \var{memcells}, and you can use \var{server} to run QEPCAD on 
     732        another machine using ssh.  (UNTESTED) 
     733         
     734        Usually you will not call this directly, but use \function{qepcad} 
     735        to do so.  Check the \function{qepcad} documentation for more 
     736        information. 
     737 
     738        EXAMPLES: 
     739            sage: from sage.interfaces.qepcad import Qepcad 
     740            sage: Qepcad(x^2 - 1 == 0) # optional 
     741            QEPCAD object in phase 'Before Normalization' 
     742        """ 
     743        self._cell_cache = {} 
     744 
     745        if verbose: 
     746            logfile=sys.stdout 
     747 
     748        varlist = None 
     749        if vars is not None: 
     750            if isinstance(vars, str): 
     751                varlist = vars.strip('()').split(',') 
     752            else: 
     753                varlist = [str(v) for v in vars] 
     754 
     755        if isinstance(formula, str): 
     756            if varlist is None: 
     757                raise ValueError, "vars must be specified if formula is a string" 
     758             
     759            free_vars = len(varlist) - formula.count('(') 
     760        else: 
     761            formula = qepcad_formula.formula(formula) 
     762            fvars = formula.vars 
     763            fqvars = formula.qvars 
     764            if varlist is None: 
     765                varlist = sorted(fvars) + fqvars 
     766            else: 
     767                # Do some error-checking here.  Parse the given vars 
     768                # and ensure they match up with the variables in the formula. 
     769                if frozenset(varlist) != (fvars | frozenset(fqvars)): 
     770                    raise ValueError, "specified vars don't match vars in formula" 
     771                if len(fqvars) and varlist[-len(fqvars):] != fqvars: 
     772                    raise ValueError, "specified vars don't match quantified vars" 
     773            free_vars = len(fvars) 
     774            formula = repr(formula) 
     775 
     776        self._varlist = varlist 
     777        self._free_vars = free_vars 
     778 
     779        varlist = [v.replace('_', '') for v in varlist] 
     780        if len(frozenset(varlist)) != len(varlist): 
     781            raise ValueError, "variables collide after stripping underscores" 
     782        formula = formula.replace('_', '') 
     783 
     784        qex = Qepcad_expect(logfile=logfile) 
     785        qex._send('[ input from Sage ]') 
     786        qex._send('(' + ','.join(varlist) + ')') 
     787        qex._send(str(free_vars)) 
     788        # I hope this prompt is distinctive enough... 
     789        # if not, we could list all the cases separately 
     790        qex._change_prompt(['\r\n([^\r\n]*) >\r\n', pexpect.EOF]) 
     791        qex.eval(formula + '.') 
     792        self._qex = qex 
     793 
     794    def __repr__(self): 
     795        r""" 
     796        Return a string representation of this \class{Qepcad} object. 
     797 
     798        EXAMPLES: 
     799            sage: qepcad(x - 1 == 0, interact=True) # optional 
     800            QEPCAD object in phase 'Before Normalization' 
     801        """ 
     802        return "QEPCAD object in phase '%s'"%self.phase() 
     803 
     804    def assume(self, assume): 
     805        r""" 
     806        The following documentation is from \file{qepcad.help}: 
     807 
     808        Add an assumption to the problem.  These will not be 
     809        included in the solution formula. 
     810 
     811        For example, with input  (E x)[ a x^2 + b x + c = 0], 
     812        if we issue the command 
     813 
     814            assume [ a /= 0 ] 
     815 
     816        we'll get the solution formula b^2 - 4 a c >= 0.  Without 
     817        the assumption we'd get something like [a = 0 /\ b /= 0] \/  
     818        [a /= 0 /\ 4 a c - b^2 <= 0] \/ [a = 0 /\ b = 0 /\ c = 0]. 
     819 
     820        EXAMPLES: 
     821            sage: var('a,b,c,x') 
     822            (a, b, c, x) 
     823            sage: qf = qepcad_formula 
     824            sage: qe = qepcad(qf.exists(x, a*x^2 + b*x + c == 0), interact=True) # optional 
     825            sage: qe.assume(a != 0) # optional 
     826            sage: qe.finish() # optional 
     827            4 a c - b^2 <= 0 
     828        """ 
     829        if not isinstance(assume, str): 
     830            assume = qepcad_formula.formula(assume) 
     831            if len(assume.qvars): 
     832                raise ValueError, "assumptions cannot be quantified" 
     833            if not assume.vars.issubset(frozenset(self._varlist[:self._free_vars])): 
     834                raise ValueError, "assumption contains variables not present in formula" 
     835            assume = repr(assume) 
     836        assume = assume.replace('_', '') 
     837        result = self._eval_line("assume [%s]" % assume) 
     838        if len(result): 
     839            return AsciiArtString(result) 
     840 
     841    def solution_extension(self, kind): 
     842        r""" 
     843        The following documentation is modified from \file{qepcad.help}: 
     844 
     845        solution-extension x 
     846  
     847        Use an alternative solution formula construction method.  The 
     848        parameter x is allowed to be T,E, or G.  If x is T, then a 
     849        formula in the usual language of Tarski formulas is produced. 
     850        If x is E, a formula in the language of Extended Tarski formulas 
     851        is produced.  If x is G, then a geometry-based formula is 
     852        produced. 
     853 
     854        EXAMPLES: 
     855            sage: var('x,y') 
     856            (x, y) 
     857            sage: qf = qepcad_formula 
     858            sage: qe = qepcad(qf.and_(x^2 + y^2 - 3 == 0, x + y > 0), interact=True) # optional 
     859            sage: qe.go(); qe.go(); qe.go() # optional 
     860            QEPCAD object has moved to phase 'Before Projection (y)' 
     861            QEPCAD object has moved to phase 'Before Choice' 
     862            QEPCAD object has moved to phase 'Before Solution' 
     863            sage: qe.solution_extension('E') # optional 
     864            x > _root_1 2 x^2 - 3 /\ y^2 + x^2 - 3 = 0 /\ [ 2 x^2 - 3 > 0 \/ y = _root_-1 y^2 + x^2 - 3 ] 
     865            sage: qe.solution_extension('G') # optional 
     866            [ 
     867              [ 
     868                2 x^2 - 3 < 0 
     869                \/ 
     870                x = _root_-1 2 x^2 - 3 
     871              ] 
     872              /\ 
     873              y = _root_-1 y^2 + x^2 - 3 
     874            ] 
     875            \/ 
     876            [ 
     877              x^2 - 3 <= 0 
     878              /\ 
     879              x > _root_-1 2 x^2 - 3 
     880              /\ 
     881              y^2 + x^2 - 3 = 0 
     882            ]             
     883            sage: qe.solution_extension('T') # optional 
     884            y + x > 0 /\ y^2 + x^2 - 3 = 0 
     885        """ 
     886        if kind == 'I': 
     887            raise ValueError, "Interactive solution construction not handled by Sage interface" 
     888        result = self._eval_line('solution-extension %s'%kind) 
     889        tagline = 'An equivalent quantifier-free formula:' 
     890        loc = result.find(tagline) 
     891        if loc >= 0: 
     892            result = result[loc + len(tagline):] 
     893        result = result.strip() 
     894        if len(result): 
     895            return AsciiArtString(result) 
     896 
     897    def set_truth_value(self, index, nv): 
     898        r""" 
     899        Given a cell index (or a cell) and an integer, set the truth value 
     900        of the cell to that integer.  Valid integers are 0 (false),  
     901        1 (true), and 2 (undetermined). 
     902 
     903        EXAMPLES: 
     904            sage: qe = qepcad(x == 1, interact=True) # optional 
     905            sage: qe.go(); qe.go(); qe.go() # optional 
     906            QEPCAD object has moved to phase 'At the end of projection phase' 
     907            QEPCAD object has moved to phase 'Before Choice' 
     908            QEPCAD object has moved to phase 'Before Solution' 
     909            sage: qe.set_truth_value(1, 1) # optional 
     910        """ 
     911        index_str = _format_cell_index([index]) 
     912        self._eval_line('set-truth-value\n%s\n%s' % (index_str, nv)) 
     913 
     914    def phase(self): 
     915        r""" 
     916        Return the current phase of the QEPCAD program. 
     917 
     918        EXAMPLES: 
     919            sage: qe = qepcad(x > 2/3, interact=True) # optional 
     920            sage: qe.phase() # optional 
     921            'Before Normalization' 
     922            sage: qe.go() # optional 
     923            QEPCAD object has moved to phase 'At the end of projection phase' 
     924            sage: qe.phase() # optional 
     925            'At the end of projection phase' 
     926            sage: qe.go() # optional 
     927            QEPCAD object has moved to phase 'Before Choice' 
     928            sage: qe.phase() # optional 
     929            'Before Choice' 
     930            sage: qe.go() # optional 
     931            QEPCAD object has moved to phase 'Before Solution' 
     932            sage: qe.phase() # optional 
     933            'Before Solution' 
     934            sage: qe.go() # optional 
     935            3 x - 2 > 0 
     936            sage: qe.phase() # optional 
     937            'EXITED' 
     938        """ 
     939        match = self._qex.expect().match 
     940        if match == pexpect.EOF: 
     941            return 'EXITED' 
     942        else: 
     943            return match.group(1) 
     944 
     945    def _parse_answer_stats(self): 
     946        r""" 
     947        Parse the final string printed by QEPCAD, which should be 
     948        the simplified quantifier-free formula followed by some 
     949        statistics.  Return a pair of the formula and the statistics 
     950        (both as strings). 
     951 
     952        EXAMPLES: 
     953            sage: qe = qepcad(x^2 > 2, interact=True) # optional 
     954            sage: qe.finish() # optional 
     955            x^2 - 2 > 0 
     956            sage: (ans, stats) = qe._parse_answer_stats() # optional 
     957            sage: ans # optional 
     958            'x^2 - 2 > 0' 
     959            sage: stats # random, optional 
     960            '-----------------------------------------------------------------------------\r\n0 Garbage collections, 0 Cells and 0 Arrays reclaimed, in 0 milliseconds.\r\n492514 Cells in AVAIL, 500000 Cells in SPACE.\r\n\r\nSystem time: 16 milliseconds.\r\nSystem time after the initialization: 4 milliseconds.\r\n-----------------------------------------------------------------------------\r\n' 
     961        """ 
     962        if self.phase() != 'EXITED': 
     963            raise ValueError, "QEPCAD is not finished yet" 
     964        final = self._qex.expect().before 
     965        match = re.search('\nAn equivalent quantifier-free formula:(.*)\n=+  The End  =+\r\n\r\n(.*)$', final, re.DOTALL) 
     966 
     967        if match: 
     968            return (match.group(1).strip(), match.group(2)) 
     969        else: 
     970            return (final, '') 
     971 
     972    def answer(self): 
     973        r""" 
     974        For a QEPCAD instance which is finished, return the 
     975        simplified quantifier-free formula that it printed just before 
     976        exiting. 
     977 
     978        EXAMPLES: 
     979            sage: qe = qepcad(x^3 - x == 0, interact=True) # optional 
     980            sage: qe.finish() # optional 
     981            x - 1 <= 0 /\ x + 1 >= 0 /\ [ x = 0 \/ x - 1 = 0 \/ x + 1 = 0 ] 
     982            sage: qe.answer() # optional 
     983            x - 1 <= 0 /\ x + 1 >= 0 /\ [ x = 0 \/ x - 1 = 0 \/ x + 1 = 0 ] 
     984        """ 
     985        return AsciiArtString(self._parse_answer_stats()[0]) 
     986 
     987    def final_stats(self): 
     988        r""" 
     989        For a QEPCAD instance which is finished, return the 
     990        statistics that it printed just before exiting. 
     991 
     992        EXAMPLES: 
     993            sage: qe = qepcad(x == 0, interact=True) # optional 
     994            sage: qe.finish() # optional 
     995            x = 0 
     996            sage: qe.final_stats() # random, optional 
     997            ----------------------------------------------------------------------------- 
     998            0 Garbage collections, 0 Cells and 0 Arrays reclaimed, in 0 milliseconds. 
     999            492840 Cells in AVAIL, 500000 Cells in SPACE. 
     1000            System time: 8 milliseconds. 
     1001            System time after the initialization: 4 milliseconds. 
     1002            ----------------------------------------------------------------------------- 
     1003        """ 
     1004        return AsciiArtString(self._parse_answer_stats()[1]) 
     1005 
     1006    def trait_names(self): 
     1007        r""" 
     1008        Returns a list of the QEPCAD commands which are available as 
     1009        extra methods on a \class{Qepcad} object. 
     1010 
     1011        EXAMPLES: 
     1012            sage: qe = qepcad(x^2 < 0, interact=True) # optional 
     1013            sage: len(qe.trait_names()) # optional 
     1014            95 
     1015            sage: 'd_cell' in qe.trait_names() # optional 
     1016            True 
     1017        """ 
     1018        _update_command_info() 
     1019        return _command_info_cache.keys() 
     1020 
     1021    def cell(self, *index): 
     1022        r""" 
     1023        Given a cell index, returns a \class{QepcadCell} wrapper for that 
     1024        cell.  Uses a cache for efficiency. 
     1025 
     1026        EXAMPLES: 
     1027            sage: qe = qepcad(x + 3 == 42, interact=True) # optional 
     1028            sage: qe.go(); qe.go(); qe.go() # optional 
     1029            QEPCAD object has moved to phase 'At the end of projection phase' 
     1030            QEPCAD object has moved to phase 'Before Choice' 
     1031            QEPCAD object has moved to phase 'Before Solution' 
     1032            sage: qe.cell(2) # optional 
     1033