Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/interfaces/qepcad.py
8814 views
1
r"""
2
Interface to QEPCAD
3
4
The basic function of QEPCAD is to construct cylindrical algebraic
5
decompositions (CADs) of $\RR^k$, given a list of polynomials. Using
6
this CAD, it is possible to perform quantifier elimination and formula
7
simplification.
8
9
A CAD for a set $A$ of $k$-variate polynomials decomposes $\RR^j$ into
10
disjoint cells, for each $j$ in $0 \leq j \leq k$. The sign of each
11
polynomial in $A$ is constant in each cell of $\RR^k$, and for each
12
cell 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
14
decomposition ``cylindrical''.)
15
16
Given a formula $\exists x. P(a,b,x) = 0$ (for a polynomial $P$), and
17
a cylindrical algebraic decomposition for $P$, we can eliminate the
18
quantifier (find an equivalent formula in the two variables $a$, $b$
19
without the quantifier $\exists$) as follows. For each cell $C$ in
20
$\RR^2$, find the cells of $\RR^3$ which project to $C$. (This
21
collection is called the \emph{stack} over $C$.) Mark $C$ as true if
22
some member of the stack has sign $= 0$; otherwise, mark $C$ as false.
23
Then, construct a polynomial formula in $a$, $b$ which specifies
24
exactly the true cells (this is always possible). The same technique
25
works if the body of the quantifier is any boolean combination of
26
polynomial equalities and inequalities.
27
28
Formula simplification is a similar technique. Given a formula which
29
describes a simple set of $\RR^k$ in a complicated way as a boolean
30
combination of polynomial equalities and inequalities, QEPCAD can
31
construct a CAD for the polynomials and recover a simple equivalent
32
formula.
33
34
Note that while the following documentation is tutorial in nature, and
35
is written for people who may not be familiar with QEPCAD, it is
36
documentation for the \sage interface rather than for QEPCAD. As
37
such, it does not cover several issues that are very important to use
38
QEPCAD efficiently, such as variable ordering, the efficient use of
39
the alternate quantifiers and \code{_root_} expressions, the
40
\code{measure-zero-error} command, etc. For more information on
41
QEPCAD, see the online documentation at
42
\url{http://www.cs.usna.edu/~qepcad/B/QEPCAD.html} and Chris Brown's
43
tutorial 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
46
sources.)
47
48
The examples below require that the optional qepcad package is installed.
49
50
QEPCAD can be run in a fully automatic fashion, or interactively.
51
We first demonstrate the automatic use of QEPCAD.
52
53
Since \sage has no built-in support for quantifiers, this interface
54
provides \code{qepcad_formula} which helps construct quantified
55
formulas in the syntax QEPCAD requires. ::
56
57
sage: var('a,b,c,d,x,y,z')
58
(a, b, c, d, x, y, z)
59
sage: qf = qepcad_formula
60
61
We start with a simple example. Consider an arbitrarily-selected
62
ellipse::
63
64
sage: ellipse = 3*x^2 + 2*x*y + y^2 - x + y - 7
65
66
What is the projection onto the $x$ axis of this ellipse? First we
67
construct a formula asking this question. ::
68
69
sage: F = qf.exists(y, ellipse == 0); F
70
(E y)[3 x^2 + 2 x y + y^2 - x + y - 7 = 0]
71
72
Then we run qepcad to get the answer::
73
74
sage: qepcad(F) # optional - qepcad
75
8 x^2 - 8 x - 29 <= 0
76
77
How about the projection onto the $y$ axis? ::
78
79
sage: qepcad(qf.exists(x, ellipse == 0)) # optional - qepcad
80
8 y^2 + 16 y - 85 <= 0
81
82
QEPCAD deals with more quantifiers than just ``exists'', of course.
83
Besides the standard ``forall'', there are also ``for infinitely
84
many'', ``for all but finitely many'', ``for a connected subset'', and
85
``for exactly $k$''. The \function{qepcad} documentation has examples
86
of all of these; here we'll just give one example.
87
88
First we construct a circle::
89
90
sage: circle = x^2 + y^2 - 3
91
92
For what values $k$ does a vertical line $x=k$ intersect the combined
93
figure of the circle and ellipse exactly three times? ::
94
95
sage: F = qf.exactly_k(3, y, circle * ellipse == 0); F # optional - qepcad
96
(X3 y)[(x^2 + y^2 - 3) (3 x^2 + 2 x y + y^2 - x + y - 7) = 0]
97
sage: qepcad(F) # optional - qepcad
98
8 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
100
Here we see that the solutions are among the eight ($4 + 2 + 2$) roots
101
of the three polynomials inside the brackets, but not all of these
102
roots are solutions; the polynomial inequalities outside the brackets
103
are needed to select those roots that are solutions.
104
105
QEPCAD 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,
108
then _root_$1$ refers to the least root and _root_$n$ refers to the greatest;
109
also, _root_$-n$ refers to the least root and _root_$-1$ refers to the
110
greatest.
111
112
This extended language is available both on input and output; see the
113
QEPCAD documentation for more information on how to use this syntax on
114
input. We can request output that's intended to be easy to interpret
115
geometrically; then QEPCAD will use the extended language to produce
116
a solution formula without the selection polynomials. ::
117
118
sage: qepcad(F, solution='geometric') # optional - qepcad
119
x = _root_1 8 x^2 - 8 x - 29
120
\/
121
8 x^4 - 26 x^2 - 4 x + 13 = 0
122
\/
123
x = _root_-1 x^2 - 3
124
125
We then see that the 6 solutions correspond to the vertical tangent on
126
the left side of the ellipse, the four intersections between the
127
ellipse and the circle, and the vertical tangent on the right side of
128
the circle.
129
130
Let's do some basic formula simplification and visualization.
131
We'll look at the region which is inside both the ellipse and the circle::
132
133
sage: F = qf.and_(ellipse < 0, circle < 0); F # optional - qepcad
134
[3 x^2 + 2 x y + y^2 - x + y - 7 < 0 /\ x^2 + y^2 - 3 < 0]
135
sage: qepcad(F) # optional - qepcad
136
y^2 + 2 x y + y + 3 x^2 - x - 7 < 0 /\ y^2 + x^2 - 3 < 0
137
138
We get back the same formula we put in. This is not surprising (we
139
started with a pretty simple formula, after all), but it's not very
140
enlightening either. Again, if we ask for a 'geometric' output, then we see
141
an output that lets us understand something about the shape of the solution
142
set. ::
143
144
sage: qepcad(F, solution='geometric') # optional - qepcad
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
167
There's another reason to prefer output using _root_ expressions; not
168
only does it sometimes give added insight into the geometric
169
structure, it also can be more efficient to construct. Consider this
170
formula for the projection of a particular semicircle onto the $x$
171
axis::
172
173
sage: F = qf.exists(y, qf.and_(circle == 0, x + y > 0)); F # optional - qepcad
174
(E y)[x^2 + y^2 - 3 = 0 /\ x + y > 0]
175
sage: qepcad(F) # optional - qepcad
176
x^2 - 3 <= 0 /\ [ x > 0 \/ 2 x^2 - 3 < 0 ]
177
178
Here, the formula $x > 0$ had to be introduced in order to get a
179
solution formula; the original CAD of F did not include the
180
polynomial $x$. To avoid having QEPCAD do the extra work to come up
181
with a solution formula, we can tell it to use the extended language;
182
it's always possible to construct a solution formula in the extended
183
language without introducing new polynomials. ::
184
185
sage: qepcad(F, solution='extended') # optional - qepcad
186
x^2 - 3 <= 0 /\ x > _root_1 2 x^2 - 3
187
188
Up to this point, all the output we've seen has basically been in the
189
form of strings; there is no support (yet) for parsing these outputs
190
back into \sage polynomials (partly because \sage does not yet have
191
support for symbolic conjunctions and disjunctions). The function
192
\function{qepcad} supports three more output types that give numbers which
193
can be manipulated in \sage: any-point, all-points, and cell-points.
194
195
These output types give dictionaries mapping variable names to values.
196
With any-point, \function{qepcad} either produces a single dictionary
197
specifying a point where the formula is true, or raises an exception
198
if the formula is false everywhere. With all-points,
199
\function{qepcad} either produces a list of dictionaries for all
200
points where the formula is true, or raises an exception if the
201
formula is true on infinitely many points. With cell-points,
202
\function{qepcad} produces a list of dictionaries with one point for
203
each cell where the formula is true. (This means you will have at least
204
one point in each connected component of the solution, although you will
205
often have many more points than that.)
206
207
Let's revisit some of the above examples and get some points to play
208
with. We'll start by finding a point on our ellipse. ::
209
210
sage: p = qepcad(ellipse == 0, solution='any-point'); p # optional - qepcad
211
{'y': 0.9685019685029527?, 'x': -1.468501968502953?}
212
213
(Note that despite the decimal printing and the question marks, these
214
are really exact numbers.)
215
216
We can verify that this point is a solution. To do so, we create
217
a copy of ellipse as a polynomial over QQ (instead of a symbolic
218
expression). ::
219
220
sage: pellipse = QQ['x,y'](ellipse) # optional - qepcad
221
sage: pellipse(**p) == 0 # optional - qepcad
222
True
223
224
For cell-points, let's look at points \emph{not} on the ellipse. ::
225
226
sage: pts = qepcad(ellipse != 0, solution='cell-points'); pts # optional - qepcad
227
[{'y': 0, 'x': 4}, {'y': 1, 'x': 2.468501968502953?}, {'y': -9, 'x': 2.468501968502953?}, {'y': 9, 'x': 1/2}, {'y': -1, 'x': 1/2}, {'y': -5, 'x': 1/2}, {'y': 3, 'x': -1.468501968502953?}, {'y': -1, 'x': -1.468501968502953?}, {'y': 0, 'x': -3}]
228
229
For the points here which are in full-dimensional cells, QEPCAD has the
230
freedom to choose rational sample points, and it does so.
231
232
And, of course, all these points really are not on the ellipse. ::
233
234
sage: [pellipse(**p) != 0 for p in pts] # optional - qepcad
235
[True, True, True, True, True, True, True, True, True]
236
237
Finally, for all-points, let's look again at finding vertical lines that
238
intersect the union of the circle and the ellipse exactly three times. ::
239
240
sage: F = qf.exactly_k(3, y, circle * ellipse == 0); F # optional - qepcad
241
(X3 y)[(x^2 + y^2 - 3) (3 x^2 + 2 x y + y^2 - x + y - 7) = 0]
242
sage: pts = qepcad(F, solution='all-points'); pts # optional - qepcad
243
[{'x': 1.732050807568878?}, {'x': 1.731054913462534?}, {'x': 0.678911384208004?}, {'x': -0.9417727377417167?}, {'x': -1.468193559928821?}, {'x': -1.468501968502953?}]
244
245
Since $y$ is bound by the quantifier, the solutions only refer to $x$.
246
247
We can substitute one of these solutions into the original equation::
248
249
sage: pt = pts[0] # optional - qepcad
250
sage: pcombo = QQ['x,y'](circle * ellipse) # optional - qepcad
251
sage: intersections = pcombo(y=polygen(AA, 'y'), **pt); intersections # optional - qepcad
252
y^4 + 4.464101615137755?*y^3 + 0.2679491924311227?*y^2
253
254
and verify that it does have three roots::
255
256
sage: intersections.roots() # optional - qepcad
257
[(-4.403249005600958?, 1), (-0.06085260953679653?, 1), (0, 2)]
258
259
Let's check all six solutions. ::
260
261
sage: [len(pcombo(y=polygen(AA, 'y'), **p).roots()) for p in pts] # optional - qepcad
262
[3, 3, 3, 3, 3, 3]
263
264
We said earlier that we can run QEPCAD either automatically or
265
interactively. Now that we've discussed the automatic modes, let's turn
266
to interactive uses.
267
268
If the \function{qepcad} function is passed \code{interact=True}, then
269
instead of returning a result, it returns an object of class
270
\class{Qepcad} representing a running instance of QEPCAD that you can
271
interact with. For example::
272
273
sage: qe = qepcad(qf.forall(x, x^2 + b*x + c > 0), interact=True); qe # optional - qepcad
274
QEPCAD object in phase 'Before Normalization'
275
276
This object is a fairly thin wrapper over QEPCAD; most QEPCAD commands
277
are available as methods on the \class{Qepcad} object. Given a
278
\class{Qepcad} object \var{qe}, you can type \code{qe.[tab]} to see
279
the available QEPCAD commands; to see the documentation for an
280
individual QEPCAD command, for example \code{d_setting}, you can type
281
\code{qe.d_setting?}. (In QEPCAD, this command is called
282
\code{d-setting}; we systematically replace hyphens with underscores
283
for this interface.)
284
285
The execution of QEPCAD is divided into four phases; most commands are
286
not available during all phases. We saw above that QEPCAD starts out
287
in phase `Before Normalization'; we see that the \code{d_cell} command
288
is not available in this phase::
289
290
sage: qe.d_cell() # optional - qepcad
291
Error GETCID: This command is not active here.
292
293
We will focus here on the fourth (and last) phase, `Before Solution',
294
because this interface has special support for some operations in this
295
phase. Consult the QEPCAD documentation for information on the other
296
phases.
297
298
We can tell QEPCAD to finish off the current phase and move to the
299
next with its \code{go} command. (There is also the \code{step}
300
command, which partially completes a phase for phases that have
301
multiple steps, and the \code{finish} command, which runs QEPCAD to
302
completion.) ::
303
304
sage: qe.go() # optional - qepcad
305
QEPCAD object has moved to phase 'Before Projection (x)'
306
sage: qe.go() # optional - qepcad
307
QEPCAD object has moved to phase 'Before Choice'
308
sage: qe.go() # optional - qepcad
309
QEPCAD object has moved to phase 'Before Solution'
310
311
Note that the \class{Qepcad} object returns the new phase whenever the
312
phase changes, as a convenience for interactive use; except that when
313
the new phase is `EXITED', the solution formula printed by QEPCAD is
314
returned instead. ::
315
316
sage: qe.go() # optional - qepcad
317
4 c - b^2 > 0
318
sage: qe # optional - qepcad
319
QEPCAD object in phase 'EXITED'
320
321
Let's pick a nice, simple example, return to phase 4, and explore the
322
resulting \var{qe} object. ::
323
324
sage: qe = qepcad(circle == 0, interact=True); qe # optional - qepcad
325
QEPCAD object in phase 'Before Normalization'
326
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
327
QEPCAD object has moved to phase 'Before Projection (y)'
328
QEPCAD object has moved to phase 'Before Choice'
329
QEPCAD object has moved to phase 'Before Solution'
330
331
We said before that QEPCAD creates ``cylindrical algebraic
332
decompositions''; since we have a bivariate polynomial, we get
333
decompositions of $\RR^0$, $\RR^1$, and $\RR^2$. In this case, where
334
our example is a circle of radius $\sqrt{3}$ centered on the origin,
335
these decompositions are as follows:
336
337
The decomposition of $\RR^0$ is trivial (of course). The
338
decomposition of $\RR^1$ has five cells: $x < -\sqrt{3}$, $x =
339
-\sqrt{3}$, $-\sqrt{3} < x < \sqrt{3}$, $x = \sqrt{3}$, and $x >
340
\sqrt{3}$. These five cells comprise the \emph{stack} over the single
341
cell in the trivial decomposition of $\RR^0$.
342
343
These five cells give rise to five stacks in $\RR^2$. The first and
344
fifth stack have just one cell apiece. The second and fourth stacks
345
have three cells: $y < 0$, $y = 0$, and $y > 0$. The third stack has
346
five cells: below the circle, the lower semicircle, the interior of
347
the circle, the upper semicircle, and above the circle.
348
349
QEPCAD (and this QEPCAD interface) number the cells in a stack
350
starting with 1. Each cell has an \emph{index}, which is a tuple of
351
integers describing the path to the cell in the tree of all cells.
352
For example, the cell ``below the circle'' has index (3,1) (the first
353
cell in the stack over the third cell of $\RR^1$) and the interior of
354
the circle has index (3,3).
355
356
We can view these cells with the QEPCAD command \code{d_cell}. For
357
instance, let's look at the cell for the upper semicircle::
358
359
sage: qe.d_cell(3, 4) # optional - qepcad
360
---------- Information about the cell (3,4) ----------
361
Level : 2
362
Dimension : 1
363
Number of children : 0
364
Truth value : T by trial evaluation.
365
Degrees after substitution : Not known yet or No polynomial.
366
Multiplicities : ((1,1))
367
Signs of Projection Factors
368
Level 1 : (-)
369
Level 2 : (0)
370
---------- Sample point ----------
371
The sample point is in a PRIMITIVE representation.
372
<BLANKLINE>
373
alpha = the unique root of x^2 - 3 between 0 and 4
374
= 1.7320508076-
375
<BLANKLINE>
376
Coordinate 1 = 0
377
= 0.0000000000
378
Coordinate 2 = alpha
379
= 1.7320508076-
380
----------------------------------------------------
381
382
We see that, the level of this cell is 2, meaning that it is part of
383
the decomposition of $\RR^2$. The dimension is 1, meaning that the
384
cell is homeomorphic to a line (rather than a plane or a point). The
385
sample point gives the coordinates of one point in the cell, both
386
symbolically and numerically.
387
388
For programmatic access to cells, we've defined a \sage wrapper class
389
\class{QepcadCell}. These cells can be created with the \method{cell}
390
method; for example::
391
392
sage: c = qe.cell(3, 4); c # optional - qepcad
393
QEPCAD cell (3, 4)
394
395
A \class{QepcadCell} has accessor methods for the important state held
396
within a cell. For instance::
397
398
sage: c.level() # optional - qepcad
399
2
400
sage: c.index() # optional - qepcad
401
(3, 4)
402
sage: qe.cell(3).number_of_children() # optional - qepcad
403
5
404
sage: len(qe.cell(3)) # optional - qepcad
405
5
406
407
One particularly useful thing we can get from a cell is its sample point,
408
as \sage algebraic real numbers. ::
409
410
sage: c.sample_point() # optional - qepcad
411
(0, 1.732050807568878?)
412
sage: c.sample_point_dict() # optional - qepcad
413
{'y': 1.732050807568878?, 'x': 0}
414
415
We've seen that we can get cells using the \method{cell} method.
416
There are several QEPCAD commands that print lists of cells; we can
417
also get cells using the \method{make_cells} method, passing it the
418
output of one of these commands. ::
419
420
sage: qe.make_cells(qe.d_true_cells()) # optional - qepcad
421
[QEPCAD cell (4, 2), QEPCAD cell (3, 4), QEPCAD cell (3, 2), QEPCAD cell (2, 2)]
422
423
Also, the cells in the stack over a given cell can be accessed using
424
array subscripting or iteration. (Remember that cells in a stack are
425
numbered starting with one; we preserve this convention in the
426
array-subscripting syntax.) ::
427
428
sage: c = qe.cell(3) # optional - qepcad
429
sage: c[1] # optional - qepcad
430
QEPCAD cell (3, 1)
431
sage: [c2 for c2 in c] # optional - qepcad
432
[QEPCAD cell (3, 1), QEPCAD cell (3, 2), QEPCAD cell (3, 3), QEPCAD cell (3, 4), QEPCAD cell (3, 5)]
433
434
We can do one more thing with a cell: we can set its truth value.
435
Once the truth values of the cells have been set, we can get QEPCAD to
436
produce a formula which is true in exactly the cells we have selected.
437
This is useful if QEPCAD's quantifier language is insufficient to
438
express your problem.
439
440
For example, consider again our combined figure of the circle and the
441
ellipse. Suppose you want to find all vertical lines that intersect
442
the circle twice, and also intersect the ellipse twice. The vertical
443
lines that intersect the circle twice can be found by simplifying::
444
445
sage: F = qf.exactly_k(2, y, circle == 0); F # optional - qepcad
446
(X2 y)[x^2 + y^2 - 3 = 0]
447
448
and the vertical lines that intersect the ellipse twice are expressed by::
449
450
sage: G = qf.exactly_k(2, y, ellipse == 0); G # optional - qepcad
451
(X2 y)[3 x^2 + 2 x y + y^2 - x + y - 7 = 0]
452
453
and the lines that intersect both figures would be::
454
455
sage: qf.and_(F, G) # optional - qepcad
456
Traceback (most recent call last):
457
...
458
ValueError: QEPCAD formulas must be in prenex (quantifiers outermost) form
459
460
...except that QEPCAD does not support formulas like this; in QEPCAD
461
input, all logical connectives must be inside all quantifiers.
462
463
Instead, we can get QEPCAD to construct a CAD for our combined figure
464
and set the truth values ourselves. (The exact formula we use doesn't
465
matter, since we're going to replace the truth values in the cells; we
466
just need to use a formula that uses both polynomials.) ::
467
468
sage: qe = qepcad(qf.and_(ellipse == 0, circle == 0), interact=True) # optional - qepcad
469
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
470
QEPCAD object has moved to phase 'Before Projection (y)'
471
QEPCAD object has moved to phase 'Before Choice'
472
QEPCAD object has moved to phase 'Before Solution'
473
474
Now we want to find all cells $c$ in the decomposition of $\RR^1$ such
475
that the stack over $c$ contains exactly two cells on the ellipse, and
476
also contains exactly two cells on the circle.
477
478
Our input polynomials are ``level-2 projection factors'', we see::
479
480
sage: qe.d_proj_factors() # optional - qepcad
481
P_1,1 = fac(J_1,1) = fac(dis(A_2,1))
482
= 8 x^2 - 8 x - 29
483
P_1,2 = fac(J_1,2) = fac(dis(A_2,2))
484
= x^2 - 3
485
P_1,3 = fac(J_1,3) = fac(res(A_2,1|A_2,2))
486
= 8 x^4 - 26 x^2 - 4 x + 13
487
A_2,1 = input
488
= y^2 + 2 x y + y + 3 x^2 - x - 7
489
A_2,2 = input
490
= y^2 + x^2 - 3
491
492
so we can test whether a cell is on the ellipse by checking that the
493
sign of the corresponding projection factor is 0 in our cell.
494
For instance, the cell (12,2) is on the ellipse::
495
496
sage: qe.cell(12,2).signs()[1][0] # optional - qepcad
497
0
498
499
So we can update the truth values as desired like this::
500
501
sage: for c in qe.cell(): # optional - qepcad
502
... count_ellipse = 0 # optional - qepcad
503
... count_circle = 0 # optional - qepcad
504
... for c2 in c: # optional - qepcad
505
... count_ellipse += (c2.signs()[1][0] == 0) # optional - qepcad
506
... count_circle += (c2.signs()[1][1] == 0) # optional - qepcad
507
... c.set_truth(count_ellipse == 2 and count_circle == 2) # optional - qepcad
508
509
and then we can get our desired solution formula. (The 'G' stands for
510
'geometric', and gives solutions using the same rules as
511
\code{solution='geometric'} described above.) ::
512
513
sage: qe.solution_extension('G') # optional - qepcad
514
8 x^2 - 8 x - 29 < 0
515
/\
516
x^2 - 3 < 0
517
518
TESTS:
519
520
Check the qepcad configuration file::
521
522
sage: from sage.misc.misc import SAGE_LOCAL
523
sage: open('%s/default.qepcadrc'%SAGE_LOCAL).readlines()[-1]
524
'SINGULAR .../local/bin\n'
525
526
AUTHORS:
527
528
- Carl Witty (2008-03): initial version
529
"""
530
531
#*****************************************************************************
532
# Copyright (C) 2008 Carl Witty <[email protected]>
533
#
534
# Distributed under the terms of the GNU General Public License (GPL)
535
# as published by the Free Software Foundation; either version 2 of
536
# the License, or (at your option) any later version.
537
# http://www.gnu.org/licenses/
538
#*****************************************************************************
539
540
import sage.misc.misc
541
import pexpect
542
import re
543
import sys
544
545
from sage.misc.flatten import flatten
546
from sage.misc.sage_eval import sage_eval
547
from sage.misc.preparser import implicit_mul
548
549
from expect import Expect, ExpectFunction, AsciiArtString
550
551
def _qepcad_cmd(memcells=None):
552
r"""
553
Construct a QEPCAD command line. Optionally set the
554
number of memory cells to use.
555
556
EXAMPLES::
557
558
sage: from sage.interfaces.qepcad import _qepcad_cmd
559
sage: from sage.misc.misc import SAGE_LOCAL
560
sage: s = _qepcad_cmd()
561
sage: s == 'env qe=%s qepcad '%SAGE_LOCAL
562
True
563
sage: s = _qepcad_cmd(memcells=8000000)
564
sage: s == 'env qe=%s qepcad +N8000000'%SAGE_LOCAL
565
True
566
"""
567
if memcells is not None:
568
memcells_arg = '+N%s' % memcells
569
else:
570
memcells_arg = ''
571
return "env qe=%s qepcad %s"%(sage.misc.misc.SAGE_LOCAL, memcells_arg)
572
573
_command_info_cache = None
574
575
def _update_command_info():
576
r"""
577
Read the file \file{qepcad.help} to find the list of commands
578
supported by QEPCAD. Used for tab-completion and documentation.
579
580
EXAMPLES::
581
582
sage: from sage.interfaces.qepcad import _update_command_info, _command_info_cache
583
sage: _update_command_info() # optional - qepcad
584
sage: _command_info_cache['approx_precision'] # optional - qepcad
585
('46', 'abcde', 'm', 'approx-precision N\n\nApproximate algeraic numbers to N decimal places.\n', None)
586
"""
587
global _command_info_cache
588
if _command_info_cache is not None:
589
return
590
591
cache = {}
592
593
with open('%s/bin/qepcad.help'%sage.misc.misc.SAGE_LOCAL) as help:
594
assert(help.readline().strip() == '@')
595
596
while True:
597
cmd_line = help.readline()
598
while len(cmd_line.strip()) == 0:
599
cmd_line = help.readline()
600
cmd_line = cmd_line.strip()
601
if cmd_line == '@@@':
602
break
603
604
(cmd, id, phases, kind) = cmd_line.split()
605
assert(help.readline().strip() == '@')
606
607
help_text = ''
608
help_line = help.readline()
609
while help_line.strip() != '@':
610
help_text += help_line
611
help_line = help.readline()
612
613
# I went through qepcad.help and picked out commands that
614
# I thought might be worth a little extra tweaking...
615
special = None
616
617
# These commands have been tweaked.
618
if cmd in ['d-all-cells-in-subtree', 'd-cell', 'd-pcad',
619
'd-pscad', 'd-stack', 'manual-choose-cell']:
620
special = 'cell'
621
if cmd in ['ipfzt', 'rational-sample', 'triv-convert', 'use-db',
622
'use-selected-cells-cond', 'verbose']:
623
special = 'yn'
624
625
# The tweaking for these commands has not been implemented yet.
626
if cmd in ['selected-cells-cond']:
627
special = 'formula'
628
if cmd in ['ch-pivot', 'rem-pf', 'rem-pj']:
629
special = 'i,j'
630
if cmd in ['d-2d-cad', 'set-truth-value', 'solution-extension']:
631
special = 'interactive'
632
if cmd in ['p-2d-cad', 'trace-alg', 'trace-data']:
633
special = 'optional'
634
635
cmd = cmd.replace('-', '_')
636
637
cache[cmd] = (id, phases, kind, help_text, special)
638
639
_command_info_cache = cache
640
641
# QEPCAD does not have a typical "computer algebra system" interaction
642
# model. Instead, you run QEPCAD once for each problem you wish to solve,
643
# then interact with it while you solve that problem.
644
645
# For this reason, most of the methods on the Expect class are
646
# inapplicable. To avoid confusing the user with useless commands
647
# in tab completion, we split the control into two classes:
648
# Qepcad_expect is a subclass of Expect, and is never seen by the user;
649
# Qepcad is a wrapper for Qepcad_expect, and is what the user interacts with.
650
651
class Qepcad_expect(Expect):
652
r"""
653
The low-level wrapper for QEPCAD.
654
"""
655
def __init__(self, memcells=None,
656
maxread=100000,
657
logfile=None,
658
server=None):
659
r"""
660
Initialize a low-level wrapper for QEPCAD. You can specify
661
the number of memory cells that QEPCAD allocates on startup
662
(which controls the maximum problem size QEPCAD can handle),
663
and specify a logfile. You can also specify a server, and the
664
interface will run QEPCAD on that server, using ssh. (UNTESTED)
665
666
EXAMPLES::
667
668
sage: from sage.interfaces.qepcad import Qepcad_expect
669
sage: Qepcad_expect(memcells=100000, logfile=sys.stdout)
670
Qepcad
671
"""
672
Expect.__init__(self,
673
name="QEPCAD",
674
# yuck: when QEPCAD first starts,
675
# it doesn't give prompts
676
prompt="\nEnter an .*:\r",
677
command=_qepcad_cmd(memcells),
678
maxread=maxread,
679
server=server,
680
restart_on_ctrlc=False,
681
verbose_start=False,
682
logfile=logfile)
683
684
class Qepcad:
685
r"""
686
The wrapper for QEPCAD.
687
"""
688
689
def __init__(self, formula,
690
vars=None, logfile=None, verbose=False,
691
memcells=None, server=None):
692
r"""
693
Construct a QEPCAD wrapper object. Requires a formula, which
694
may be a \class{qformula} as returned by the methods of
695
\code{qepcad_formula}, a symbolic equality or inequality, a
696
polynomial $p$ (meaning $p = 0$), or a string, which is passed
697
straight to QEPCAD.
698
699
\var{vars} specifies the variables to use; this gives the variable
700
ordering, which may be very important. If \var{formula} is
701
given as a string, then \var{vars} is required; otherwise,
702
if \var{vars} is omitted, then a default ordering is used
703
(alphabetical ordering for the free variables).
704
705
A logfile can be specified with \var{logfile}.
706
If \code{verbose=True} is given, then the logfile is automatically
707
set to \code{sys.stdout}, so all QEPCAD interaction is echoed to
708
the terminal.
709
710
You can set the amount of memory that QEPCAD allocates with
711
\var{memcells}, and you can use \var{server} to run QEPCAD on
712
another machine using ssh. (UNTESTED)
713
714
Usually you will not call this directly, but use \function{qepcad}
715
to do so. Check the \function{qepcad} documentation for more
716
information.
717
718
EXAMPLES::
719
720
sage: from sage.interfaces.qepcad import Qepcad
721
sage: Qepcad(x^2 - 1 == 0) # optional - qepcad
722
QEPCAD object in phase 'Before Normalization'
723
"""
724
self._cell_cache = {}
725
726
if verbose:
727
logfile=sys.stdout
728
729
varlist = None
730
if vars is not None:
731
if isinstance(vars, str):
732
varlist = vars.strip('()').split(',')
733
else:
734
varlist = [str(v) for v in vars]
735
736
if isinstance(formula, str):
737
if varlist is None:
738
raise ValueError, "vars must be specified if formula is a string"
739
740
free_vars = len(varlist) - formula.count('(')
741
else:
742
formula = qepcad_formula.formula(formula)
743
fvars = formula.vars
744
fqvars = formula.qvars
745
if varlist is None:
746
varlist = sorted(fvars) + fqvars
747
else:
748
# Do some error-checking here. Parse the given vars
749
# and ensure they match up with the variables in the formula.
750
if frozenset(varlist) != (fvars | frozenset(fqvars)):
751
raise ValueError, "specified vars don't match vars in formula"
752
if len(fqvars) and varlist[-len(fqvars):] != fqvars:
753
raise ValueError, "specified vars don't match quantified vars"
754
free_vars = len(fvars)
755
formula = repr(formula)
756
757
self._varlist = varlist
758
self._free_vars = free_vars
759
760
varlist = [v.replace('_', '') for v in varlist]
761
if len(frozenset(varlist)) != len(varlist):
762
raise ValueError, "variables collide after stripping underscores"
763
formula = formula.replace('_', '')
764
765
qex = Qepcad_expect(logfile=logfile)
766
qex._send('[ input from Sage ]')
767
qex._send('(' + ','.join(varlist) + ')')
768
qex._send(str(free_vars))
769
# I hope this prompt is distinctive enough...
770
# if not, we could list all the cases separately
771
qex._change_prompt(['\r\n([^\r\n]*) >\r\n', pexpect.EOF])
772
qex.eval(formula + '.')
773
self._qex = qex
774
775
def __repr__(self):
776
r"""
777
Return a string representation of this \class{Qepcad} object.
778
779
EXAMPLES::
780
781
sage: qepcad(x - 1 == 0, interact=True) # optional - qepcad
782
QEPCAD object in phase 'Before Normalization'
783
"""
784
return "QEPCAD object in phase '%s'"%self.phase()
785
786
def assume(self, assume):
787
r"""
788
The following documentation is from \file{qepcad.help}:
789
790
Add an assumption to the problem. These will not be
791
included in the solution formula.
792
793
For example, with input (E x)[ a x^2 + b x + c = 0],
794
if we issue the command
795
796
assume [ a /= 0 ]
797
798
we'll get the solution formula b^2 - 4 a c >= 0. Without
799
the assumption we'd get something like [a = 0 /\ b /= 0] \/
800
[a /= 0 /\ 4 a c - b^2 <= 0] \/ [a = 0 /\ b = 0 /\ c = 0].
801
802
EXAMPLES::
803
804
sage: var('a,b,c,x')
805
(a, b, c, x)
806
sage: qf = qepcad_formula
807
sage: qe = qepcad(qf.exists(x, a*x^2 + b*x + c == 0), interact=True) # optional - qepcad
808
sage: qe.assume(a != 0) # optional - qepcad
809
sage: qe.finish() # optional - qepcad
810
4 a c - b^2 <= 0
811
"""
812
if not isinstance(assume, str):
813
assume = qepcad_formula.formula(assume)
814
if len(assume.qvars):
815
raise ValueError, "assumptions cannot be quantified"
816
if not assume.vars.issubset(frozenset(self._varlist[:self._free_vars])):
817
raise ValueError, "assumption contains variables not present in formula"
818
assume = repr(assume)
819
assume = assume.replace('_', '')
820
result = self._eval_line("assume [%s]" % assume)
821
if len(result):
822
return AsciiArtString(result)
823
824
def solution_extension(self, kind):
825
r"""
826
The following documentation is modified from \file{qepcad.help}:
827
828
solution-extension x
829
830
Use an alternative solution formula construction method. The
831
parameter x is allowed to be T,E, or G. If x is T, then a
832
formula in the usual language of Tarski formulas is produced.
833
If x is E, a formula in the language of Extended Tarski formulas
834
is produced. If x is G, then a geometry-based formula is
835
produced.
836
837
EXAMPLES::
838
839
sage: var('x,y')
840
(x, y)
841
sage: qf = qepcad_formula
842
sage: qe = qepcad(qf.and_(x^2 + y^2 - 3 == 0, x + y > 0), interact=True) # optional
843
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
844
QEPCAD object has moved to phase 'Before Projection (y)'
845
QEPCAD object has moved to phase 'Before Choice'
846
QEPCAD object has moved to phase 'Before Solution'
847
sage: qe.solution_extension('E') # optional - qepcad
848
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 ]
849
sage: qe.solution_extension('G') # optional - qepcad
850
[
851
[
852
2 x^2 - 3 < 0
853
\/
854
x = _root_-1 2 x^2 - 3
855
]
856
/\
857
y = _root_-1 y^2 + x^2 - 3
858
]
859
\/
860
[
861
x^2 - 3 <= 0
862
/\
863
x > _root_-1 2 x^2 - 3
864
/\
865
y^2 + x^2 - 3 = 0
866
]
867
sage: qe.solution_extension('T') # optional - qepcad
868
y + x > 0 /\ y^2 + x^2 - 3 = 0
869
"""
870
if kind == 'I':
871
raise ValueError, "Interactive solution construction not handled by Sage interface"
872
result = self._eval_line('solution-extension %s'%kind)
873
tagline = 'An equivalent quantifier-free formula:'
874
loc = result.find(tagline)
875
if loc >= 0:
876
result = result[loc + len(tagline):]
877
result = result.strip()
878
if len(result):
879
return AsciiArtString(result)
880
881
def set_truth_value(self, index, nv):
882
r"""
883
Given a cell index (or a cell) and an integer, set the truth value
884
of the cell to that integer. Valid integers are 0 (false),
885
1 (true), and 2 (undetermined).
886
887
EXAMPLES::
888
889
sage: qe = qepcad(x == 1, interact=True) # optional - qepcad
890
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
891
QEPCAD object has moved to phase 'At the end of projection phase'
892
QEPCAD object has moved to phase 'Before Choice'
893
QEPCAD object has moved to phase 'Before Solution'
894
sage: qe.set_truth_value(1, 1) # optional - qepcad
895
"""
896
index_str = _format_cell_index([index])
897
self._eval_line('set-truth-value\n%s\n%s' % (index_str, nv))
898
899
def phase(self):
900
r"""
901
Return the current phase of the QEPCAD program.
902
903
EXAMPLES::
904
905
sage: qe = qepcad(x > 2/3, interact=True) # optional - qepcad
906
sage: qe.phase() # optional - qepcad
907
'Before Normalization'
908
sage: qe.go() # optional
909
QEPCAD object has moved to phase 'At the end of projection phase'
910
sage: qe.phase() # optional - qepcad
911
'At the end of projection phase'
912
sage: qe.go() # optional
913
QEPCAD object has moved to phase 'Before Choice'
914
sage: qe.phase() # optional - qepcad
915
'Before Choice'
916
sage: qe.go() # optional
917
QEPCAD object has moved to phase 'Before Solution'
918
sage: qe.phase() # optional - qepcad
919
'Before Solution'
920
sage: qe.go() # optional - qepcad
921
3 x - 2 > 0
922
sage: qe.phase() # optional - qepcad
923
'EXITED'
924
"""
925
match = self._qex.expect().match
926
if match == pexpect.EOF:
927
return 'EXITED'
928
else:
929
return match.group(1)
930
931
def _parse_answer_stats(self):
932
r"""
933
Parse the final string printed by QEPCAD, which should be
934
the simplified quantifier-free formula followed by some
935
statistics. Return a pair of the formula and the statistics
936
(both as strings).
937
938
EXAMPLES::
939
940
sage: qe = qepcad(x^2 > 2, interact=True) # optional - qepcad
941
sage: qe.finish() # optional - qepcad
942
x^2 - 2 > 0
943
sage: (ans, stats) = qe._parse_answer_stats() # optional - qepcad
944
sage: ans # optional - qepcad
945
'x^2 - 2 > 0'
946
sage: stats # random, optional - qepcad
947
'-----------------------------------------------------------------------------\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'
948
"""
949
if self.phase() != 'EXITED':
950
raise ValueError, "QEPCAD is not finished yet"
951
final = self._qex.expect().before
952
match = re.search('\nAn equivalent quantifier-free formula:(.*)\n=+ The End =+\r\n\r\n(.*)$', final, re.DOTALL)
953
954
if match:
955
return (match.group(1).strip(), match.group(2))
956
else:
957
return (final, '')
958
959
def answer(self):
960
r"""
961
For a QEPCAD instance which is finished, return the
962
simplified quantifier-free formula that it printed just before
963
exiting.
964
965
EXAMPLES::
966
967
sage: qe = qepcad(x^3 - x == 0, interact=True) # optional - qepcad
968
sage: qe.finish() # optional - qepcad
969
x - 1 <= 0 /\ x + 1 >= 0 /\ [ x = 0 \/ x - 1 = 0 \/ x + 1 = 0 ]
970
sage: qe.answer() # optional - qepcad
971
x - 1 <= 0 /\ x + 1 >= 0 /\ [ x = 0 \/ x - 1 = 0 \/ x + 1 = 0 ]
972
"""
973
return AsciiArtString(self._parse_answer_stats()[0])
974
975
def final_stats(self):
976
r"""
977
For a QEPCAD instance which is finished, return the
978
statistics that it printed just before exiting.
979
980
EXAMPLES::
981
982
sage: qe = qepcad(x == 0, interact=True) # optional
983
sage: qe.finish() # optional
984
x = 0
985
sage: qe.final_stats() # random, optional
986
-----------------------------------------------------------------------------
987
0 Garbage collections, 0 Cells and 0 Arrays reclaimed, in 0 milliseconds.
988
492840 Cells in AVAIL, 500000 Cells in SPACE.
989
System time: 8 milliseconds.
990
System time after the initialization: 4 milliseconds.
991
-----------------------------------------------------------------------------
992
"""
993
return AsciiArtString(self._parse_answer_stats()[1])
994
995
def trait_names(self):
996
r"""
997
Returns a list of the QEPCAD commands which are available as
998
extra methods on a \class{Qepcad} object.
999
1000
EXAMPLES::
1001
1002
sage: qe = qepcad(x^2 < 0, interact=True) # optional - qepcad
1003
sage: len(qe.trait_names()) # random, optional - qepcad
1004
97
1005
sage: 'd_cell' in qe.trait_names() # optional - qepcad
1006
True
1007
"""
1008
_update_command_info()
1009
return _command_info_cache.keys()
1010
1011
def cell(self, *index):
1012
r"""
1013
Given a cell index, returns a \class{QepcadCell} wrapper for that
1014
cell. Uses a cache for efficiency.
1015
1016
EXAMPLES::
1017
1018
sage: qe = qepcad(x + 3 == 42, interact=True) # optional - qepcad
1019
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
1020
QEPCAD object has moved to phase 'At the end of projection phase'
1021
QEPCAD object has moved to phase 'Before Choice'
1022
QEPCAD object has moved to phase 'Before Solution'
1023
sage: qe.cell(2) # optional - qepcad
1024
QEPCAD cell (2)
1025
sage: qe.cell(2) is qe.cell(2) # optional - qepcad
1026
True
1027
"""
1028
index_str = _format_cell_index(index)
1029
if self._cell_cache.has_key(index_str):
1030
return self._cell_cache[index_str]
1031
else:
1032
c = self.make_cells(self.d_cell(index))[0]
1033
self._cell_cache[index_str] = c
1034
return c
1035
1036
def make_cells(self, text):
1037
r"""
1038
Given the result of some QEPCAD command that returns cells
1039
(such as \method{d_cell}, \method{d_witness_list}, etc.),
1040
return a list of cell objects.
1041
1042
EXAMPLES::
1043
1044
sage: var('x,y')
1045
(x, y)
1046
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
1047
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
1048
QEPCAD object has moved to phase 'Before Projection (y)'
1049
QEPCAD object has moved to phase 'Before Choice'
1050
QEPCAD object has moved to phase 'Before Solution'
1051
sage: qe.make_cells(qe.d_false_cells()) # optional - qepcad
1052
[QEPCAD cell (5, 1), QEPCAD cell (4, 3), QEPCAD cell (4, 1), QEPCAD cell (3, 5), QEPCAD cell (3, 3), QEPCAD cell (3, 1), QEPCAD cell (2, 3), QEPCAD cell (2, 1), QEPCAD cell (1, 1)]
1053
"""
1054
# get rid of AsciiArtString
1055
text = str(text)
1056
1057
lines = text.strip().splitlines()
1058
1059
cells = []
1060
cell_lines = []
1061
in_cell = False
1062
1063
for line in lines:
1064
if 'Information about the cell' in line:
1065
in_cell = True
1066
if in_cell: cell_lines.append(line)
1067
if line == '----------------------------------------------------':
1068
cells.append(QepcadCell(self, cell_lines))
1069
cell_lines = []
1070
in_cell = False
1071
1072
return cells
1073
1074
1075
def __getattr__(self, attrname):
1076
r"""
1077
Returns a \class{QepcadFunction} object for any QEPCAD command.
1078
1079
EXAMPLES::
1080
1081
sage: qe = qepcad(x^3 == 8, interact=True) # optional - qepcad
1082
sage: qe.d_cell # optional - qepcad
1083
d_cell
1084
"""
1085
if attrname[:1] == "_":
1086
raise AttributeError
1087
if not attrname in self.trait_names():
1088
raise AttributeError
1089
return QepcadFunction(self, attrname)
1090
1091
def _eval_line(self, cmd, restart_if_needed=False):
1092
r"""
1093
Send a command to QEPCAD, wait for a prompt, and return the
1094
text printed by QEPCAD before the prompt. Not intended for
1095
direct use.
1096
1097
EXAMPLES::
1098
1099
sage: qe = qepcad(x^2 == x^3, interact=True) # optional - qepcad
1100
sage: qe._eval_line('d-formula') # optional - qepcad
1101
'-x^3 + x^2 = 0'
1102
"""
1103
# Evidently when the QEPCAD command parser reads a command, it
1104
# parses from the beginning of a line until it has found a command,
1105
# ignoring newlines. It then throws away the rest of the line
1106
# after the end of the command. There is no command terminator;
1107
# each command does its own argument parsing and executes
1108
# as soon as it sees all the arguments.
1109
1110
# Thus, if the user gives a function call with a missing argument,
1111
# QEPCAD will wait forever for the missing argument to show up
1112
# (with no further prompting), and Sage will wait forever for
1113
# a prompt. Not good.
1114
1115
# To avoid this, we add a trailing "&" to each command.
1116
# If there is a missing argument, then the QEPCAD argument parsing
1117
# will give a syntax error on the "&", instead of waiting forever;
1118
# if there is no missing argument then QEPCAD will silently
1119
# ignore the "&". (Hopefully all QEPCAD commands will treat "&"
1120
# as a syntax error; as far as I know that's the case.)
1121
1122
# This trailing "&" also helps with another problem; sometimes
1123
# we get a copy of the command we send to QEPCAD echoed back as
1124
# the first line of the result. I'm guessing this is because
1125
# readline turns raw mode on when it's reading and off when it isn't;
1126
# if we manage to send a command with raw mode off, then it's
1127
# echoed twice (once by the normal UNIX tty handling and once by
1128
# readline). To work around this problem, we check for an "&"
1129
# in the first line of the input, and remove the text up to the "&"
1130
# if it is present. (Hopefully QEPCAD doesn't print "&" as part
1131
# of its normal operation.)
1132
1133
result = self._qex._eval_line(cmd + ' &')
1134
1135
nl = result.find('\n')
1136
if nl < 0: nl = len(result)
1137
1138
amp = result.find('&', 0, nl)
1139
if amp > 0: result = result[amp+1:]
1140
1141
result = result.strip()
1142
1143
return result
1144
1145
def _function_call(self, name, args):
1146
r"""
1147
Given a command name and a list of arguments, send the command
1148
to QEPCAD and return the result. Not intended for direct use.
1149
1150
EXAMPLES::
1151
1152
sage: qe = qepcad(x^2 - 1 == 0, interact=True) # optional - qepcad
1153
sage: qe.go(); qe.go() # optional - qepcad
1154
QEPCAD object has moved to phase 'At the end of projection phase'
1155
QEPCAD object has moved to phase 'Before Choice'
1156
sage: qe._function_call('d_level_factors', (1,)) # optional - qepcad
1157
A_1,1 = input
1158
= x + 1
1159
A_1,2 = input
1160
= x - 1
1161
"""
1162
name = name.replace('_', '-')
1163
args = map(str, args)
1164
pre_phase = self.phase()
1165
result = self._eval_line('%s %s'%(name, ' '.join(args)))
1166
post_phase = self.phase()
1167
if len(result) and post_phase != 'EXITED':
1168
return AsciiArtString(result)
1169
if pre_phase != post_phase:
1170
if post_phase == 'EXITED' and name != 'quit':
1171
return self.answer()
1172
return AsciiArtString("QEPCAD object has moved to phase '%s'"%post_phase)
1173
1174
def _format_cell_index(a):
1175
"""
1176
Given a tuple (or list, etc.) containing a QEPCAD cell index, return a
1177
string with a properly-formatted index. The input is flattened, so
1178
extra levels of brackets are ignored. Also, if the first item
1179
in the flattened list is a \class{QepcadCell} object, then its index
1180
is used in place of the object.
1181
1182
EXAMPLES::
1183
1184
sage: from sage.interfaces.qepcad import _format_cell_index
1185
1186
sage: qe = qepcad(x == 0, interact=True); qe.go(); qe.go(); qe.go() # optional - qepcad
1187
QEPCAD object has moved to phase 'At the end of projection phase'
1188
QEPCAD object has moved to phase 'Before Choice'
1189
QEPCAD object has moved to phase 'Before Solution'
1190
sage: _format_cell_index(qe.cell(1)) # optional - qepcad
1191
'(1)'
1192
sage: _format_cell_index((qe.cell(1), 2, 3)) # optional - qepcad
1193
'(1, 2, 3)'
1194
sage: _format_cell_index(())
1195
'()'
1196
sage: _format_cell_index(5)
1197
'(5)'
1198
"""
1199
a = flatten([a])
1200
if len(a) and isinstance(a[0], QepcadCell):
1201
a[0:1] = a[0].index()
1202
if len(a) == 1:
1203
return '(%s)' % a[0]
1204
else:
1205
return str(tuple(a))
1206
1207
class QepcadFunction(ExpectFunction):
1208
r"""
1209
A wrapper for a QEPCAD command.
1210
"""
1211
def _sage_doc_(self):
1212
r"""
1213
Returns the documentation for a QEPCAD command, from
1214
\file{qepcad.help}.
1215
1216
EXAMPLES::
1217
1218
sage: qe = qepcad(x == 17, interact=True) # optional - qepcad
1219
sage: cmd = qe.approx_precision # optional - qepcad
1220
sage: cmd._sage_doc_() # optional - qepcad
1221
'approx-precision N\n\nApproximate algeraic numbers to N decimal places.\n'
1222
"""
1223
_update_command_info()
1224
return _command_info_cache[self._name][3]
1225
1226
def __call__(self, *args):
1227
r"""
1228
Calls QEPCAD with the command this is a wrapper for.
1229
1230
For commands which take cell indexes, \function{_format_cell_index}
1231
is automatically called. For commands which take 'y' or 'n',
1232
booleans are also allowed. (These special commands were hand-selected
1233
after reading \file{qepcad.help}.)
1234
1235
EXAMPLES::
1236
1237
sage: qe = qepcad(x^2 < 1, interact=True) # optional - qepcad
1238
sage: cmd = qe.d_formula # optional - qepcad
1239
sage: cmd.__call__() # optional - qepcad
1240
x^2 - 1 < 0
1241
"""
1242
_update_command_info()
1243
info = _command_info_cache[self._name]
1244
special = info[4]
1245
# Tweak the argument processing for a few commands.
1246
args = list(args)
1247
1248
if special == 'cell':
1249
args = [_format_cell_index(args)]
1250
1251
if special == 'yn':
1252
if isinstance(args[0], bool):
1253
args[0] = 'y' if args[0] else 'n'
1254
1255
if special == 'interactive':
1256
raise ValueError, "Cannot call %s through Sage interface... interactive commands not handled"
1257
1258
return self._parent._function_call(self._name, args)
1259
1260
def qepcad(formula, assume=None, interact=False, solution=None, vars=None, **kwargs):
1261
r"""
1262
Quantifier elimination and formula simplification using QEPCAD B.
1263
1264
If \var{assume} is specified, then the given formula is ``assumed'',
1265
which is taken into account during final solution formula construction.
1266
1267
If \code{interact=True} is given, then a \class{Qepcad} object is
1268
returned which can be interacted with either at the command line
1269
or programmatically.
1270
1271
The type of solution returned can be adjusted with \var{solution}.
1272
The options are \code{'geometric'}, which tries to construct a
1273
solution formula with geometric meaning; \code{'extended'}, which
1274
gives a solution formula in an extended language that may be more
1275
efficient to construct; \code{'any-point'}, which returns any
1276
point where the formula is true; \code{'all-points'}, which
1277
returns a list of all points where the formula is true (or raises
1278
an exception if there are infinitely many); and \code{'cell-points'},
1279
which returns one point in each cell where the formula is true.
1280
1281
All other keyword arguments are passed through to the \class{Qepcad}
1282
constructor.
1283
1284
For much more documentation and many more examples, see the module
1285
docstring for this module (type \code{sage.interfaces.qepcad?} to
1286
read this docstring from the \sage command line).
1287
1288
The examples below require that the optional qepcad package is installed.
1289
1290
EXAMPLES::
1291
1292
sage: qf = qepcad_formula
1293
1294
sage: var('a,b,c,d,x,y,z,long_with_underscore_314159')
1295
(a, b, c, d, x, y, z, long_with_underscore_314159)
1296
sage: K.<q,r> = QQ[]
1297
1298
sage: qepcad('(E x)[a x + b > 0]', vars='(a,b,x)') # optional - qepcad
1299
a /= 0 \/ b > 0
1300
1301
sage: qepcad(a > b) # optional - qepcad
1302
b - a < 0
1303
1304
sage: qepcad(qf.exists(x, a*x^2 + b*x + c == 0)) # optional - qepcad
1305
4 a c - b^2 <= 0 /\ [ c = 0 \/ a /= 0 \/ 4 a c - b^2 < 0 ]
1306
1307
sage: qepcad(qf.exists(x, a*x^2 + b*x + c == 0), assume=(a != 0)) # optional - qepcad
1308
4 a c - b^2 <= 0
1309
1310
For which values of $a$, $b$, $c$ does $a x^2 + b x + c$ have
1311
2 real zeroes? ::
1312
1313
sage: exact2 = qepcad(qf.exactly_k(2, x, a*x^2 + b*x + c == 0)); exact2 # optional - qepcad
1314
a /= 0 /\ 4 a c - b^2 < 0
1315
1316
one real zero? ::
1317
1318
sage: exact1 = qepcad(qf.exactly_k(1, x, a*x^2 + b*x + c == 0)); exact1 # optional - qepcad
1319
[ a > 0 /\ 4 a c - b^2 = 0 ] \/ [ a < 0 /\ 4 a c - b^2 = 0 ] \/ [ a = 0 /\ 4 a c - b^2 < 0 ]
1320
1321
No real zeroes? ::
1322
1323
sage: exact0 = qepcad(qf.forall(x, a*x^2 + b*x + c != 0)); exact0 # optional - qepcad
1324
4 a c - b^2 >= 0 /\ c /= 0 /\ [ b = 0 \/ 4 a c - b^2 > 0 ]
1325
1326
$3^{75}$ real zeroes? ::
1327
1328
sage: qepcad(qf.exactly_k(3^75, x, a*x^2 + b*x + c == 0)) # optional - qepcad
1329
FALSE
1330
1331
We can check that the results don't overlap::
1332
1333
sage: qepcad(r'[[%s] /\ [%s]]' % (exact0, exact1), vars='a,b,c') # optional - qepcad
1334
FALSE
1335
sage: qepcad(r'[[%s] /\ [%s]]' % (exact0, exact2), vars='a,b,c') # optional - qepcad
1336
FALSE
1337
sage: qepcad(r'[[%s] /\ [%s]]' % (exact1, exact2), vars='a,b,c') # optional - qepcad
1338
FALSE
1339
1340
and that the union of the results is as expected::
1341
1342
sage: qepcad(r'[[%s] \/ [%s] \/ [%s]]' % (exact0, exact1, exact2), vars=(a,b,c)) # optional - qepcad
1343
b /= 0 \/ a /= 0 \/ c /= 0
1344
1345
So we have finitely many zeroes if $a$, $b$, or $c$ is nonzero;
1346
which means we should have infinitely many zeroes if they are all
1347
zero. ::
1348
1349
sage: qepcad(qf.infinitely_many(x, a*x^2 + b*x + c == 0)) # optional - qepcad
1350
a = 0 /\ b = 0 /\ c = 0
1351
1352
The polynomial is nonzero almost everywhere iff it is not
1353
identically zero. ::
1354
1355
sage: qepcad(qf.all_but_finitely_many(x, a*x^2 + b*x + c != 0)) # optional - qepcad
1356
b /= 0 \/ a /= 0 \/ c /= 0
1357
1358
The non-zeroes are continuous iff there are no zeroes or if
1359
the polynomial is zero. ::
1360
1361
sage: qepcad(qf.connected_subset(x, a*x^2 + b*x + c != 0)) # optional - qepcad
1362
4 a c - b^2 >= 0 /\ [ a = 0 \/ 4 a c - b^2 > 0 ]
1363
1364
The zeroes are continuous iff there are no or one zeroes, or if the
1365
polynomial is zero::
1366
1367
sage: qepcad(qf.connected_subset(x, a*x^2 + b*x + c == 0)) # optional - qepcad
1368
a = 0 \/ 4 a c - b^2 >= 0
1369
sage: qepcad(r'[[%s] \/ [%s] \/ [a = 0 /\ b = 0 /\ c = 0]]' % (exact0, exact1), vars='a,b,c') # optional - qepcad
1370
a = 0 \/ 4 a c - b^2 >= 0
1371
1372
Since polynomials are continuous and $y > 0$ is an open set,
1373
they are positive infinitely often iff they are positive at
1374
least once. ::
1375
1376
sage: qepcad(qf.infinitely_many(x, a*x^2 + b*x + c > 0)) # optional - qepcad
1377
c > 0 \/ a > 0 \/ 4 a c - b^2 < 0
1378
sage: qepcad(qf.exists(x, a*x^2 + b*x + c > 0)) # optional - qepcad
1379
c > 0 \/ a > 0 \/ 4 a c - b^2 < 0
1380
1381
However, since $y >= 0$ is not open, the equivalence does not
1382
hold if you replace ``positive'' with ``nonnegative''.
1383
(We assume $a \neq 0$ to get simpler formulas.) ::
1384
1385
sage: qepcad(qf.infinitely_many(x, a*x^2 + b*x + c >= 0), assume=(a != 0)) # optional - qepcad
1386
a > 0 \/ 4 a c - b^2 < 0
1387
sage: qepcad(qf.exists(x, a*x^2 + b*x + c >= 0), assume=(a != 0)) # optional - qepcad
1388
a > 0 \/ 4 a c - b^2 <= 0
1389
1390
TESTS:
1391
1392
We verify that long variable names work. (Note that QEPCAD
1393
does not support underscores, so they are stripped from the formula.) ::
1394
1395
sage: qepcad(qf.exists(a, a*long_with_underscore_314159 == 1)) # optional - qepcad
1396
longwithunderscore314159 /= 0
1397
"""
1398
use_witness = False
1399
if solution == 'any-point':
1400
formula = qepcad_formula.formula(formula)
1401
if len(formula.qvars) == 0:
1402
if vars is None:
1403
vars = sorted(list(formula.vars))
1404
formula = qepcad_formula.exists(vars, formula)
1405
vars = None
1406
use_witness = True
1407
1408
qe = Qepcad(formula, vars=vars, **kwargs)
1409
if assume is not None:
1410
qe.assume(assume)
1411
if interact:
1412
if solution is not None:
1413
print "WARNING: 'solution=' is ignored for interactive use"
1414
return qe
1415
else:
1416
qe.go()
1417
qe.go()
1418
qe.go()
1419
if solution is None:
1420
qe.finish()
1421
return qe.answer()
1422
elif solution == 'geometric':
1423
s = qe.solution_extension('G')
1424
qe.quit()
1425
return s
1426
elif solution == 'extended':
1427
s = qe.solution_extension('E')
1428
qe.quit()
1429
return s
1430
elif solution == 'any-point':
1431
if use_witness:
1432
cells = qe.make_cells(qe.d_witness_list())
1433
else:
1434
cells = qe.make_cells(qe.d_true_cells())
1435
qe.quit()
1436
if len(cells) == 0:
1437
raise ValueError, "input formula is false everywhere"
1438
return cells[0].sample_point_dict()
1439
elif solution == 'cell-points':
1440
cells = qe.make_cells(qe.d_true_cells())
1441
qe.quit()
1442
return [c.sample_point_dict() for c in cells]
1443
elif solution == 'all-points':
1444
cells = qe.make_cells(qe.d_true_cells())
1445
qe.quit()
1446
for c in cells:
1447
if c._dimension > 0:
1448
raise ValueError, "input formula is true for infinitely many points"
1449
return [c.sample_point_dict() for c in cells]
1450
else:
1451
raise ValueError, "Unknown solution type (%s)" % solution
1452
1453
1454
import os
1455
def qepcad_console(memcells=None):
1456
r"""
1457
Run QEPCAD directly. To exit early, press Control-C.
1458
1459
EXAMPLES::
1460
1461
sage: qepcad_console() # not tested
1462
...
1463
Enter an informal description between '[' and ']':
1464
"""
1465
# This will only spawn local processes
1466
os.system(_qepcad_cmd(memcells))
1467
1468
1469
def qepcad_banner():
1470
"""
1471
Return the QEPCAD startup banner.
1472
1473
EXAMPLES::
1474
1475
sage: from sage.interfaces.qepcad import qepcad_banner
1476
sage: qepcad_banner() # optional - qepcad
1477
=======================================================
1478
Quantifier Elimination
1479
in
1480
Elementary Algebra and Geometry
1481
by
1482
Partial Cylindrical Algebraic Decomposition
1483
...
1484
by
1485
Hoon Hong
1486
([email protected])
1487
With contributions by: Christopher W. Brown, George E.
1488
Collins, Mark J. Encarnacion, Jeremy R. Johnson
1489
Werner Krandick, Richard Liska, Scott McCallum,
1490
Nicolas Robidoux, and Stanly Steinberg
1491
=======================================================
1492
"""
1493
qex = Qepcad_expect()
1494
qex._start()
1495
banner = qex.expect().before
1496
qex.quit(timeout=0)
1497
return AsciiArtString(banner)
1498
1499
def qepcad_version():
1500
"""
1501
Return a string containing the current QEPCAD version number.
1502
1503
EXAMPLES::
1504
1505
sage: qepcad_version() # random, optional - qepcad
1506
'Version B 1.48, 25 Oct 2007'
1507
1508
TESTS::
1509
1510
sage: qepcad_version() # optional - qepcad
1511
'Version B ..., ...'
1512
"""
1513
banner = str(qepcad_banner())
1514
lines = banner.split('\n')
1515
for line in lines:
1516
if 'Version' in line:
1517
return line.strip()
1518
1519
class qformula:
1520
"""
1521
A qformula holds a string describing a formula in QEPCAD's syntax,
1522
and a set of variables used.
1523
"""
1524
def __init__(self, formula, vars, qvars=[]):
1525
r"""
1526
Construct a qformula from a string, a frozenset of variable names,
1527
and (optionally) a list of ordered quantified names.
1528
1529
EXAMPLES::
1530
1531
sage: from sage.interfaces.qepcad import qformula
1532
sage: f = qformula('x + y = 0', frozenset(['x','y']))
1533
sage: f
1534
x + y = 0
1535
sage: f.formula
1536
'x + y = 0'
1537
sage: f.vars
1538
frozenset(['y', 'x'])
1539
sage: f.qvars
1540
[]
1541
"""
1542
self.formula = formula
1543
self.vars = vars
1544
self.qvars = qvars
1545
1546
def __repr__(self):
1547
r"""
1548
Returns a string representation of a qformula (this is just
1549
the formula it holds).
1550
1551
EXAMPLES::
1552
1553
sage: from sage.interfaces.qepcad import qformula
1554
sage: f = qformula('x + y = 0', frozenset(['x','y']))
1555
sage: f.__repr__()
1556
'x + y = 0'
1557
"""
1558
return self.formula
1559
1560
class qepcad_formula_factory:
1561
r"""
1562
Contains routines to help construct formulas in QEPCAD syntax.
1563
"""
1564
1565
def _normalize_op(self, op):
1566
r"""
1567
Given a relational operator (either a string, or a function from
1568
the \module{operator} module) return the corresponding QEPCAD
1569
operator.
1570
1571
EXAMPLES::
1572
1573
sage: qf = qepcad_formula
1574
sage: qf._normalize_op(operator.ne)
1575
'/='
1576
sage: qf._normalize_op('==')
1577
'='
1578
"""
1579
import operator
1580
if op == operator.eq: return '='
1581
if op == operator.ne: return '/='
1582
if op == operator.lt: return '<'
1583
if op == operator.gt: return '>'
1584
if op == operator.le: return '<='
1585
if op == operator.ge: return '>='
1586
1587
if op == '==': return '='
1588
if op == '!=': return '/='
1589
1590
return op
1591
1592
def _varset(self, p):
1593
r"""
1594
Given a polynomial or a symbolic expression, return a frozenset
1595
of the variables involved.
1596
1597
EXAMPLES::
1598
1599
sage: qf = qepcad_formula
1600
sage: var('x,y')
1601
(x, y)
1602
sage: K.<p,q> = QQ[]
1603
sage: qf._varset(x)
1604
frozenset(['x'])
1605
sage: qf._varset(x*y)
1606
frozenset(['y', 'x'])
1607
sage: qf._varset(q)
1608
frozenset(['q'])
1609
sage: qf._varset(p*q)
1610
frozenset(['q', 'p'])
1611
"""
1612
try:
1613
vars = p.variables()
1614
except AttributeError:
1615
vars = []
1616
return frozenset([str(v) for v in vars])
1617
1618
def _combine_formulas(self, formulas):
1619
r"""
1620
Given a list of formulas, convert them to strings and return
1621
the free variables found in the formulas.
1622
1623
Since this is used to build boolean expressions for QEPCAD,
1624
and QEPCAD boolean expressions cannot be built from quantified
1625
formulas, an exception is raised if an input formula is quantified.
1626
1627
INPUT:
1628
1629
- ``formulas`` -- a list of unquantified formulas
1630
1631
OUTPUT:
1632
1633
form_strs -- a list of formulas as strings
1634
vars -- a frozenset of all variables in the formulas
1635
1636
EXAMPLES::
1637
1638
sage: var('x,y')
1639
(x, y)
1640
sage: qf = qepcad_formula
1641
sage: qf._combine_formulas([x^2 == 0, y < 17])
1642
(['x^2 = 0', 'y < 17'], frozenset(['y', 'x']))
1643
"""
1644
formulas = map(self.atomic, formulas)
1645
formulas = map(self.atomic, formulas)
1646
formula_strs = map(repr, formulas)
1647
vars = frozenset()
1648
for f in formulas:
1649
vars = vars | f.vars
1650
if len(f.qvars):
1651
raise ValueError, "QEPCAD formulas must be in prenex (quantifiers outermost) form"
1652
return formula_strs, vars
1653
1654
def atomic(self, lhs, op='=', rhs=0):
1655
r"""
1656
Constructs a QEPCAD formula from the given inputs.
1657
1658
INPUT:
1659
1660
- ``lhs`` -- a polynomial, or a symbolic equality or inequality
1661
- ``op`` -- a relational operator, default '='
1662
- ``rhs`` -- a polynomial, default 0
1663
1664
If \var{lhs} is a symbolic equality or inequality, then \var{op}
1665
and \var{rhs} are ignored.
1666
1667
This method works by printing the given polynomials, so we don't
1668
care what ring they are in (as long as they print with integral
1669
or rational coefficients).
1670
1671
EXAMPLES::
1672
1673
sage: qf = qepcad_formula
1674
sage: var('a,b,c')
1675
(a, b, c)
1676
sage: K.<x,y> = QQ[]
1677
sage: def test_qf(qf):
1678
... return qf, qf.vars
1679
sage: test_qf(qf.atomic(a^2 + 17))
1680
(a^2 + 17 = 0, frozenset(['a']))
1681
sage: test_qf(qf.atomic(a*b*c <= c^3))
1682
(a b c <= c^3, frozenset(['a', 'c', 'b']))
1683
sage: test_qf(qf.atomic(x+y^2, '!=', a+b))
1684
(y^2 + x /= a + b, frozenset(['y', 'x', 'b', 'a']))
1685
sage: test_qf(qf.atomic(x, operator.lt))
1686
(x < 0, frozenset(['x']))
1687
"""
1688
if isinstance(lhs, qformula):
1689
return lhs
1690
1691
from sage.symbolic.expression import is_SymbolicEquation
1692
if is_SymbolicEquation(lhs):
1693
rhs = lhs.rhs()
1694
op = lhs.operator()
1695
lhs = lhs.lhs()
1696
1697
op = self._normalize_op(op)
1698
1699
formula = ('%r %s %r' % (lhs, op, rhs))
1700
formula = formula.replace('*', ' ')
1701
vars = self._varset(lhs) | self._varset(rhs)
1702
1703
return qformula(formula, vars)
1704
1705
def formula(self, formula):
1706
r"""
1707
Constructs a QEPCAD formula from the given input.
1708
1709
INPUTS:
1710
1711
- ``formula`` -- a polynomial, a symbolic equality or inequality,
1712
or a list of polynomials, equalities, or inequalities
1713
1714
A polynomial $p$ is interpreted as the equation $p = 0$.
1715
A list is interpreted as the conjunction (``and'') of the elements.
1716
1717
EXAMPLES::
1718
1719
sage: var('a,b,c,x')
1720
(a, b, c, x)
1721
sage: qf = qepcad_formula
1722
sage: qf.formula(a*x + b)
1723
a x + b = 0
1724
sage: qf.formula((a*x^2 + b*x + c, a != 0))
1725
[a x^2 + b x + c = 0 /\ a /= 0]
1726
"""
1727
if isinstance(formula, (list, tuple)):
1728
return self.and_(formula)
1729
else:
1730
return self.atomic(formula)
1731
1732
def and_(self, *formulas):
1733
r"""
1734
Returns the conjunction of its input formulas. (This method would
1735
be named ``and'' if that weren't a Python keyword.)
1736
1737
Each input formula may be a \class{qformula} as returned by the
1738
methods of \code{qepcad_formula}, a symbolic equality or
1739
inequality, or a polynomial $p$ (meaning $p = 0$).
1740
1741
EXAMPLES::
1742
1743
sage: var('a,b,c,x')
1744
(a, b, c, x)
1745
sage: qf = qepcad_formula
1746
sage: qf.and_(a*b, a*c, b*c != 0)
1747
[a b = 0 /\ a c = 0 /\ b c /= 0]
1748
sage: qf.and_(a*x^2 == 3, qf.or_(a > b, b > c))
1749
[a x^2 = 3 /\ [a > b \/ b > c]]
1750
"""
1751
formulas = flatten(formulas)
1752
formula_strs, vars = self._combine_formulas(formulas)
1753
formula = '[' + r' /\ '.join(formula_strs) + ']'
1754
return qformula(formula, vars)
1755
1756
def or_(self, *formulas):
1757
r"""
1758
Returns the disjunction of its input formulas. (This method would
1759
be named ``or'' if that weren't a Python keyword.)
1760
1761
Each input formula may be a \class{qformula} as returned by the
1762
methods of \code{qepcad_formula}, a symbolic equality or
1763
inequality, or a polynomial $p$ (meaning $p = 0$).
1764
1765
EXAMPLES::
1766
1767
sage: var('a,b,c,x')
1768
(a, b, c, x)
1769
sage: qf = qepcad_formula
1770
sage: qf.or_(a*b, a*c, b*c != 0)
1771
[a b = 0 \/ a c = 0 \/ b c /= 0]
1772
sage: qf.or_(a*x^2 == 3, qf.and_(a > b, b > c))
1773
[a x^2 = 3 \/ [a > b /\ b > c]]
1774
"""
1775
formulas = flatten(formulas)
1776
formula_strs, vars = self._combine_formulas(formulas)
1777
formula = '[' + r' \/ '.join(formula_strs) + ']'
1778
return qformula(formula, vars)
1779
1780
def not_(self, formula):
1781
r"""
1782
Returns the negation of its input formula. (This method would be
1783
named ``not'' if that weren't a Python keyword.)
1784
1785
The input formula may be a \class{qformula} as returned by the
1786
methods of \code{qepcad_formula}, a symbolic equality or
1787
inequality, or a polynomial $p$ (meaning $p = 0$).
1788
1789
EXAMPLES::
1790
1791
sage: var('a,b')
1792
(a, b)
1793
sage: qf = qepcad_formula
1794
sage: qf.not_(a > b)
1795
[~a > b]
1796
sage: qf.not_(a^2 + b^2)
1797
[~a^2 + b^2 = 0]
1798
sage: qf.not_(qf.and_(a > 0, b < 0))
1799
[~[a > 0 /\ b < 0]]
1800
"""
1801
(formula_str,), vars = self._combine_formulas([formula])
1802
f = '[~' + formula_str + ']'
1803
return qformula(f, vars)
1804
1805
def implies(self, f1, f2):
1806
r"""
1807
Returns the implication of its input formulas (that is, given
1808
formulas $P$ and $Q$, returns ``$P$ implies $Q$'').
1809
1810
The input formulas may be a \class{qformula} as returned by the
1811
methods of \code{qepcad_formula}, a symbolic equality or
1812
inequality, or a polynomial $p$ (meaning $p = 0$).
1813
1814
EXAMPLES::
1815
1816
sage: var('a,b')
1817
(a, b)
1818
sage: qf = qepcad_formula
1819
sage: qf.implies(a, b)
1820
[a = 0 ==> b = 0]
1821
sage: qf.implies(a^2 < b, b^2 < a)
1822
[a^2 < b ==> b^2 < a]
1823
"""
1824
(f1s, f2s), vars = self._combine_formulas([f1, f2])
1825
f = '[' + f1s + ' ==> ' + f2s + ']'
1826
return qformula(f, vars)
1827
1828
# QEPCAD also has a "reverse implication" symbol "<==", which
1829
# I'm not bothering to support.
1830
1831
def iff(self, f1, f2):
1832
r"""
1833
Returns the equivalence of its input formulas (that is, given
1834
formulas $P$ and $Q$, returns ``$P$ iff $Q$'').
1835
1836
The input formulas may be a \class{qformula} as returned by the
1837
methods of \code{qepcad_formula}, a symbolic equality or
1838
inequality, or a polynomial $p$ (meaning $p = 0$).
1839
1840
EXAMPLES::
1841
1842
sage: var('a,b')
1843
(a, b)
1844
sage: qf = qepcad_formula
1845
sage: qf.iff(a, b)
1846
[a = 0 <==> b = 0]
1847
sage: qf.iff(a^2 < b, b^2 < a)
1848
[a^2 < b <==> b^2 < a]
1849
"""
1850
(f1s, f2s), vars = self._combine_formulas([f1, f2])
1851
f = '[' + f1s + ' <==> ' + f2s + ']'
1852
return qformula(f, vars)
1853
1854
def exists(self, v, formula):
1855
r"""
1856
Given a variable (or list of variables) and a formula, returns
1857
the existential quantification of the formula over the variables.
1858
1859
This method is available both as \method{exists} and \method{E}
1860
(the QEPCAD name for this quantifier).
1861
1862
The input formula may be a \class{qformula} as returned by the
1863
methods of \code{qepcad_formula}, a symbolic equality or
1864
inequality, or a polynomial $p$ (meaning $p = 0$).
1865
1866
EXAMPLE::
1867
1868
sage: var('a,b')
1869
(a, b)
1870
sage: qf = qepcad_formula
1871
sage: qf.exists(a, a^2 + b > b^2 + a)
1872
(E a)[a^2 + b > b^2 + a]
1873
sage: qf.exists((a, b), a^2 + b^2 < 0)
1874
(E a)(E b)[a^2 + b^2 < 0]
1875
sage: qf.E(b, b^2 == a)
1876
(E b)[b^2 = a]
1877
"""
1878
return self.quantifier('E', v, formula)
1879
E = exists
1880
1881
def forall(self, v, formula):
1882
r"""
1883
Given a variable (or list of variables) and a formula, returns
1884
the universal quantification of the formula over the variables.
1885
1886
This method is available both as \method{forall} and \method{A}
1887
(the QEPCAD name for this quantifier).
1888
1889
The input formula may be a \class{qformula} as returned by the
1890
methods of \code{qepcad_formula}, a symbolic equality or
1891
inequality, or a polynomial $p$ (meaning $p = 0$).
1892
1893
EXAMPLE::
1894
1895
sage: var('a,b')
1896
(a, b)
1897
sage: qf = qepcad_formula
1898
sage: qf.forall(a, a^2 + b > b^2 + a)
1899
(A a)[a^2 + b > b^2 + a]
1900
sage: qf.forall((a, b), a^2 + b^2 > 0)
1901
(A a)(A b)[a^2 + b^2 > 0]
1902
sage: qf.A(b, b^2 != a)
1903
(A b)[b^2 /= a]
1904
"""
1905
return self.quantifier('A', v, formula)
1906
A = forall
1907
1908
def infinitely_many(self, v, formula):
1909
r"""
1910
Given a variable and a formula, returns a new formula which is
1911
true iff the original formula was true for infinitely many
1912
values of the variable.
1913
1914
This method is available both as \method{infinitely_many}
1915
and \method{F} (the QEPCAD name for this quantifier).
1916
1917
The input formula may be a \class{qformula} as returned by the
1918
methods of \code{qepcad_formula}, a symbolic equality or
1919
inequality, or a polynomial $p$ (meaning $p = 0$).
1920
1921
EXAMPLE::
1922
1923
sage: var('a,b')
1924
(a, b)
1925
sage: qf = qepcad_formula
1926
sage: qf.infinitely_many(a, a^2 + b > b^2 + a)
1927
(F a)[a^2 + b > b^2 + a]
1928
sage: qf.F(b, b^2 != a)
1929
(F b)[b^2 /= a]
1930
"""
1931
return self.quantifier('F', v, formula, allow_multi=False)
1932
F = infinitely_many
1933
1934
def all_but_finitely_many(self, v, formula):
1935
r"""
1936
Given a variable and a formula, returns a new formula which is
1937
true iff the original formula was true for all but finitely many
1938
values of the variable.
1939
1940
This method is available both as \method{all_but_finitely_many}
1941
and \method{G} (the QEPCAD name for this quantifier).
1942
1943
The input formula may be a \class{qformula} as returned by the
1944
methods of \code{qepcad_formula}, a symbolic equality or
1945
inequality, or a polynomial $p$ (meaning $p = 0$).
1946
1947
EXAMPLE::
1948
1949
sage: var('a,b')
1950
(a, b)
1951
sage: qf = qepcad_formula
1952
sage: qf.all_but_finitely_many(a, a^2 + b > b^2 + a)
1953
(G a)[a^2 + b > b^2 + a]
1954
sage: qf.G(b, b^2 != a)
1955
(G b)[b^2 /= a]
1956
"""
1957
return self.quantifier('G', v, formula, allow_multi=False)
1958
G = all_but_finitely_many
1959
1960
def connected_subset(self, v, formula, allow_multi=False):
1961
r"""
1962
Given a variable and a formula, returns a new formula which is
1963
true iff the set of values for the variable at which the
1964
original formula was true is connected (including cases where
1965
this set is empty or is a single point).
1966
1967
This method is available both as \method{connected_subset}
1968
and \method{C} (the QEPCAD name for this quantifier).
1969
1970
The input formula may be a \class{qformula} as returned by the
1971
methods of \code{qepcad_formula}, a symbolic equality or
1972
inequality, or a polynomial $p$ (meaning $p = 0$).
1973
1974
EXAMPLE::
1975
1976
sage: var('a,b')
1977
(a, b)
1978
sage: qf = qepcad_formula
1979
sage: qf.connected_subset(a, a^2 + b > b^2 + a)
1980
(C a)[a^2 + b > b^2 + a]
1981
sage: qf.C(b, b^2 != a)
1982
(C b)[b^2 /= a]
1983
"""
1984
return self.quantifier('C', v, formula)
1985
C = connected_subset
1986
1987
def exactly_k(self, k, v, formula, allow_multi=False):
1988
r"""
1989
Given a nonnegative integer $k$, a variable, and a formula,
1990
returns a new formula which is true iff the original formula
1991
is true for exactly $k$ values of the variable.
1992
1993
This method is available both as \method{exactly_k}
1994
and \method{X} (the QEPCAD name for this quantifier).
1995
1996
(Note that QEPCAD does not support $k=0$ with this syntax, so if
1997
$k=0$ is requested we implement it with \method{forall} and
1998
\method{not_}.)
1999
2000
The input formula may be a \class{qformula} as returned by the
2001
methods of \code{qepcad_formula}, a symbolic equality or
2002
inequality, or a polynomial $p$ (meaning $p = 0$).
2003
2004
EXAMPLE::
2005
2006
sage: var('a,b')
2007
(a, b)
2008
sage: qf = qepcad_formula
2009
sage: qf.exactly_k(1, x, x^2 + a*x + b == 0)
2010
(X1 x)[a x + x^2 + b = 0]
2011
sage: qf.exactly_k(0, b, a*b == 1)
2012
(A b)[~a b = 1]
2013
"""
2014
from sage.all import ZZ
2015
k = ZZ(k)
2016
if k < 0:
2017
raise ValueError, "negative k in exactly_k quantifier"
2018
2019
if k == 0:
2020
return self.forall(v, self.not_(formula))
2021
2022
return self.quantifier('X%s' % k, v, formula)
2023
X = exactly_k
2024
2025
def quantifier(self, kind, v, formula, allow_multi=True):
2026
r"""
2027
A helper method for building quantified QEPCAD formulas; not
2028
expected to be called directly.
2029
2030
Takes the quantifier kind (the string label of this quantifier),
2031
a variable or list of variables, and a formula, and returns
2032
the quantified formula.
2033
2034
EXAMPLES::
2035
2036
sage: var('a,b')
2037
(a, b)
2038
sage: qf = qepcad_formula
2039
sage: qf.quantifier('NOT_A_REAL_QEPCAD_QUANTIFIER', a, a*b==0)
2040
(NOT_A_REAL_QEPCAD_QUANTIFIER a)[a b = 0]
2041
sage: qf.quantifier('FOO', (a, b), a*b)
2042
(FOO a)(FOO b)[a b = 0]
2043
"""
2044
2045
formula = self.formula(formula)
2046
2047
if allow_multi and isinstance(v, (list, tuple)):
2048
if len(v) == 0:
2049
return formula
2050
else:
2051
return self.quantifier(kind, v[0],
2052
self.quantifier(kind, v[1:], formula))
2053
2054
form_str = str(formula)
2055
if form_str[-1] != ']':
2056
form_str = '[' + form_str + ']'
2057
v = str(v)
2058
if not (v in formula.vars):
2059
raise ValueError, "Attempting to quantify variable which does not occur in formula"
2060
form_str = "(%s %s)%s" % (kind, v, form_str)
2061
return qformula(form_str, formula.vars - frozenset([v]), [v] + formula.qvars)
2062
2063
2064
qepcad_formula = qepcad_formula_factory()
2065
2066
_qepcad_algebraic_re = \
2067
re.compile(' ?the unique root of (.*) between (.*) and (.*)$')
2068
2069
def _eval_qepcad_algebraic(text):
2070
r"""
2071
Given a string of the form:
2072
'the unique root of 8 x^2 - 8 x - 29 between -47/32 and -1503/1024'
2073
(as produced by QEPCAD) this returns the corresponding \sage
2074
algebraic real number.
2075
2076
Requires that the given rational bounds are exactly representable as
2077
arbitrary-precision floating-point numbers (that is, that the
2078
denominators are powers of two); this is true of the expressions
2079
given by QEPCAD.
2080
2081
EXAMPLES::
2082
2083
sage: from sage.interfaces.qepcad import _eval_qepcad_algebraic
2084
sage: x = _eval_qepcad_algebraic('the unique root of 8 x^2 - 8 x - 29 between -47/32 and -1503/1024'); x
2085
-1.468501968502953?
2086
sage: 8*x^2 - 8*x - 29 == 0
2087
True
2088
"""
2089
from sage.rings.rational_field import QQ
2090
from sage.rings.polynomial.polynomial_ring import polygen
2091
from sage.rings.real_mpfi import RealIntervalField
2092
from sage.rings.qqbar import AA
2093
2094
match = _qepcad_algebraic_re.match(text)
2095
2096
p_text = match.group(1)
2097
lbound = QQ(match.group(2))
2098
ubound = QQ(match.group(3))
2099
2100
p = sage_eval(implicit_mul(p_text), {'x': polygen(QQ)})
2101
2102
for prec_scale in range(15):
2103
fld = RealIntervalField(53 << prec_scale)
2104
intv = fld(lbound, ubound)
2105
if intv.lower().exact_rational() == lbound and intv.upper().exact_rational() == ubound:
2106
return AA.polynomial_root(p, intv)
2107
2108
raise ValueError, "%s or %s not an exact floating-point number"%(lbound, ubound)
2109
2110
class QepcadCell:
2111
r"""
2112
A wrapper for a QEPCAD cell.
2113
"""
2114
def __init__(self, parent, lines):
2115
r"""
2116
Constructs a \class{QepcadCell} wrapper for a QEPCAD cell, given
2117
a \class{Qepcad} object and a list of lines holding QEPCAD's
2118
cell output. This is typically called by the \method{make_cells}
2119
method of \class{Qepcad}.
2120
2121
EXAMPLES::
2122
2123
sage: from sage.interfaces.qepcad import QepcadCell
2124
sage: var('a,b')
2125
(a, b)
2126
sage: qe = qepcad(a^2 + b^2 == 5, interact=True) # optional - qepcad
2127
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2128
QEPCAD object has moved to phase 'Before Projection (b)'
2129
QEPCAD object has moved to phase 'Before Choice'
2130
QEPCAD object has moved to phase 'Before Solution'
2131
sage: qe.d_cell(4, 3) # optional - qepcad
2132
---------- Information about the cell (4,3) ----------
2133
Level : 2
2134
Dimension : 1
2135
Number of children : 0
2136
Truth value : F by trial evaluation.
2137
Degrees after substitution : Not known yet or No polynomial.
2138
Multiplicities : ()
2139
Signs of Projection Factors
2140
Level 1 : (0)
2141
Level 2 : (+)
2142
---------- Sample point ----------
2143
The sample point is in a PRIMITIVE representation.
2144
<BLANKLINE>
2145
alpha = the unique root of x^2 - 5 between 2289/1024 and 1145/512
2146
= 2.2360679775-
2147
<BLANKLINE>
2148
Coordinate 1 = alpha
2149
= 2.2360679775-
2150
Coordinate 2 = 1
2151
= 1.0000000000
2152
----------------------------------------------------
2153
2154
sage: QepcadCell(qe, str(qe.d_cell(4, 3)).splitlines()) # optional - qepcad
2155
QEPCAD cell (4, 3)
2156
"""
2157
self._parent = parent
2158
self._lines = lines
2159
2160
max_level = len(parent._varlist)
2161
2162
all_signs = []
2163
saw_signs = False
2164
2165
saw_primitive = False
2166
saw_extended = False
2167
2168
grab_extended = False
2169
2170
all_coordinates = []
2171
2172
for line in lines:
2173
if 'Information about the cell' in line:
2174
tail = line.split('(')[1]
2175
index = tail.split(')')[0]
2176
if index == '':
2177
index = ()
2178
else:
2179
index = sage_eval(index)
2180
if not isinstance(index, tuple):
2181
index = (index,)
2182
self._index = index
2183
2184
self._dimension = sum([r&1 for r in index])
2185
if 'Level ' in line:
2186
self._level = int(line.split(':')[1].strip())
2187
if 'Number of children' in line:
2188
nkids = int(line.split(':')[1].strip())
2189
if nkids > 0 or self._level == max_level:
2190
self._number_of_children = nkids
2191
else:
2192
self._number_of_children = None
2193
if 'Truth value' in line:
2194
pass # might change
2195
if 'Degrees after substitution' in line:
2196
if self._level == max_level or self._level == 0:
2197
self._degrees = None
2198
else:
2199
self._degrees = sage_eval(line.split(':')[1].strip())
2200
if 'Multiplicities' in line:
2201
self._multiplicities = sage_eval(line.split(':')[1].strip())
2202
if 'Signs of Projection Factors' in line:
2203
saw_signs = True
2204
if saw_signs and 'Level' in line:
2205
(lev, n, colon, signs) = line.split()
2206
assert(lev == 'Level' and colon == ':')
2207
assert(int(n) == len(all_signs) + 1)
2208
signs = signs.replace('+','1').replace('-','-1').replace(')',',)')
2209
all_signs.append(sage_eval(signs))
2210
if 'PRIMITIVE' in line:
2211
saw_primitive = True
2212
if 'EXTENDED' in line:
2213
saw_extended = True
2214
2215
if 'alpha = ' in line:
2216
alpha_text = line[8:]
2217
if 'Coordinate ' in line:
2218
(coord_n, val) = line.split('=')
2219
n = int(coord_n.split()[1])
2220
assert(n == len(all_coordinates) + 1)
2221
if n == self._level and saw_extended:
2222
grab_extended = True
2223
else:
2224
all_coordinates.append(val)
2225
elif grab_extended:
2226
assert('=' in line)
2227
grab_extended = False
2228
all_coordinates.append(line.split('=')[1])
2229
2230
if saw_signs:
2231
self._signs = all_signs
2232
2233
if saw_primitive or saw_extended:
2234
self._raw_sample_point = (saw_extended, alpha_text, all_coordinates)
2235
2236
def __iter__(self):
2237
r"""
2238
Iterate through the stack over a QEPCAD cell.
2239
2240
EXAMPLES::
2241
2242
sage: var('x,y')
2243
(x, y)
2244
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2245
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2246
QEPCAD object has moved to phase 'Before Projection (y)'
2247
QEPCAD object has moved to phase 'Before Choice'
2248
QEPCAD object has moved to phase 'Before Solution'
2249
sage: [c.sample_point() for c in qe.cell(3)] # optional - qepcad
2250
[(0, -3), (0, -1), (0, -1/2), (0, 1), (0, 3)]
2251
"""
2252
for i in range(1, self._number_of_children + 1):
2253
yield self[i]
2254
2255
def __getitem__(self, i):
2256
r"""
2257
Select an element from the stack over a QEPCAD cell.
2258
2259
Note that cells are numbered starting with 1.
2260
2261
EXAMPLES::
2262
2263
sage: var('x,y')
2264
(x, y)
2265
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional
2266
sage: qe.go(); qe.go(); qe.go() # optional
2267
QEPCAD object has moved to phase 'Before Projection (y)'
2268
QEPCAD object has moved to phase 'Before Choice'
2269
QEPCAD object has moved to phase 'Before Solution'
2270
sage: qe.cell(2).__getitem__(3) # optional
2271
QEPCAD cell (2, 3)
2272
"""
2273
return self._parent.cell(self.index() + (i,))
2274
2275
def __len__(self):
2276
r"""
2277
Return the number of elements in the stack over a QEPCAD cell.
2278
(This is always an odd number, if the stack has been constructed.)
2279
2280
EXAMPLES::
2281
2282
sage: var('x,y')
2283
(x, y)
2284
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2285
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2286
QEPCAD object has moved to phase 'Before Projection (y)'
2287
QEPCAD object has moved to phase 'Before Choice'
2288
QEPCAD object has moved to phase 'Before Solution'
2289
sage: len(qe.cell()) # optional - qepcad
2290
5
2291
sage: [len(c) for c in qe.cell()] # optional - qepcad
2292
[1, 3, 5, 3, 1]
2293
"""
2294
return self._number_of_children
2295
2296
def __repr__(self):
2297
r"""
2298
Return a string representation of a QEPCAD cell. Just gives the
2299
cell index.
2300
2301
EXAMPLES::
2302
2303
sage: var('x,y')
2304
(x, y)
2305
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2306
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2307
QEPCAD object has moved to phase 'Before Projection (y)'
2308
QEPCAD object has moved to phase 'Before Choice'
2309
QEPCAD object has moved to phase 'Before Solution'
2310
sage: qe.cell() # optional - qepcad
2311
QEPCAD cell ()
2312
sage: qe.cell(1) # optional - qepcad
2313
QEPCAD cell (1)
2314
sage: qe.cell(2, 2) # optional - qepcad
2315
QEPCAD cell (2, 2)
2316
"""
2317
ind = self.index()
2318
if len(ind) == 1:
2319
ind = '(%s)' % ind[0]
2320
else:
2321
ind = str(ind)
2322
return ('QEPCAD cell %s' % ind)
2323
2324
def index(self):
2325
r"""
2326
Gives the index of a QEPCAD cell.
2327
2328
EXAMPLES::
2329
2330
sage: var('x,y')
2331
(x, y)
2332
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2333
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2334
QEPCAD object has moved to phase 'Before Projection (y)'
2335
QEPCAD object has moved to phase 'Before Choice'
2336
QEPCAD object has moved to phase 'Before Solution'
2337
sage: qe.cell().index() # optional - qepcad
2338
()
2339
sage: qe.cell(1).index() # optional - qepcad
2340
(1,)
2341
sage: qe.cell(2, 2).index() # optional - qepcad
2342
(2, 2)
2343
"""
2344
return self._index
2345
2346
def level(self):
2347
r"""
2348
Returns the level of a QEPCAD cell.
2349
2350
EXAMPLES::
2351
2352
sage: var('x,y')
2353
(x, y)
2354
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2355
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2356
QEPCAD object has moved to phase 'Before Projection (y)'
2357
QEPCAD object has moved to phase 'Before Choice'
2358
QEPCAD object has moved to phase 'Before Solution'
2359
sage: qe.cell().level() # optional - qepcad
2360
0
2361
sage: qe.cell(1).level() # optional - qepcad
2362
1
2363
sage: qe.cell(2, 2).level() # optional - qepcad
2364
2
2365
"""
2366
return self._level
2367
2368
def signs(self):
2369
r"""
2370
Returns the sign vector of a QEPCAD cell. This is a list of lists.
2371
The outer list contains one element for each level of the cell;
2372
the inner list contains one element for each projection factor at
2373
that level. These elements are either -1, 0, or 1.
2374
2375
EXAMPLES::
2376
2377
sage: var('x,y')
2378
(x, y)
2379
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2380
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2381
QEPCAD object has moved to phase 'Before Projection (y)'
2382
QEPCAD object has moved to phase 'Before Choice'
2383
QEPCAD object has moved to phase 'Before Solution'
2384
sage: from sage.interfaces.qepcad import QepcadCell
2385
sage: all_cells = flatten(qe.cell(), ltypes=QepcadCell, max_level=1) # optional - qepcad
2386
sage: [(c, c.signs()[1][0]) for c in all_cells] # optional - qepcad
2387
[(QEPCAD cell (1, 1), 1), (QEPCAD cell (2, 1), 1), (QEPCAD cell (2, 2), 0), (QEPCAD cell (2, 3), 1), (QEPCAD cell (3, 1), 1), (QEPCAD cell (3, 2), 0), (QEPCAD cell (3, 3), -1), (QEPCAD cell (3, 4), 0), (QEPCAD cell (3, 5), 1), (QEPCAD cell (4, 1), 1), (QEPCAD cell (4, 2), 0), (QEPCAD cell (4, 3), 1), (QEPCAD cell (5, 1), 1)]
2388
"""
2389
return self._signs
2390
2391
def number_of_children(self):
2392
r"""
2393
Return the number of elements in the stack over a QEPCAD cell.
2394
(This is always an odd number, if the stack has been constructed.)
2395
2396
EXAMPLES::
2397
2398
sage: var('x,y')
2399
(x, y)
2400
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2401
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2402
QEPCAD object has moved to phase 'Before Projection (y)'
2403
QEPCAD object has moved to phase 'Before Choice'
2404
QEPCAD object has moved to phase 'Before Solution'
2405
sage: qe.cell().number_of_children() # optional - qepcad
2406
5
2407
sage: [c.number_of_children() for c in qe.cell()] # optional - qepcad
2408
[1, 3, 5, 3, 1]
2409
"""
2410
return self._number_of_children
2411
2412
def set_truth(self, v):
2413
r"""
2414
Sets the truth value of this cell, as used by QEPCAD for solution
2415
formula construction.
2416
2417
The argument \var{v} should be either a boolean or \code{None}
2418
(which will set the truth value to ``undetermined'').
2419
2420
EXAMPLES::
2421
2422
sage: var('x,y')
2423
(x, y)
2424
sage: qe = qepcad(x^2 + y^2 == 1, interact=True) # optional - qepcad
2425
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2426
QEPCAD object has moved to phase 'Before Projection (y)'
2427
QEPCAD object has moved to phase 'Before Choice'
2428
QEPCAD object has moved to phase 'Before Solution'
2429
sage: qe.solution_extension('T') # optional - qepcad
2430
y^2 + x^2 - 1 = 0
2431
sage: qe.cell(3, 3).set_truth(True) # optional - qepcad
2432
sage: qe.solution_extension('T') # optional - qepcad
2433
y^2 + x^2 - 1 <= 0
2434
"""
2435
if v is None:
2436
nv = 2
2437
else:
2438
nv = 1 if v else 0
2439
self._parent.set_truth_value(self, nv)
2440
2441
def sample_point(self):
2442
r"""
2443
Returns the coordinates of a point in the cell, as a tuple of
2444
\sage algebraic reals.
2445
2446
EXAMPLES::
2447
2448
sage: qe = qepcad(x^2 - x - 1 == 0, interact=True) # optional - qepcad
2449
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2450
QEPCAD object has moved to phase 'At the end of projection phase'
2451
QEPCAD object has moved to phase 'Before Choice'
2452
QEPCAD object has moved to phase 'Before Solution'
2453
sage: v1 = qe.cell(2).sample_point()[0]; v1 # optional - qepcad
2454
-0.618033988749895?
2455
sage: v2 = qe.cell(4).sample_point()[0]; v2 # optional - qepcad
2456
1.618033988749895?
2457
sage: v1 + v2 == 1 # optional - qepcad
2458
True
2459
"""
2460
try:
2461
return self._sample_point
2462
except AttributeError:
2463
(extended, alpha, coordinates) = self._raw_sample_point
2464
2465
need_alpha = False
2466
for c in coordinates:
2467
if 'alpha' in c:
2468
need_alpha = True
2469
break
2470
2471
if need_alpha:
2472
locals = {'alpha': _eval_qepcad_algebraic(alpha)}
2473
else:
2474
locals = {}
2475
2476
points = []
2477
2478
for c in coordinates:
2479
if 'unique' in c:
2480
points.append(_eval_qepcad_algebraic(c))
2481
else:
2482
points.append(sage_eval(implicit_mul(c), locals))
2483
2484
return tuple(points)
2485
2486
def sample_point_dict(self):
2487
r"""
2488
Returns the coordinates of a point in the cell, as a dictionary
2489
mapping variable names (as strings) to \sage algebraic reals.
2490
2491
EXAMPLES::
2492
2493
sage: qe = qepcad(x^2 - x - 1 == 0, interact=True) # optional - qepcad
2494
sage: qe.go(); qe.go(); qe.go() # optional - qepcad
2495
QEPCAD object has moved to phase 'At the end of projection phase'
2496
QEPCAD object has moved to phase 'Before Choice'
2497
QEPCAD object has moved to phase 'Before Solution'
2498
sage: qe.cell(4).sample_point_dict() # optional - qepcad
2499
{'x': 1.618033988749895?}
2500
"""
2501
points = self.sample_point()
2502
vars = self._parent._varlist
2503
2504
return dict([(vars[i], points[i]) for i in range(len(points))])
2505
2506