passagemath-fricas 10.5.32__cp39-cp39-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_fricas-10.5.32.dist-info/METADATA +87 -0
- passagemath_fricas-10.5.32.dist-info/RECORD +11 -0
- passagemath_fricas-10.5.32.dist-info/WHEEL +5 -0
- passagemath_fricas-10.5.32.dist-info/top_level.txt +1 -0
- sage/all__sagemath_fricas.py +3 -0
- sage/interfaces/all__sagemath_fricas.py +1 -0
- sage/interfaces/fricas.py +2186 -0
- sage/libs/all__sagemath_fricas.py +1 -0
- sage/libs/fricas.cpython-39-x86_64-linux-gnu.so +0 -0
- sage/symbolic/all__sagemath_fricas.py +1 -0
- sage/symbolic/expression_conversion_fricas.py +201 -0
@@ -0,0 +1,2186 @@
|
|
1
|
+
# sage_setup: distribution = sagemath-fricas
|
2
|
+
# sage.doctest: optional - fricas
|
3
|
+
r"""
|
4
|
+
Interface to FriCAS
|
5
|
+
|
6
|
+
.. TODO::
|
7
|
+
|
8
|
+
- some conversions in ``sage.functions`` are still missing and
|
9
|
+
all should be checked and tested
|
10
|
+
|
11
|
+
FriCAS is a free GPL-compatible (modified BSD license) general
|
12
|
+
purpose computer algebra system based on Axiom. The FriCAS
|
13
|
+
website can be found at http://fricas.sourceforge.net/.
|
14
|
+
|
15
|
+
AUTHORS:
|
16
|
+
|
17
|
+
- Mike Hansen (2009-02): Split off the FriCAS interface from
|
18
|
+
the Axiom interface.
|
19
|
+
|
20
|
+
- Martin Rubey, Bill Page (2016-08): Completely separate from Axiom,
|
21
|
+
implement more complete translation from FriCAS to SageMath types.
|
22
|
+
|
23
|
+
EXAMPLES::
|
24
|
+
|
25
|
+
sage: fricas('3 * 5')
|
26
|
+
15
|
27
|
+
sage: a = fricas(3) * fricas(5); a
|
28
|
+
15
|
29
|
+
|
30
|
+
The type of a is :class:`FriCASElement`, i.e., an element of the
|
31
|
+
FriCAS interpreter::
|
32
|
+
|
33
|
+
sage: type(a)
|
34
|
+
<class 'sage.interfaces.fricas.FriCASElement'>
|
35
|
+
sage: a.parent()
|
36
|
+
FriCAS
|
37
|
+
|
38
|
+
The underlying FriCAS type of a is also available, via the type
|
39
|
+
method::
|
40
|
+
|
41
|
+
sage: a.typeOf()
|
42
|
+
PositiveInteger
|
43
|
+
|
44
|
+
FriCAS objects are normally displayed using "ASCII art"::
|
45
|
+
|
46
|
+
sage: fricas(2/3)
|
47
|
+
2
|
48
|
+
-
|
49
|
+
3
|
50
|
+
sage: fricas('x^2 + 3/7')
|
51
|
+
2 3
|
52
|
+
x + -
|
53
|
+
7
|
54
|
+
|
55
|
+
Functions defined in FriCAS are available as methods of the :class:`fricas<FriCAS>` object::
|
56
|
+
|
57
|
+
sage: F = fricas.factor('x^5 - y^5'); F
|
58
|
+
4 3 2 2 3 4
|
59
|
+
- (y - x)(y + x y + x y + x y + x )
|
60
|
+
sage: type(F)
|
61
|
+
<class 'sage.interfaces.fricas.FriCASElement'>
|
62
|
+
sage: F.typeOf()
|
63
|
+
Factored(Polynomial(Integer))
|
64
|
+
|
65
|
+
We can also create a FriCAS polynomial and apply the function
|
66
|
+
``factor`` from FriCAS. The notation ``f.factor()`` is consistent
|
67
|
+
with how the rest of SageMath works::
|
68
|
+
|
69
|
+
sage: f = fricas('x^5 - y^5')
|
70
|
+
sage: f^2
|
71
|
+
10 5 5 10
|
72
|
+
y - 2 x y + x
|
73
|
+
sage: f.factor()
|
74
|
+
4 3 2 2 3 4
|
75
|
+
- (y - x)(y + x y + x y + x y + x )
|
76
|
+
|
77
|
+
For many FriCAS types, translation to an appropriate SageMath type is
|
78
|
+
available::
|
79
|
+
|
80
|
+
sage: f.factor().sage()
|
81
|
+
(y - x) * (y^4 + y^3*x + y^2*x^2 + y*x^3 + x^4)
|
82
|
+
|
83
|
+
Control-C interruption works well with the FriCAS interface. For
|
84
|
+
example, try the following sum but with a much bigger range, and hit
|
85
|
+
control-C::
|
86
|
+
|
87
|
+
sage: f = fricas('(x^5 - y^5)^10000') # not tested
|
88
|
+
Interrupting FriCAS...
|
89
|
+
...
|
90
|
+
KeyboardInterrupt: Ctrl-c pressed while running FriCAS
|
91
|
+
|
92
|
+
Let us demonstrate some features of FriCAS. FriCAS can guess a
|
93
|
+
differential equation for the generating function for integer
|
94
|
+
partitions::
|
95
|
+
|
96
|
+
sage: fricas("guessADE([partition n for n in 0..40], homogeneous==4)")
|
97
|
+
[
|
98
|
+
[
|
99
|
+
n
|
100
|
+
[x ]f(x):
|
101
|
+
2 3 (iv) 2 2 , 3 ,,,
|
102
|
+
x f(x) f (x) + (20 x f(x) f (x) + 5 x f(x) )f (x)
|
103
|
+
<BLANKLINE>
|
104
|
+
+
|
105
|
+
2 2 ,, 2
|
106
|
+
- 39 x f(x) f (x)
|
107
|
+
<BLANKLINE>
|
108
|
+
+
|
109
|
+
2 , 2 2 , 3 ,, 2 , 4
|
110
|
+
(12 x f(x)f (x) - 15 x f(x) f (x) + 4 f(x) )f (x) + 6 x f (x)
|
111
|
+
<BLANKLINE>
|
112
|
+
+
|
113
|
+
, 3 2 , 2
|
114
|
+
10 x f(x)f (x) - 16 f(x) f (x)
|
115
|
+
<BLANKLINE>
|
116
|
+
=
|
117
|
+
0
|
118
|
+
,
|
119
|
+
2 3 4
|
120
|
+
f(x) = 1 + x + 2 x + 3 x + O(x )]
|
121
|
+
]
|
122
|
+
|
123
|
+
FriCAS can solve linear ordinary differential equations::
|
124
|
+
|
125
|
+
sage: fricas.set("y", "operator y")
|
126
|
+
sage: fricas.set("deq", "x^3*D(y x, x, 3) + x^2*D(y x, x, 2) - 2*x*D(y x, x) + 2*y x - 2*x^4")
|
127
|
+
sage: fricas.set("sol", "solve(deq, y, x)"); fricas("sol")
|
128
|
+
5 3 2
|
129
|
+
x - 10 x + 20 x + 4
|
130
|
+
[particular = ----------------------,
|
131
|
+
15 x
|
132
|
+
3 2 3 3 2
|
133
|
+
2 x - 3 x + 1 x - 1 x - 3 x - 1
|
134
|
+
basis = [---------------, ------, -------------]]
|
135
|
+
x x x
|
136
|
+
|
137
|
+
sage: fricas("sol.particular").sage()
|
138
|
+
1/15*(x^5 - 10*x^3 + 20*x^2 + 4)/x
|
139
|
+
sage: fricas("sol.basis").sage()
|
140
|
+
[(2*x^3 - 3*x^2 + 1)/x, (x^3 - 1)/x, (x^3 - 3*x^2 - 1)/x]
|
141
|
+
sage: fricas.eval(")clear values y deq sol")
|
142
|
+
''
|
143
|
+
|
144
|
+
FriCAS can expand expressions into series::
|
145
|
+
|
146
|
+
sage: x = var('x'); ex = sqrt(cos(x)); a = fricas(ex).series(x=0); a
|
147
|
+
1 2 1 4 19 6 559 8 29161 10 11
|
148
|
+
1 - - x - -- x - ---- x - ------ x - --------- x + O(x )
|
149
|
+
4 96 5760 645120 116121600
|
150
|
+
|
151
|
+
sage: a.coefficients()[38].sage()
|
152
|
+
-29472026335337227150423659490832640468979/274214482066329363682430667508979749984665600000000
|
153
|
+
|
154
|
+
sage: ex = sqrt(atan(x)); a = fricas(ex).series(x=0); a
|
155
|
+
1 5 9
|
156
|
+
- - -
|
157
|
+
2 1 2 31 2 6
|
158
|
+
x - - x + --- x + O(x )
|
159
|
+
6 360
|
160
|
+
|
161
|
+
sage: a.coefficient(9/2).sage()
|
162
|
+
31/360
|
163
|
+
|
164
|
+
sage: x = fricas("x::TaylorSeries Fraction Integer")
|
165
|
+
sage: y = fricas("y::TaylorSeries Fraction Integer")
|
166
|
+
sage: 2*(1+2*x+sqrt(1-4*x)-2*x*y).recip()
|
167
|
+
2 3 2 2 3 4 4 5
|
168
|
+
1 + (x y + x ) + 2 x + (x y + 2 x y + 6 x ) + (4 x y + 18 x )
|
169
|
+
+
|
170
|
+
3 3 4 2 5 6 5 2 6 7
|
171
|
+
(x y + 3 x y + 13 x y + 57 x ) + (6 x y + 40 x y + 186 x )
|
172
|
+
+
|
173
|
+
4 4 5 3 6 2 7 8
|
174
|
+
(x y + 4 x y + 21 x y + 130 x y + 622 x )
|
175
|
+
+
|
176
|
+
6 3 7 2 8 9
|
177
|
+
(8 x y + 66 x y + 432 x y + 2120 x )
|
178
|
+
+
|
179
|
+
5 5 6 4 7 3 8 2 9 10
|
180
|
+
(x y + 5 x y + 30 x y + 220 x y + 1466 x y + 7338 x ) + O(11)
|
181
|
+
|
182
|
+
FriCAS does some limits right::
|
183
|
+
|
184
|
+
sage: x = var('x'); ex = x^2*exp(-x)*Ei(x) - x; fricas(ex).limit(x=oo)
|
185
|
+
1
|
186
|
+
"""
|
187
|
+
|
188
|
+
###########################################################################
|
189
|
+
# Copyright (C) 2008 Mike Hansen <mhansen@gmail.com>
|
190
|
+
# 2007 Bill Page
|
191
|
+
# 2006 William Stein <wstein@gmail.com>
|
192
|
+
#
|
193
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
194
|
+
# The full text of the GPL is available at:
|
195
|
+
#
|
196
|
+
# https://www.gnu.org/licenses/
|
197
|
+
###########################################################################
|
198
|
+
|
199
|
+
import re
|
200
|
+
import os
|
201
|
+
|
202
|
+
import sage.interfaces.abc
|
203
|
+
|
204
|
+
from sage.interfaces.tab_completion import ExtraTabCompletion
|
205
|
+
from sage.interfaces.expect import Expect, ExpectElement, FunctionElement, ExpectFunction
|
206
|
+
from sage.env import DOT_SAGE, LOCAL_IDENTIFIER
|
207
|
+
from sage.misc.instancedoc import instancedoc
|
208
|
+
from sage.rings.integer_ring import ZZ
|
209
|
+
from sage.rings.rational_field import QQ
|
210
|
+
from sage.misc.lazy_import import lazy_import
|
211
|
+
lazy_import('sage.symbolic.expression', ['symbol_table', 'register_symbol'])
|
212
|
+
lazy_import('sage.calculus.var', ['var', 'function'])
|
213
|
+
lazy_import('sage.symbolic.constants', ['I', 'e', 'pi'])
|
214
|
+
|
215
|
+
FRICAS_CONSTANTS = {'%i': I,
|
216
|
+
'%e': e,
|
217
|
+
'%pi': pi}
|
218
|
+
|
219
|
+
FRICAS_SINGLE_LINE_START = 3 # where output starts when it fits next to the line number
|
220
|
+
FRICAS_MULTI_LINE_START = 2 # and when it doesn't
|
221
|
+
FRICAS_LINE_LENGTH = 80 # length of a line, should match the line length in sage
|
222
|
+
# the following messages have, unfortunately, no markup.
|
223
|
+
FRICAS_WHAT_OPERATIONS_STRING = r"Operations whose names satisfy the above pattern\(s\):"
|
224
|
+
FRICAS_ERROR_IN_LIBRARY_CODE = ">> Error detected within library code:"
|
225
|
+
|
226
|
+
# only the last command should be necessary to make the interface
|
227
|
+
# work, the other are optimizations. Beware that lisp distinguishes
|
228
|
+
# between ' and ".
|
229
|
+
FRICAS_INIT_CODE = (
|
230
|
+
")set functions compile on",
|
231
|
+
")set message any on",
|
232
|
+
")set message autoload off",
|
233
|
+
")set message bottomup off",
|
234
|
+
")set message dropmap off",
|
235
|
+
")set message expose off",
|
236
|
+
")set message file off",
|
237
|
+
")set message frame off",
|
238
|
+
")set message highlighting off",
|
239
|
+
")set message instant off",
|
240
|
+
")set message insteach off",
|
241
|
+
")set message interponly off",
|
242
|
+
")set message prompt step",
|
243
|
+
")set message selection off",
|
244
|
+
")set message set off",
|
245
|
+
")set message startup on",
|
246
|
+
")set message storage off",
|
247
|
+
")set message testing off",
|
248
|
+
")set message time off",
|
249
|
+
")set message type off",
|
250
|
+
")set message void off",
|
251
|
+
")set output length " + str(FRICAS_LINE_LENGTH),
|
252
|
+
")lisp (setf |$ioHook|"
|
253
|
+
" (lambda (x &optional args)"
|
254
|
+
" (when (member x '(|startAlgebraOutput| |endOfAlgebraOutput|"
|
255
|
+
" |startKeyedMsg| |endOfKeyedMsg|))"
|
256
|
+
" (prin1 x)"
|
257
|
+
" (princ #\\Newline))))")
|
258
|
+
# code (one-liners!) executed after having set up the prompt
|
259
|
+
FRICAS_HELPER_CODE = (
|
260
|
+
'sageprint(x:InputForm):String == ' +
|
261
|
+
'(atom? x => (' +
|
262
|
+
'float? x => return float(x)::String;' +
|
263
|
+
'integer? x => return integer(x)::String;' +
|
264
|
+
'string? x => return concat(["_"", string(x)::String, "_""])$String;' +
|
265
|
+
'symbol? x => return string(symbol(x)));' +
|
266
|
+
'S: List String := [sageprint y for y in destruct x];' +
|
267
|
+
'R: String := new(1 + reduce(_+, [1 + #(s)$String for s in S], 0),' +
|
268
|
+
'space()$Character);' +
|
269
|
+
'copyInto!(R, "(", 1);' +
|
270
|
+
'i := 2;' +
|
271
|
+
'for s in S repeat'
|
272
|
+
'(copyInto!(R, s, i); i := i + 1 + #(s)$String);' +
|
273
|
+
'copyInto!(R, ")", i-1);' +
|
274
|
+
'return R)',)
|
275
|
+
|
276
|
+
FRICAS_LINENUMBER_OFF_CODE = ")lisp (setf |$IOindex| NIL)"
|
277
|
+
FRICAS_FIRST_PROMPT = r"\(1\) -> "
|
278
|
+
FRICAS_LINENUMBER_OFF_PROMPT = r"\(NIL\) -> "
|
279
|
+
|
280
|
+
|
281
|
+
class FriCAS(ExtraTabCompletion, Expect):
|
282
|
+
"""
|
283
|
+
Interface to a FriCAS interpreter.
|
284
|
+
"""
|
285
|
+
def __init__(self, name='fricas', command=None,
|
286
|
+
script_subdirectory=None, logfile=None,
|
287
|
+
server=None, server_tmpdir=None):
|
288
|
+
"""
|
289
|
+
Create an instance of the FriCAS interpreter.
|
290
|
+
|
291
|
+
TESTS::
|
292
|
+
|
293
|
+
sage: fricas == loads(dumps(fricas))
|
294
|
+
True
|
295
|
+
|
296
|
+
Check that :issue:`25174` is fixed::
|
297
|
+
|
298
|
+
sage: fricas(I)
|
299
|
+
%i
|
300
|
+
|
301
|
+
sage: integrate(sin(x)*exp(I*x), x, -pi, 0, algorithm='fricas')
|
302
|
+
1/2*I*pi
|
303
|
+
|
304
|
+
sage: fricas(I*sin(x)).sage()
|
305
|
+
I*sin(x)
|
306
|
+
|
307
|
+
sage: fricas(I*x).sage()
|
308
|
+
I*x
|
309
|
+
"""
|
310
|
+
if command is None:
|
311
|
+
from sage.features.fricas import FriCAS
|
312
|
+
command = [FriCAS(), "-nosman"]
|
313
|
+
|
314
|
+
eval_using_file_cutoff = 4096 - 5 # magic number from Expect._eval_line (there might be a bug)
|
315
|
+
assert max(len(c) for c in FRICAS_INIT_CODE) < eval_using_file_cutoff
|
316
|
+
self.__eval_using_file_cutoff = eval_using_file_cutoff
|
317
|
+
self._COMMANDS_CACHE = '%s/%s_commandlist_cache.sobj' % (DOT_SAGE, name)
|
318
|
+
# we run the init code in _start to avoid spurious output
|
319
|
+
Expect.__init__(self,
|
320
|
+
name=name,
|
321
|
+
prompt=FRICAS_FIRST_PROMPT,
|
322
|
+
command=command,
|
323
|
+
script_subdirectory=script_subdirectory,
|
324
|
+
server=server,
|
325
|
+
server_tmpdir=server_tmpdir,
|
326
|
+
restart_on_ctrlc=False,
|
327
|
+
verbose_start=False,
|
328
|
+
init_code=[],
|
329
|
+
logfile=logfile,
|
330
|
+
eval_using_file_cutoff=eval_using_file_cutoff)
|
331
|
+
|
332
|
+
def _start(self):
|
333
|
+
"""
|
334
|
+
Start the FriCAS interpreter and switch off the linenumbers.
|
335
|
+
|
336
|
+
EXAMPLES::
|
337
|
+
|
338
|
+
sage: a = FriCAS()
|
339
|
+
sage: a.is_running()
|
340
|
+
False
|
341
|
+
sage: a._start()
|
342
|
+
sage: a.is_running()
|
343
|
+
True
|
344
|
+
sage: a.quit()
|
345
|
+
"""
|
346
|
+
# setting the prompt properly is necessary for restarting FriCAS
|
347
|
+
self._prompt = FRICAS_FIRST_PROMPT
|
348
|
+
Expect._start(self)
|
349
|
+
for line in FRICAS_INIT_CODE:
|
350
|
+
self.eval(line, reformat=False)
|
351
|
+
# switching off the line numbers also modified the prompt
|
352
|
+
self._prompt = FRICAS_LINENUMBER_OFF_PROMPT
|
353
|
+
self.eval(FRICAS_LINENUMBER_OFF_CODE, reformat=False)
|
354
|
+
for line in FRICAS_HELPER_CODE:
|
355
|
+
self.eval(line, reformat=False)
|
356
|
+
# register translations between SymbolicRing and FriCAS Expression
|
357
|
+
self._register_symbols()
|
358
|
+
|
359
|
+
def _install_hints(self):
|
360
|
+
"""
|
361
|
+
Hints for installing FriCAS on your computer.
|
362
|
+
|
363
|
+
EXAMPLES::
|
364
|
+
|
365
|
+
sage: print(fricas._install_hints())
|
366
|
+
In order...
|
367
|
+
"""
|
368
|
+
return r"""
|
369
|
+
In order to use the FriCAS interface you need to have FriCAS installed.
|
370
|
+
You can either run 'sage -i fricas' to install FriCAS as an optional
|
371
|
+
package within SageMath, or install FriCAS separately, see
|
372
|
+
http://fricas.sourceforge.net.
|
373
|
+
"""
|
374
|
+
|
375
|
+
def _quit_string(self):
|
376
|
+
"""
|
377
|
+
Return the string used to quit FriCAS.
|
378
|
+
|
379
|
+
EXAMPLES::
|
380
|
+
|
381
|
+
sage: fricas._quit_string()
|
382
|
+
')quit'
|
383
|
+
sage: a = FriCAS()
|
384
|
+
sage: a.is_running()
|
385
|
+
False
|
386
|
+
sage: a._start()
|
387
|
+
sage: a.is_running()
|
388
|
+
True
|
389
|
+
sage: a.quit()
|
390
|
+
sage: a.is_running()
|
391
|
+
False
|
392
|
+
|
393
|
+
TESTS:
|
394
|
+
|
395
|
+
Ensure that a new process is started after ``quit()``::
|
396
|
+
|
397
|
+
sage: p = fricas.pid()
|
398
|
+
sage: fricas.quit()
|
399
|
+
sage: fricas.pid() == p
|
400
|
+
False
|
401
|
+
"""
|
402
|
+
return ')quit'
|
403
|
+
|
404
|
+
def _commands(self):
|
405
|
+
"""
|
406
|
+
Return a list of commands available. This is done by parsing the
|
407
|
+
result of the first section of the output of ')what things'.
|
408
|
+
|
409
|
+
EXAMPLES::
|
410
|
+
|
411
|
+
sage: cmds = fricas._commands()
|
412
|
+
sage: len(cmds) > 100
|
413
|
+
True
|
414
|
+
sage: '<' in cmds
|
415
|
+
True
|
416
|
+
sage: 'factor' in cmds
|
417
|
+
True
|
418
|
+
"""
|
419
|
+
output = self.eval(")what operations", reformat=False)
|
420
|
+
m = re.search(FRICAS_WHAT_OPERATIONS_STRING + r"\n(.*)\n\|startKeyedMsg\|",
|
421
|
+
output, flags=re.DOTALL)
|
422
|
+
l = m.groups()[0].split()
|
423
|
+
return l
|
424
|
+
|
425
|
+
def _tab_completion(self, verbose=True, use_disk_cache=True):
|
426
|
+
"""
|
427
|
+
Return a list of all the commands defined in Fricas and optionally
|
428
|
+
(per default) store them to disk.
|
429
|
+
|
430
|
+
EXAMPLES::
|
431
|
+
|
432
|
+
sage: c = fricas._tab_completion(use_disk_cache=False, verbose=False)
|
433
|
+
sage: len(c) > 100
|
434
|
+
True
|
435
|
+
sage: 'factor' in c
|
436
|
+
True
|
437
|
+
sage: '**' in c
|
438
|
+
False
|
439
|
+
sage: 'upperCase?' in c
|
440
|
+
False
|
441
|
+
sage: 'upperCase_q' in c
|
442
|
+
True
|
443
|
+
sage: 'upperCase_e' in c
|
444
|
+
True
|
445
|
+
"""
|
446
|
+
try:
|
447
|
+
return self.__tab_completion
|
448
|
+
except AttributeError:
|
449
|
+
import sage.misc.persist
|
450
|
+
if use_disk_cache:
|
451
|
+
try:
|
452
|
+
self.__tab_completion = sage.misc.persist.load(self._COMMANDS_CACHE)
|
453
|
+
return self.__tab_completion
|
454
|
+
except OSError:
|
455
|
+
pass
|
456
|
+
if verbose:
|
457
|
+
print("\nBuilding %s command completion list (this takes" % self)
|
458
|
+
print("a few seconds only the first time you do it).")
|
459
|
+
print("To force rebuild later, delete %s." % self._COMMANDS_CACHE)
|
460
|
+
v = self._commands()
|
461
|
+
|
462
|
+
# process the commands to strip out things which are not
|
463
|
+
# valid Python identifiers
|
464
|
+
valid = re.compile('[^a-zA-Z0-9_]+')
|
465
|
+
names = [x for x in v if valid.search(x) is None]
|
466
|
+
|
467
|
+
# replace trailing ? with _q and trailing ! with _e
|
468
|
+
names += [x[:-1] + "_q" for x in v if x.endswith("?")]
|
469
|
+
names += [x[:-1] + "_e" for x in v if x.endswith("!")]
|
470
|
+
|
471
|
+
self.__tab_completion = names
|
472
|
+
if len(v) > 200:
|
473
|
+
# Fricas is actually installed.
|
474
|
+
sage.misc.persist.save(v, self._COMMANDS_CACHE)
|
475
|
+
return names
|
476
|
+
|
477
|
+
def _read_in_file_command(self, filename):
|
478
|
+
"""
|
479
|
+
Return the FriCAS command to read the file ``filename``.
|
480
|
+
|
481
|
+
INPUT:
|
482
|
+
|
483
|
+
- ``filename`` -- string ending in '.input'.
|
484
|
+
|
485
|
+
OUTPUT: string with the command for reading filename without output
|
486
|
+
|
487
|
+
TESTS:
|
488
|
+
|
489
|
+
Evaluate a rather long line::
|
490
|
+
|
491
|
+
sage: len(fricas([i for i in range(600)])) # indirect doctest
|
492
|
+
600
|
493
|
+
"""
|
494
|
+
if not filename.endswith('.input'):
|
495
|
+
raise ValueError("the filename must end with .input")
|
496
|
+
|
497
|
+
return ')read %s )quiet' % filename
|
498
|
+
|
499
|
+
def _remote_tmpfile(self):
|
500
|
+
"""
|
501
|
+
Return a remote tmpfile ending with ".input" used to buffer long
|
502
|
+
command lines sent to FriCAS.
|
503
|
+
"""
|
504
|
+
try:
|
505
|
+
return self.__remote_tmpfile
|
506
|
+
except AttributeError:
|
507
|
+
self.__remote_tmpfile = self._remote_tmpdir() + "/interface_%s:%s.input" % (LOCAL_IDENTIFIER, self.pid())
|
508
|
+
return self.__remote_tmpfile
|
509
|
+
|
510
|
+
# what I expect from FriCAS:
|
511
|
+
|
512
|
+
# 1.) in set(self, var, value)
|
513
|
+
#
|
514
|
+
# no markers:
|
515
|
+
# there could be some "debugging" output, as in fricas("guessADE([1,1,1,1], debug==true)")
|
516
|
+
#
|
517
|
+
# startKeyedMsg: an error happened
|
518
|
+
#
|
519
|
+
# 2.) in get(self, var)
|
520
|
+
#
|
521
|
+
# |startAlgebraOutput\|...|endOfAlgebraOutput\|
|
522
|
+
#
|
523
|
+
# 3.) I also need a routine to send a system command and get its output.
|
524
|
+
|
525
|
+
def _check_errors(self, line, output):
|
526
|
+
"""
|
527
|
+
Check whether output contains an error and, if so, raise it.
|
528
|
+
|
529
|
+
INPUT:
|
530
|
+
|
531
|
+
- ``line`` -- string that was sent to FriCAS
|
532
|
+
|
533
|
+
- ``output`` -- string returned by FriCAS
|
534
|
+
|
535
|
+
OUTPUT: none
|
536
|
+
|
537
|
+
TESTS::
|
538
|
+
|
539
|
+
sage: fricas.set("x", "[i fo83r i in 0..17]") # indirect doctest
|
540
|
+
Traceback (most recent call last):
|
541
|
+
...
|
542
|
+
RuntimeError: An error occurred when FriCAS evaluated '[i fo83r i in 0..17]':
|
543
|
+
Line 1: x:=[i fo83r i in 0..17];
|
544
|
+
...A..........B
|
545
|
+
Error A: Missing mate.
|
546
|
+
Error B: syntax error at top level
|
547
|
+
Error B: Possibly missing a ]
|
548
|
+
3 error(s) parsing
|
549
|
+
|
550
|
+
sage: fricas.set("x", "something stupid") # indirect doctest
|
551
|
+
Traceback (most recent call last):
|
552
|
+
...
|
553
|
+
RuntimeError: An error occurred when FriCAS evaluated 'something stupid':
|
554
|
+
There are no library operations named something
|
555
|
+
Use HyperDoc Browse or issue
|
556
|
+
)what op something
|
557
|
+
to learn if there is any operation containing " something " in its
|
558
|
+
name.
|
559
|
+
<BLANKLINE>
|
560
|
+
Cannot find a definition or applicable library operation named
|
561
|
+
something with argument type(s)
|
562
|
+
Variable(stupid)
|
563
|
+
<BLANKLINE>
|
564
|
+
Perhaps you should use "@" to indicate the required return type, or
|
565
|
+
"$" to specify which version of the function you need.
|
566
|
+
"""
|
567
|
+
# otherwise there might be a message
|
568
|
+
m = re.search(r"\|startKeyedMsg\|\n(.*)\n\|endOfKeyedMsg\|",
|
569
|
+
output, flags=re.DOTALL)
|
570
|
+
if m:
|
571
|
+
replacements = [('|startKeyedMsg|\n', ''),
|
572
|
+
('|endOfKeyedMsg|', '')]
|
573
|
+
for old, new in replacements:
|
574
|
+
output = output.replace(old, new)
|
575
|
+
raise RuntimeError("An error occurred when FriCAS evaluated '%s':\n%s" % (line, output))
|
576
|
+
|
577
|
+
# or even an error
|
578
|
+
if FRICAS_ERROR_IN_LIBRARY_CODE in output:
|
579
|
+
raise RuntimeError("An error occurred when FriCAS evaluated '%s':\n%s" % (line, output))
|
580
|
+
|
581
|
+
@staticmethod
|
582
|
+
def _register_symbols():
|
583
|
+
"""
|
584
|
+
Register translations between elements of the ``SymbolicRing`` and FriCAS ``Expression`` domain.
|
585
|
+
|
586
|
+
This method is called from :meth:`_start`, to work around a
|
587
|
+
circular import problem involving ``pi``.
|
588
|
+
"""
|
589
|
+
from sage.calculus.functional import diff
|
590
|
+
from sage.functions.log import dilog, lambert_w
|
591
|
+
from sage.functions.trig import sin, cos, tan, cot, sec, csc, asin
|
592
|
+
from sage.functions.hyperbolic import tanh, sinh, cosh, coth, sech, csch
|
593
|
+
from sage.functions.other import abs
|
594
|
+
from sage.functions.gamma import gamma
|
595
|
+
from sage.functions.special import elliptic_e, elliptic_f
|
596
|
+
from sage.misc.functional import symbolic_sum, symbolic_prod
|
597
|
+
from sage.rings.infinity import infinity
|
598
|
+
register_symbol(pi, {'fricas': 'pi'}, 0) # %pi::INFORM is %pi, but (pi) also exists
|
599
|
+
register_symbol(lambda: infinity, {'fricas': 'infinity'}, 0) # %infinity::INFORM is (infinity)
|
600
|
+
register_symbol(lambda: infinity, {'fricas': 'plusInfinity'}, 0) # %plusInfinity::INFORM is (plusInfinity)
|
601
|
+
register_symbol(lambda: -infinity, {'fricas': 'minusInfinity'}, 0) # %minusInfinity::INFORM is (minusInfinity)
|
602
|
+
register_symbol(cos, {'fricas': 'cos'})
|
603
|
+
register_symbol(sin, {'fricas': 'sin'})
|
604
|
+
register_symbol(tan, {'fricas': 'tan'})
|
605
|
+
register_symbol(cot, {'fricas': 'cot'})
|
606
|
+
register_symbol(sec, {'fricas': 'sec'})
|
607
|
+
register_symbol(csc, {'fricas': 'csc'})
|
608
|
+
register_symbol(tanh, {'fricas': 'tanh'})
|
609
|
+
register_symbol(sinh, {'fricas': 'sinh'})
|
610
|
+
register_symbol(cosh, {'fricas': 'cosh'})
|
611
|
+
register_symbol(coth, {'fricas': 'coth'})
|
612
|
+
register_symbol(sech, {'fricas': 'sech'})
|
613
|
+
register_symbol(csch, {'fricas': 'csch'})
|
614
|
+
register_symbol(gamma, {'fricas': 'Gamma'}, 1)
|
615
|
+
register_symbol(gamma, {'fricas': 'Gamma'}, 2)
|
616
|
+
register_symbol(lambda x, y: elliptic_e(asin(x), y), {'fricas': 'ellipticE'}, 2)
|
617
|
+
register_symbol(lambda x, y: elliptic_f(asin(x), y), {'fricas': 'ellipticF'}, 2)
|
618
|
+
register_symbol(lambda x, y: x + y, {'fricas': '+'}, 2)
|
619
|
+
register_symbol(lambda x, y: x - y, {'fricas': '-'}, 2)
|
620
|
+
register_symbol(lambda x, y: x * y, {'fricas': '*'}, 2)
|
621
|
+
register_symbol(lambda x, y: x / y, {'fricas': '/'}, 2)
|
622
|
+
register_symbol(lambda x, y: x ** y, {'fricas': '^'}, 2)
|
623
|
+
register_symbol(lambda f, x: diff(f, x), {'fricas': 'D'}, 2)
|
624
|
+
register_symbol(lambda x, y: x + y * I, {'fricas': 'complex'}, 2)
|
625
|
+
register_symbol(lambda x: dilog(1 - x), {'fricas': 'dilog'}, 1)
|
626
|
+
register_symbol(lambda z: lambert_w(z), {'fricas': 'lambertW'}, 1)
|
627
|
+
register_symbol(abs, {'fricas': 'abs'}, 1)
|
628
|
+
# construct occurs in the InputForm of hypergeometricF
|
629
|
+
register_symbol(lambda *x: x, {'fricas': 'construct'}, -1)
|
630
|
+
# the following is a hack to deal with
|
631
|
+
# integrate(sin((x^2+1)/x),x)::INFORM giving
|
632
|
+
# (integral (sin (/ (+ (^ x 2) 1) x)) (:: x Symbol))
|
633
|
+
register_symbol(lambda x, y: x, {'fricas': '::'}, 2)
|
634
|
+
|
635
|
+
def _convert_eval(f, a, b):
|
636
|
+
# it might be that FriCAS also returns a two-argument
|
637
|
+
# eval, where the second argument is a list of equations,
|
638
|
+
# in which case this function needs to be adapted
|
639
|
+
return f.subs({a: b})
|
640
|
+
|
641
|
+
register_symbol(_convert_eval, {'fricas': 'eval'}, 3)
|
642
|
+
|
643
|
+
def _convert_sum(x, y):
|
644
|
+
v, seg = y.operands()
|
645
|
+
a, b = seg.operands()
|
646
|
+
return symbolic_sum(x, v, a, b)
|
647
|
+
|
648
|
+
def _convert_prod(x, y):
|
649
|
+
v, seg = y.operands()
|
650
|
+
a, b = seg.operands()
|
651
|
+
return symbolic_prod(x, v, a, b)
|
652
|
+
|
653
|
+
register_symbol(_convert_sum, {'fricas': 'sum'}, 2)
|
654
|
+
register_symbol(_convert_prod, {'fricas': 'product'}, 2)
|
655
|
+
|
656
|
+
def explicitly_not_implemented(*args):
|
657
|
+
raise NotImplementedError("the translation of the FriCAS Expression '%s' to sage is not yet implemented" % args)
|
658
|
+
|
659
|
+
register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}, 2) # to be removed once we fully on FriCAS 1.3.10+
|
660
|
+
register_symbol(lambda *args: explicitly_not_implemented("FEseries"), {'fricas': 'FEseries'}, 2)
|
661
|
+
register_symbol(lambda *args: explicitly_not_implemented("rootOfRec"), {'fricas': 'rootOfRec'}, 2)
|
662
|
+
|
663
|
+
def set(self, var, value):
|
664
|
+
"""
|
665
|
+
Set a variable to a value in FriCAS.
|
666
|
+
|
667
|
+
INPUT:
|
668
|
+
|
669
|
+
- ``var``, ``value`` -- strings; the first representing a valid
|
670
|
+
FriCAS variable identifier, the second a FriCAS expression
|
671
|
+
|
672
|
+
OUTPUT: none
|
673
|
+
|
674
|
+
EXAMPLES::
|
675
|
+
|
676
|
+
sage: fricas.set('xx', '2')
|
677
|
+
sage: fricas.get('xx')
|
678
|
+
'2'
|
679
|
+
"""
|
680
|
+
cmd = '%s%s%s;' % (var, self._assign_symbol(), value)
|
681
|
+
output = self.eval(cmd, reformat=False)
|
682
|
+
self._check_errors(value, output)
|
683
|
+
|
684
|
+
def get(self, var):
|
685
|
+
r"""
|
686
|
+
Get the string representation of the value (more precisely, the
|
687
|
+
OutputForm) of a variable or expression in FriCAS.
|
688
|
+
|
689
|
+
If FriCAS cannot evaluate `var` an error is raised.
|
690
|
+
|
691
|
+
EXAMPLES::
|
692
|
+
|
693
|
+
sage: fricas.set('xx', '2')
|
694
|
+
sage: fricas.get('xx')
|
695
|
+
'2'
|
696
|
+
sage: a = fricas('(1 + sqrt(2))^5')
|
697
|
+
sage: fricas.get(a.name())
|
698
|
+
' +-+\n29 \\|2 + 41'
|
699
|
+
sage: fricas.get('(1 + sqrt(2))^5')
|
700
|
+
' +-+\n29 \\|2 + 41'
|
701
|
+
sage: fricas.new('(1 + sqrt(2))^5')
|
702
|
+
+-+
|
703
|
+
29 \|2 + 41
|
704
|
+
"""
|
705
|
+
output = self.eval(str(var), reformat=False)
|
706
|
+
# if there is AlgebraOutput we ask no more
|
707
|
+
m = re.search(r"\|startAlgebraOutput\|\n(.*)\n\|endOfAlgebraOutput\|",
|
708
|
+
output, flags=re.DOTALL)
|
709
|
+
if m:
|
710
|
+
lines = m.groups()[0].split("\n")
|
711
|
+
if max(len(line) for line in lines) < FRICAS_LINE_LENGTH:
|
712
|
+
return "\n".join(line[FRICAS_SINGLE_LINE_START:] for line in lines)
|
713
|
+
else:
|
714
|
+
return "\n".join(line[FRICAS_MULTI_LINE_START:] for line in lines)
|
715
|
+
|
716
|
+
self._check_errors(var, output)
|
717
|
+
|
718
|
+
def get_string(self, var):
|
719
|
+
"""
|
720
|
+
Return the value of a FriCAS string as a string, without checking
|
721
|
+
that it is a string.
|
722
|
+
|
723
|
+
TESTS:
|
724
|
+
|
725
|
+
We test that strings are returned properly::
|
726
|
+
|
727
|
+
sage: r = fricas.get_string('concat([concat(string(i)," ") for i in 0..299])')
|
728
|
+
sage: r == " ".join(str(i) for i in range(300)) + ' '
|
729
|
+
True
|
730
|
+
|
731
|
+
sage: fricas.get_string('concat([string(1) for i in 1..5])') == "1"*5
|
732
|
+
True
|
733
|
+
|
734
|
+
sage: fricas.get_string('concat([string(1) for i in 1..10000])') == "1"*10000
|
735
|
+
True
|
736
|
+
|
737
|
+
A problem with leading space::
|
738
|
+
|
739
|
+
sage: s = "unparse((-1234567890123456789012345678901234567890123456789012345678901234567890*n::EXPR INT)::INFORM)"
|
740
|
+
sage: fricas.get_string(s)
|
741
|
+
'(-1234567890123456789012345678901234567890123456789012345678901234567890)*n'
|
742
|
+
|
743
|
+
Check that :issue:`25628` is fixed::
|
744
|
+
|
745
|
+
sage: var("a b"); f = 1/(1+a*cos(x))
|
746
|
+
(a, b)
|
747
|
+
sage: lF = integrate(f, x, algorithm='fricas')
|
748
|
+
sage: (diff(lF[0], x) - f).simplify_trig()
|
749
|
+
0
|
750
|
+
sage: (diff(lF[1], x) - f).simplify_trig()
|
751
|
+
0
|
752
|
+
sage: f = 1/(b*x^2+a); lF = integrate(f, x, algorithm='fricas'); lF
|
753
|
+
[1/2*log((2*a*b*x + (b*x^2 - a)*sqrt(-a*b))/(b*x^2 + a))/sqrt(-a*b),
|
754
|
+
arctan(sqrt(a*b)*x/a)/sqrt(a*b)]
|
755
|
+
sage: (diff(lF[0], x) - f).simplify_trig()
|
756
|
+
0
|
757
|
+
sage: (diff(lF[1], x) - f).simplify_trig()
|
758
|
+
0
|
759
|
+
"""
|
760
|
+
# strip removes leading and trailing whitespace, after that
|
761
|
+
# we can assume that the first and the last character are
|
762
|
+
# double quotes
|
763
|
+
return self.get(str(var)).replace("\n", "").strip()[1:-1]
|
764
|
+
|
765
|
+
def get_integer(self, var):
|
766
|
+
"""
|
767
|
+
Return the value of a FriCAS integer as an integer, without
|
768
|
+
checking that it is an integer.
|
769
|
+
|
770
|
+
TESTS::
|
771
|
+
|
772
|
+
sage: fricas.get_integer('factorial 1111') == factorial(1111)
|
773
|
+
True
|
774
|
+
"""
|
775
|
+
return int(self.get_unparsed_InputForm(str(var)))
|
776
|
+
|
777
|
+
def get_boolean(self, var):
|
778
|
+
"""
|
779
|
+
Return the value of a FriCAS boolean as a boolean, without checking
|
780
|
+
that it is a boolean.
|
781
|
+
|
782
|
+
TESTS::
|
783
|
+
|
784
|
+
sage: fricas.get_boolean('(1=1)::Boolean') == True
|
785
|
+
True
|
786
|
+
|
787
|
+
sage: fricas.get_boolean('(1=2)::Boolean') == False
|
788
|
+
True
|
789
|
+
"""
|
790
|
+
return self.get(str(var)).replace("\n", "") == "true"
|
791
|
+
|
792
|
+
def get_unparsed_InputForm(self, var):
|
793
|
+
"""
|
794
|
+
Return the unparsed ``InputForm`` as a string.
|
795
|
+
|
796
|
+
.. TODO::
|
797
|
+
|
798
|
+
- catch errors, especially when InputForm is not available:
|
799
|
+
|
800
|
+
- for example when integration returns ``'failed'``
|
801
|
+
|
802
|
+
- ``UnivariatePolynomial``
|
803
|
+
|
804
|
+
- should we provide workarounds, too?
|
805
|
+
|
806
|
+
TESTS::
|
807
|
+
|
808
|
+
sage: fricas.get_unparsed_InputForm('1..3')
|
809
|
+
'(1..3)$Segment(PositiveInteger())'
|
810
|
+
"""
|
811
|
+
return self.get_string('unparse((%s)::InputForm)' % var)
|
812
|
+
|
813
|
+
def get_InputForm(self, var):
|
814
|
+
"""
|
815
|
+
Return the ``InputForm`` as a string.
|
816
|
+
|
817
|
+
TESTS::
|
818
|
+
|
819
|
+
sage: fricas.get_InputForm('1..3')
|
820
|
+
'(($elt (Segment (PositiveInteger)) SEGMENT) 1 3)'
|
821
|
+
"""
|
822
|
+
return self.get_string('sageprint((%s)::InputForm)' % str(var))
|
823
|
+
|
824
|
+
def _assign_symbol(self):
|
825
|
+
"""
|
826
|
+
Return the symbol used for setting a variable in FriCAS.
|
827
|
+
|
828
|
+
EXAMPLES::
|
829
|
+
|
830
|
+
sage: fricas.set("x", "1"); # indirect doctest
|
831
|
+
sage: fricas.get("x")
|
832
|
+
'1'
|
833
|
+
sage: fricas.eval(")cl val x")
|
834
|
+
''
|
835
|
+
"""
|
836
|
+
return ":="
|
837
|
+
|
838
|
+
def _equality_symbol(self):
|
839
|
+
"""
|
840
|
+
Return the equality testing logical symbol in FriCAS.
|
841
|
+
|
842
|
+
EXAMPLES::
|
843
|
+
|
844
|
+
sage: a = fricas(x==6); a # indirect doctest
|
845
|
+
x = 6
|
846
|
+
|
847
|
+
A warning::
|
848
|
+
|
849
|
+
sage: fricas.set("x", 2);
|
850
|
+
sage: a = fricas(x==6); a
|
851
|
+
2 = 6
|
852
|
+
sage: fricas.eval(")cl val x")
|
853
|
+
''
|
854
|
+
"""
|
855
|
+
return "="
|
856
|
+
|
857
|
+
def _true_symbol(self):
|
858
|
+
"""
|
859
|
+
Return the string used for ``True`` in FriCAS.
|
860
|
+
|
861
|
+
EXAMPLES::
|
862
|
+
|
863
|
+
sage: str(fricas("(1=1)@Boolean")) == fricas._true_symbol()
|
864
|
+
True
|
865
|
+
"""
|
866
|
+
return "true"
|
867
|
+
|
868
|
+
def _false_symbol(self):
|
869
|
+
"""
|
870
|
+
Return the string used for ``False`` in FriCAS.
|
871
|
+
|
872
|
+
EXAMPLES::
|
873
|
+
|
874
|
+
sage: str(fricas("(1~=1)@Boolean")) == fricas._false_symbol()
|
875
|
+
True
|
876
|
+
"""
|
877
|
+
return "false"
|
878
|
+
|
879
|
+
def _inequality_symbol(self):
|
880
|
+
"""
|
881
|
+
Return the string used for False in FriCAS.
|
882
|
+
|
883
|
+
EXAMPLES::
|
884
|
+
|
885
|
+
sage: fricas(x!=0) # indirect doctest
|
886
|
+
true
|
887
|
+
"""
|
888
|
+
return '~='
|
889
|
+
|
890
|
+
def _repr_(self):
|
891
|
+
"""
|
892
|
+
EXAMPLES::
|
893
|
+
|
894
|
+
sage: fricas # indirect doctest
|
895
|
+
FriCAS
|
896
|
+
"""
|
897
|
+
return "FriCAS"
|
898
|
+
|
899
|
+
def __reduce__(self):
|
900
|
+
"""
|
901
|
+
EXAMPLES::
|
902
|
+
|
903
|
+
sage: FriCAS().__reduce__()
|
904
|
+
(<function reduce_load_fricas at 0x...>, ())
|
905
|
+
sage: f, args = _
|
906
|
+
sage: f(*args)
|
907
|
+
FriCAS
|
908
|
+
"""
|
909
|
+
return reduce_load_fricas, tuple([])
|
910
|
+
|
911
|
+
def eval(self, code, strip=True, synchronize=False, locals=None, allow_use_file=True,
|
912
|
+
split_lines='nofile', reformat=True, **kwds):
|
913
|
+
"""
|
914
|
+
Evaluate ``code`` using FriCAS.
|
915
|
+
|
916
|
+
Except ``reformat``, all arguments are passed to
|
917
|
+
:meth:`sage.interfaces.expect.Expect.eval`.
|
918
|
+
|
919
|
+
INPUT:
|
920
|
+
|
921
|
+
- ``reformat`` -- boolean; remove the output markers when True
|
922
|
+
|
923
|
+
This can also be used to pass system commands to FriCAS.
|
924
|
+
|
925
|
+
EXAMPLES::
|
926
|
+
|
927
|
+
sage: fricas.set("x", "1783"); fricas("x")
|
928
|
+
1783
|
929
|
+
sage: fricas.eval(")cl val x");
|
930
|
+
''
|
931
|
+
sage: fricas("x")
|
932
|
+
x
|
933
|
+
"""
|
934
|
+
output = Expect.eval(self, code, strip=strip,
|
935
|
+
synchronize=synchronize, locals=locals,
|
936
|
+
allow_use_file=allow_use_file, split_lines=split_lines,
|
937
|
+
**kwds)
|
938
|
+
# we remove carriage returns (\r) to make parsing easier
|
939
|
+
# they are sent depending on how fricas was invoked:
|
940
|
+
# on linux, "fricas -nox -noclef" sends "\r\n" and "fricas -nosman" sends "\n"
|
941
|
+
output = output.replace('\r', '')
|
942
|
+
if reformat:
|
943
|
+
replacements = [('|startAlgebraOutput|\n', ''),
|
944
|
+
('|endOfAlgebraOutput|', ''),
|
945
|
+
('|startKeyedMsg|\n', ''),
|
946
|
+
('|endOfKeyedMsg|', '')]
|
947
|
+
for old, new in replacements:
|
948
|
+
output = output.replace(old, new)
|
949
|
+
|
950
|
+
return output
|
951
|
+
|
952
|
+
def _function_class(self):
|
953
|
+
"""
|
954
|
+
Return the FriCASExpectFunction class.
|
955
|
+
|
956
|
+
EXAMPLES::
|
957
|
+
|
958
|
+
sage: fricas._function_class()
|
959
|
+
<class 'sage.interfaces.fricas.FriCASExpectFunction'>
|
960
|
+
sage: type(fricas.gcd)
|
961
|
+
<class 'sage.interfaces.fricas.FriCASExpectFunction'>
|
962
|
+
"""
|
963
|
+
return FriCASExpectFunction
|
964
|
+
|
965
|
+
def _object_class(self):
|
966
|
+
"""
|
967
|
+
EXAMPLES::
|
968
|
+
|
969
|
+
sage: fricas._object_class()
|
970
|
+
<class 'sage.interfaces.fricas.FriCASElement'>
|
971
|
+
sage: type(fricas(2))
|
972
|
+
<class 'sage.interfaces.fricas.FriCASElement'>
|
973
|
+
"""
|
974
|
+
return FriCASElement
|
975
|
+
|
976
|
+
def _function_element_class(self):
|
977
|
+
"""
|
978
|
+
Return the FriCAS function element class.
|
979
|
+
|
980
|
+
EXAMPLES::
|
981
|
+
|
982
|
+
sage: fricas._function_element_class()
|
983
|
+
<class 'sage.interfaces.fricas.FriCASFunctionElement'>
|
984
|
+
sage: type(fricas(2).gcd)
|
985
|
+
<class 'sage.interfaces.fricas.FriCASFunctionElement'>
|
986
|
+
"""
|
987
|
+
return FriCASFunctionElement
|
988
|
+
|
989
|
+
def console(self):
|
990
|
+
"""
|
991
|
+
Spawn a new FriCAS command-line session.
|
992
|
+
|
993
|
+
EXAMPLES::
|
994
|
+
|
995
|
+
sage: fricas.console() # not tested
|
996
|
+
FriCAS (AXIOM fork) Computer Algebra System
|
997
|
+
Version: FriCAS 1.0.5
|
998
|
+
Timestamp: Thursday February 19, 2009 at 06:57:33
|
999
|
+
-----------------------------------------------------------------------------
|
1000
|
+
Issue )copyright to view copyright notices.
|
1001
|
+
Issue )summary for a summary of useful system commands.
|
1002
|
+
Issue )quit to leave AXIOM and return to shell.
|
1003
|
+
-----------------------------------------------------------------------------
|
1004
|
+
"""
|
1005
|
+
fricas_console()
|
1006
|
+
|
1007
|
+
|
1008
|
+
@instancedoc
|
1009
|
+
class FriCASElement(ExpectElement, sage.interfaces.abc.FriCASElement):
|
1010
|
+
"""
|
1011
|
+
Instances of this class represent objects in FriCAS.
|
1012
|
+
|
1013
|
+
Using the method :meth:`sage` we can translate some of them to
|
1014
|
+
SageMath objects:
|
1015
|
+
|
1016
|
+
.. automethod:: _sage_
|
1017
|
+
"""
|
1018
|
+
def __len__(self):
|
1019
|
+
"""
|
1020
|
+
Return the length of a list.
|
1021
|
+
|
1022
|
+
EXAMPLES::
|
1023
|
+
|
1024
|
+
sage: v = fricas('[x^i for i in 0..5]')
|
1025
|
+
sage: len(v)
|
1026
|
+
6
|
1027
|
+
|
1028
|
+
TESTS:
|
1029
|
+
|
1030
|
+
Streams are not handled yet::
|
1031
|
+
|
1032
|
+
sage: oh = fricas('[i for i in 1..]')
|
1033
|
+
sage: len(oh)
|
1034
|
+
Traceback (most recent call last):
|
1035
|
+
...
|
1036
|
+
TypeError: ...
|
1037
|
+
"""
|
1038
|
+
P = self._check_valid()
|
1039
|
+
l = P('#(%s)' % self._name)
|
1040
|
+
return l.sage()
|
1041
|
+
|
1042
|
+
def __iter__(self):
|
1043
|
+
"""
|
1044
|
+
Return an iterator over ``self``.
|
1045
|
+
|
1046
|
+
EXAMPLES::
|
1047
|
+
|
1048
|
+
sage: L = fricas([4,5,6])
|
1049
|
+
sage: list(L)
|
1050
|
+
[4, 5, 6]
|
1051
|
+
|
1052
|
+
TESTS:
|
1053
|
+
|
1054
|
+
Streams are not handled yet::
|
1055
|
+
|
1056
|
+
sage: oh = fricas('[i for i in 1..]')
|
1057
|
+
sage: next(iter(oh)) # known bug
|
1058
|
+
"""
|
1059
|
+
for i in range(len(self)):
|
1060
|
+
yield self[i]
|
1061
|
+
|
1062
|
+
def __getitem__(self, n):
|
1063
|
+
"""
|
1064
|
+
We implement the sage conventions here, translating to 0-based iterables.
|
1065
|
+
|
1066
|
+
We do not check validity, since many objects in FriCAS are
|
1067
|
+
iterable, in particular Streams
|
1068
|
+
|
1069
|
+
TESTS::
|
1070
|
+
|
1071
|
+
sage: fricas("[1,2,3]")[0]
|
1072
|
+
1
|
1073
|
+
|
1074
|
+
Negative indices do work::
|
1075
|
+
|
1076
|
+
sage: fricas("[1,2,3]")[-1]
|
1077
|
+
3
|
1078
|
+
|
1079
|
+
sage: fricas("[1,2,3]")[-2]
|
1080
|
+
2
|
1081
|
+
|
1082
|
+
Invalid indices raise exceptions::
|
1083
|
+
|
1084
|
+
sage: fricas("[1,2,3]")[3]
|
1085
|
+
Traceback (most recent call last):
|
1086
|
+
...
|
1087
|
+
TypeError: An error occurred when FriCAS evaluated 'elt(...,...)':
|
1088
|
+
<BLANKLINE>
|
1089
|
+
>> Error detected within library code:
|
1090
|
+
index out of range
|
1091
|
+
|
1092
|
+
And streams are ok too::
|
1093
|
+
|
1094
|
+
sage: oh = fricas('[i for i in 1..]')
|
1095
|
+
sage: oh[4]
|
1096
|
+
5
|
1097
|
+
"""
|
1098
|
+
n = int(n)
|
1099
|
+
P = self._check_valid()
|
1100
|
+
if n < -1:
|
1101
|
+
try:
|
1102
|
+
l = len(self)
|
1103
|
+
except TypeError:
|
1104
|
+
raise
|
1105
|
+
else:
|
1106
|
+
n += l
|
1107
|
+
if not (0 <= n < l):
|
1108
|
+
raise IndexError("index out of range")
|
1109
|
+
# use "elt" instead of "." here because then the error
|
1110
|
+
# message is clearer
|
1111
|
+
if n == -1:
|
1112
|
+
return P.new("elt(%s,last)" % (self._name))
|
1113
|
+
return P.new("elt(%s,%s)" % (self._name, n + 1))
|
1114
|
+
|
1115
|
+
def __int__(self):
|
1116
|
+
"""
|
1117
|
+
TESTS::
|
1118
|
+
|
1119
|
+
sage: int(fricas(2))
|
1120
|
+
2
|
1121
|
+
"""
|
1122
|
+
return int(self.sage())
|
1123
|
+
|
1124
|
+
def bool(self):
|
1125
|
+
"""
|
1126
|
+
Coerce the expression into a boolean.
|
1127
|
+
|
1128
|
+
EXAMPLES::
|
1129
|
+
|
1130
|
+
sage: fricas("1=1").bool()
|
1131
|
+
True
|
1132
|
+
sage: fricas("1~=1").bool()
|
1133
|
+
False
|
1134
|
+
"""
|
1135
|
+
P = self._check_valid()
|
1136
|
+
return P.new(self._name + "::Boolean").sage()
|
1137
|
+
|
1138
|
+
def __bool__(self):
|
1139
|
+
"""
|
1140
|
+
Check whether the expression is different from zero.
|
1141
|
+
|
1142
|
+
EXAMPLES::
|
1143
|
+
|
1144
|
+
sage: fricas(0).is_zero() # indirect doctest
|
1145
|
+
True
|
1146
|
+
"""
|
1147
|
+
P = self._check_valid()
|
1148
|
+
return not P.new("zero?(%s)" % self._name).sage()
|
1149
|
+
|
1150
|
+
def __float__(self):
|
1151
|
+
"""
|
1152
|
+
TESTS::
|
1153
|
+
|
1154
|
+
sage: float(fricas(2))
|
1155
|
+
2.0
|
1156
|
+
"""
|
1157
|
+
return float(self.sage())
|
1158
|
+
|
1159
|
+
def _integer_(self, ZZ=None):
|
1160
|
+
"""
|
1161
|
+
EXAMPLES::
|
1162
|
+
|
1163
|
+
sage: ZZ(fricas('1'))
|
1164
|
+
1
|
1165
|
+
"""
|
1166
|
+
return ZZ(self.sage())
|
1167
|
+
|
1168
|
+
def _rational_(self):
|
1169
|
+
"""
|
1170
|
+
EXAMPLES::
|
1171
|
+
|
1172
|
+
sage: QQ(fricas('-1/2'))
|
1173
|
+
-1/2
|
1174
|
+
"""
|
1175
|
+
return QQ(self.sage())
|
1176
|
+
|
1177
|
+
def gen(self, n):
|
1178
|
+
"""
|
1179
|
+
Return an error, since the n-th generator in FriCAS is not well defined.
|
1180
|
+
"""
|
1181
|
+
raise NotImplementedError
|
1182
|
+
|
1183
|
+
def _latex_(self):
|
1184
|
+
r"""
|
1185
|
+
EXAMPLES::
|
1186
|
+
|
1187
|
+
sage: latex(fricas("sin(x+y)/exp(z)*log(1+%e)"))
|
1188
|
+
\frac{{{\log \left( {{e+1}} \right)} \ {\sin \left( {{y+x}} \right)}}}{{{e} ^{z}}}
|
1189
|
+
|
1190
|
+
sage: latex(fricas("matrix([[1,2],[3,4]])"))
|
1191
|
+
\left[ \begin{array}{cc} 1 & 2 \\ 3 & 4...\end{array}...\right]
|
1192
|
+
|
1193
|
+
sage: latex(fricas("integrate(sin(x+1/x),x)"))
|
1194
|
+
\int ^{\displaystyle x} {{\sin \left( {{\frac{{{{ \%...} ^{2}}+1}}{ \%...}}} \right)} \ {d \%...}}
|
1195
|
+
"""
|
1196
|
+
replacements = [(r'\sp ', '^'),
|
1197
|
+
(r'\sp{', '^{'),
|
1198
|
+
(r'\sb ', '_'),
|
1199
|
+
(r'\sb{', '_{')]
|
1200
|
+
P = self._check_valid()
|
1201
|
+
s = P.get_string("latex(%s)" % self._name)
|
1202
|
+
for old, new in replacements:
|
1203
|
+
s = s.replace(old, new)
|
1204
|
+
return s
|
1205
|
+
|
1206
|
+
def _get_sage_type(self, domain):
|
1207
|
+
"""
|
1208
|
+
INPUT:
|
1209
|
+
|
1210
|
+
- ``domain`` -- a FriCAS SExpression
|
1211
|
+
|
1212
|
+
OUTPUT: a corresponding Sage type
|
1213
|
+
|
1214
|
+
EXAMPLES::
|
1215
|
+
|
1216
|
+
sage: m = fricas("dom(1/2)::Any")
|
1217
|
+
sage: fricas(0)._get_sage_type(m)
|
1218
|
+
Rational Field
|
1219
|
+
|
1220
|
+
TESTS::
|
1221
|
+
|
1222
|
+
sage: m = fricas("UP(y, UP(x, AN))::INFORM")
|
1223
|
+
sage: fricas(0)._get_sage_type(m)
|
1224
|
+
Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Algebraic Field
|
1225
|
+
"""
|
1226
|
+
from sage.rings.qqbar import QQbar
|
1227
|
+
from sage.rings.real_double import RDF
|
1228
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
1229
|
+
from sage.rings.fraction_field import FractionField
|
1230
|
+
from sage.rings.finite_rings.integer_mod_ring import Integers
|
1231
|
+
from sage.rings.finite_rings.finite_field_constructor import FiniteField
|
1232
|
+
from sage.rings.real_mpfr import RealField
|
1233
|
+
from sage.symbolic.ring import SR
|
1234
|
+
|
1235
|
+
# first implement domains without arguments
|
1236
|
+
head = str(domain.car())
|
1237
|
+
if head in ["Integer", "NonNegativeInteger", "PositiveInteger"]:
|
1238
|
+
return ZZ
|
1239
|
+
if head == "String":
|
1240
|
+
return str
|
1241
|
+
if head == "Float":
|
1242
|
+
P = self._check_valid()
|
1243
|
+
prec = max(P.new("length mantissa(%s)" % self._name).sage(), 53)
|
1244
|
+
return RealField(prec)
|
1245
|
+
if head == "DoubleFloat":
|
1246
|
+
return RDF
|
1247
|
+
if head == "AlgebraicNumber":
|
1248
|
+
return QQbar
|
1249
|
+
|
1250
|
+
# now implement "functorial" types
|
1251
|
+
if head == "OrderedCompletion" or head == "Complex":
|
1252
|
+
# this is a workaround, I don't know how translate this
|
1253
|
+
return SR
|
1254
|
+
|
1255
|
+
if head == "IntegerMod":
|
1256
|
+
return Integers(domain[1].integer().sage())
|
1257
|
+
|
1258
|
+
if head == "PrimeField":
|
1259
|
+
return FiniteField(domain[1].integer().sage())
|
1260
|
+
|
1261
|
+
if head == "Fraction":
|
1262
|
+
return FractionField(self._get_sage_type(domain[1]))
|
1263
|
+
|
1264
|
+
if head == "Expression":
|
1265
|
+
return SR
|
1266
|
+
|
1267
|
+
if head == "Polynomial":
|
1268
|
+
# this is a workaround, since in sage we always have to specify the variables
|
1269
|
+
return SR
|
1270
|
+
|
1271
|
+
if head == "UnivariatePolynomial":
|
1272
|
+
var = str(domain[1])
|
1273
|
+
return PolynomialRing(self._get_sage_type(domain[2]), var)
|
1274
|
+
|
1275
|
+
raise NotImplementedError("the translation of FriCAS type %s to sage is not yet implemented" % domain)
|
1276
|
+
|
1277
|
+
_WHITESPACE = " "
|
1278
|
+
_LEFTBRACKET = "("
|
1279
|
+
_RIGHTBRACKET = ")"
|
1280
|
+
_STRINGMARKER = '"'
|
1281
|
+
_ESCAPEMARKER = '_' # STRINGMARKER must be escaped in strings
|
1282
|
+
|
1283
|
+
@staticmethod
|
1284
|
+
def _parse_and_eval(s, start=0):
|
1285
|
+
"""
|
1286
|
+
Parse and evaluate the string.
|
1287
|
+
|
1288
|
+
INPUT:
|
1289
|
+
|
1290
|
+
- ``s`` -- string
|
1291
|
+
- ``start`` -- integer; specifies where to start parsing
|
1292
|
+
|
1293
|
+
OUTPUT:
|
1294
|
+
|
1295
|
+
- a pair ``(L, end)``, where ``L`` is the parsed list and
|
1296
|
+
``end`` is the position of the last parsed letter.
|
1297
|
+
|
1298
|
+
TESTS::
|
1299
|
+
|
1300
|
+
sage: from sage.interfaces.fricas import FriCASElement
|
1301
|
+
sage: FriCASElement._parse_and_eval("abc")
|
1302
|
+
(abc, 2)
|
1303
|
+
|
1304
|
+
sage: FriCASElement._parse_and_eval("(asin c)")
|
1305
|
+
(arcsin(c), 7)
|
1306
|
+
|
1307
|
+
sage: N(FriCASElement._parse_and_eval("(pi)")[0])
|
1308
|
+
3.14159265358979
|
1309
|
+
|
1310
|
+
sage: FriCASElement._parse_and_eval('(a "(b c)")')
|
1311
|
+
Traceback (most recent call last):
|
1312
|
+
...
|
1313
|
+
TypeError: cannot coerce arguments: no canonical coercion from <class 'str'> to Symbolic Ring
|
1314
|
+
"""
|
1315
|
+
a = start
|
1316
|
+
while s[a] in FriCASElement._WHITESPACE:
|
1317
|
+
a += 1
|
1318
|
+
|
1319
|
+
if s[a] == FriCASElement._LEFTBRACKET:
|
1320
|
+
return FriCASElement._parse_list(s, start=a)
|
1321
|
+
elif s[a] == FriCASElement._STRINGMARKER:
|
1322
|
+
return FriCASElement._parse_string(s, start=a)
|
1323
|
+
else:
|
1324
|
+
return FriCASElement._parse_other(s, start=a)
|
1325
|
+
|
1326
|
+
@staticmethod
|
1327
|
+
def _parse_list(s, start=0):
|
1328
|
+
"""
|
1329
|
+
Parse the initial part of a string, assuming that it is a
|
1330
|
+
whitespace separated list, treating its first element as
|
1331
|
+
function and the rest as arguments.
|
1332
|
+
|
1333
|
+
INPUT:
|
1334
|
+
|
1335
|
+
- ``s`` -- string
|
1336
|
+
- ``start`` -- integer; specifies the position of the left
|
1337
|
+
bracket
|
1338
|
+
|
1339
|
+
TESTS::
|
1340
|
+
|
1341
|
+
sage: from sage.interfaces.fricas import FriCASElement
|
1342
|
+
sage: FriCASElement._parse_list("()")
|
1343
|
+
Traceback (most recent call last):
|
1344
|
+
...
|
1345
|
+
TypeError: 'tuple' object is not callable
|
1346
|
+
|
1347
|
+
sage: FriCASElement._parse_list("(a b c)")
|
1348
|
+
(a(b, c), 6)
|
1349
|
+
|
1350
|
+
sage: FriCASElement._parse_list('(bcd)')
|
1351
|
+
(bcd(), 4)
|
1352
|
+
"""
|
1353
|
+
a = start
|
1354
|
+
assert s[a] == FriCASElement._LEFTBRACKET
|
1355
|
+
a += 1
|
1356
|
+
# the first element of a list must be a function call
|
1357
|
+
fun_string, a = FriCASElement._parse_other(s, start=a, make_fun=True)
|
1358
|
+
a += 1
|
1359
|
+
# now parse the arguments
|
1360
|
+
args = []
|
1361
|
+
while s[a] != FriCASElement._RIGHTBRACKET:
|
1362
|
+
e, a = FriCASElement._parse_and_eval(s, start=a)
|
1363
|
+
args.append(e)
|
1364
|
+
a += 1
|
1365
|
+
# then lookup for the function with the given number of arguments
|
1366
|
+
try:
|
1367
|
+
fun = symbol_table["fricas"][(fun_string, len(args))]
|
1368
|
+
except KeyError:
|
1369
|
+
try:
|
1370
|
+
# to handle the case of "construct"
|
1371
|
+
fun = symbol_table["fricas"][(fun_string, -1)]
|
1372
|
+
except KeyError:
|
1373
|
+
fun = function(fun_string)
|
1374
|
+
return fun(*args), a
|
1375
|
+
|
1376
|
+
@staticmethod
|
1377
|
+
def _parse_other(s, start=0, make_fun=False):
|
1378
|
+
"""
|
1379
|
+
Parse the initial part of a string, assuming that it is an
|
1380
|
+
atom, but not a string.
|
1381
|
+
|
1382
|
+
Symbols and numbers must not contain ``FriCASElement._WHITESPACE`` and
|
1383
|
+
``FriCASElement._RIGHTBRACKET``.
|
1384
|
+
|
1385
|
+
INPUT:
|
1386
|
+
|
1387
|
+
- ``s`` -- string
|
1388
|
+
- ``start`` -- integer; specifies where the symbol begins
|
1389
|
+
- ``make_fun`` -- boolean (default: ``False``); whether the atom should
|
1390
|
+
be interpreted as a function call
|
1391
|
+
|
1392
|
+
TESTS::
|
1393
|
+
|
1394
|
+
sage: from sage.interfaces.fricas import FriCASElement
|
1395
|
+
sage: FriCASElement._parse_other("abc")
|
1396
|
+
(abc, 2)
|
1397
|
+
sage: FriCASElement._parse_other("123 xyz")
|
1398
|
+
(123, 2)
|
1399
|
+
sage: FriCASElement._parse_other("abc -1.23", 4)
|
1400
|
+
(-1.23, 8)
|
1401
|
+
|
1402
|
+
This function cannot use the symbol table to translate
|
1403
|
+
symbols which are not function calls, as :issue:`31849` shows
|
1404
|
+
``D`` would erroneously be interpreted as differential
|
1405
|
+
then::
|
1406
|
+
|
1407
|
+
sage: var("D")
|
1408
|
+
D
|
1409
|
+
sage: integrate(D/x, x, algorithm='fricas')
|
1410
|
+
D*log(x)
|
1411
|
+
|
1412
|
+
However, it does have to check for constants, for example
|
1413
|
+
``%pi``::
|
1414
|
+
|
1415
|
+
sage: FriCASElement._parse_other("%pi")
|
1416
|
+
(pi, 2)
|
1417
|
+
"""
|
1418
|
+
a = start
|
1419
|
+
b = len(s)
|
1420
|
+
while a < b and s[a] not in FriCASElement._WHITESPACE and s[a] != FriCASElement._RIGHTBRACKET:
|
1421
|
+
a += 1
|
1422
|
+
|
1423
|
+
e = s[start:a]
|
1424
|
+
if make_fun:
|
1425
|
+
pass
|
1426
|
+
else:
|
1427
|
+
try:
|
1428
|
+
e = ZZ(e)
|
1429
|
+
except TypeError:
|
1430
|
+
try:
|
1431
|
+
e = float(e)
|
1432
|
+
except ValueError:
|
1433
|
+
try:
|
1434
|
+
e = FRICAS_CONSTANTS[e]
|
1435
|
+
except KeyError:
|
1436
|
+
e = var(e.replace("%", "_"))
|
1437
|
+
return e, a - 1
|
1438
|
+
|
1439
|
+
@staticmethod
|
1440
|
+
def _parse_string(s, start=0):
|
1441
|
+
r"""
|
1442
|
+
Parse the initial part of a string, assuming that it represents a
|
1443
|
+
string.
|
1444
|
+
|
1445
|
+
INPUT:
|
1446
|
+
|
1447
|
+
- ``s`` -- string
|
1448
|
+
- ``start`` -- integer; specifies the position of the left
|
1449
|
+
quote
|
1450
|
+
|
1451
|
+
TESTS::
|
1452
|
+
|
1453
|
+
sage: from sage.interfaces.fricas import FriCASElement
|
1454
|
+
sage: FriCASElement._parse_string('"abc" 123')
|
1455
|
+
('abc', 4)
|
1456
|
+
|
1457
|
+
sage: FriCASElement._parse_string('"" 123')
|
1458
|
+
('', 1)
|
1459
|
+
|
1460
|
+
sage: FriCASElement._parse_string('"____" 123')
|
1461
|
+
('__', 5)
|
1462
|
+
|
1463
|
+
sage: FriCASElement._parse_string('"_a" 123')
|
1464
|
+
('a', 3)
|
1465
|
+
|
1466
|
+
sage: FriCASElement._parse_string('"_" _"" 123')
|
1467
|
+
('" "', 6)
|
1468
|
+
|
1469
|
+
sage: FriCASElement._parse_string('"(b c)"')
|
1470
|
+
('(b c)', 6)
|
1471
|
+
"""
|
1472
|
+
a = start
|
1473
|
+
assert s[a] == FriCASElement._STRINGMARKER
|
1474
|
+
b = a + 1
|
1475
|
+
a = b
|
1476
|
+
S = []
|
1477
|
+
while s[b] != FriCASElement._STRINGMARKER:
|
1478
|
+
if s[b] == FriCASElement._ESCAPEMARKER:
|
1479
|
+
S.append(s[a:b])
|
1480
|
+
b += 1
|
1481
|
+
a = b
|
1482
|
+
b += 1
|
1483
|
+
S.append(s[a:b])
|
1484
|
+
return "".join(S), b
|
1485
|
+
|
1486
|
+
@staticmethod
|
1487
|
+
def _sage_expression(fricas_InputForm):
|
1488
|
+
r"""
|
1489
|
+
Convert an expression to an element of the Symbolic Ring.
|
1490
|
+
|
1491
|
+
INPUT:
|
1492
|
+
|
1493
|
+
- fricas_InputForm, a string, the InputForm of a FriCAS expression.
|
1494
|
+
|
1495
|
+
TESTS::
|
1496
|
+
|
1497
|
+
sage: f = fricas('integrate(sin(x^2), x)'); f
|
1498
|
+
+---+
|
1499
|
+
| 2
|
1500
|
+
fresnelS(x |--- )
|
1501
|
+
\|%pi
|
1502
|
+
-----------------
|
1503
|
+
+---+
|
1504
|
+
| 2
|
1505
|
+
|---
|
1506
|
+
\|%pi
|
1507
|
+
sage: s = fricas.get_InputForm(f._name); s
|
1508
|
+
'(/ (fresnelS (* x (^ (/ 2 (pi)) (/ 1 2)))) (^ (/ 2 (pi)) (/ 1 2)))'
|
1509
|
+
sage: from sage.interfaces.fricas import FriCASElement
|
1510
|
+
sage: FriCASElement._sage_expression(s)
|
1511
|
+
1/2*sqrt(2)*sqrt(pi)*fresnel_sin(sqrt(2)*x/sqrt(pi))
|
1512
|
+
|
1513
|
+
Check that :issue:`22525` is fixed::
|
1514
|
+
|
1515
|
+
sage: l = [sin, cos, sec, csc, cot, tan, asin, acos, atan, acot, acsc, asec, arcsin, arccos, arctan, arccot, arccsc, arcsec]
|
1516
|
+
sage: [f(x)._fricas_().sage().subs(x=0.9) for f in l]
|
1517
|
+
[0.783326909627483,
|
1518
|
+
0.621609968270664,
|
1519
|
+
1.60872581046605,
|
1520
|
+
1.27660621345890,
|
1521
|
+
0.793551147842317,
|
1522
|
+
1.26015821755034,
|
1523
|
+
1.11976951499863,
|
1524
|
+
0.451026811796262,
|
1525
|
+
0.732815101786507,
|
1526
|
+
0.837981225008390,
|
1527
|
+
1.57079632679490 - 0.467145308103262*I,
|
1528
|
+
0.467145308103262*I,
|
1529
|
+
1.11976951499863,
|
1530
|
+
0.451026811796262,
|
1531
|
+
0.732815101786507,
|
1532
|
+
0.837981225008390,
|
1533
|
+
1.57079632679490 - 0.467145308103262*I,
|
1534
|
+
0.467145308103262*I]
|
1535
|
+
sage: l = [tanh, sinh, cosh, coth, sech, csch, asinh, acosh, atanh, acoth, asech, acsch, arcsinh, arccosh, arctanh, arccoth, arcsech, arccsch]
|
1536
|
+
sage: [f(x)._fricas_().sage().subs(x=0.9) for f in l]
|
1537
|
+
[0.716297870199024,
|
1538
|
+
1.02651672570818,
|
1539
|
+
1.43308638544877,
|
1540
|
+
1.39606725303001,
|
1541
|
+
0.697794641100332,
|
1542
|
+
0.974168247780004,
|
1543
|
+
0.808866935652782,
|
1544
|
+
0.451026811796262*I,
|
1545
|
+
1.47221948958322,
|
1546
|
+
1.47221948958322 - 1.57079632679490*I,
|
1547
|
+
0.467145308103262,
|
1548
|
+
0.957800449200672,
|
1549
|
+
0.808866935652782,
|
1550
|
+
0.451026811796262*I,
|
1551
|
+
1.47221948958322,
|
1552
|
+
1.47221948958322 - 1.57079632679490*I,
|
1553
|
+
0.467145308103262,
|
1554
|
+
0.957800449200672]
|
1555
|
+
|
1556
|
+
Check that :issue:`23782` is fixed::
|
1557
|
+
|
1558
|
+
sage: s = '((3*n^10-25*n^9+50*n^8+62*n^7-229*n^6-25*n^5+320*n^4-12*n^3-144*n^2)/11520)::EXPR INT'
|
1559
|
+
sage: fricas(s).sage()
|
1560
|
+
1/3840*n^10 - 5/2304*n^9 + 5/1152*n^8 + 31/5760*n^7 - 229/11520*n^6 - 5/2304*n^5 + 1/36*n^4 - 1/960*n^3 - 1/80*n^2
|
1561
|
+
|
1562
|
+
Some checks for digamma and polygamma (:issue:`31853`)::
|
1563
|
+
|
1564
|
+
sage: fricas.digamma(1.0)
|
1565
|
+
- 0.5772156649_0153286061
|
1566
|
+
sage: psi(1.0)
|
1567
|
+
-0.577215664901533
|
1568
|
+
sage: fricas.polygamma(1, 1.0)
|
1569
|
+
1.644934066848226...
|
1570
|
+
sage: psi(1, 1).n()
|
1571
|
+
1.64493406684823
|
1572
|
+
|
1573
|
+
sage: var("w")
|
1574
|
+
w
|
1575
|
+
sage: fricas.laplace(log(x), x, w).sage()
|
1576
|
+
-(euler_gamma + log(w))/w
|
1577
|
+
sage: fricas(laplace(log(x), x, w)).sage()
|
1578
|
+
-(euler_gamma + log(w))/w
|
1579
|
+
|
1580
|
+
Check that :issue:`25224` is fixed::
|
1581
|
+
|
1582
|
+
sage: integrate(log(x)/(1-x),x,algorithm='fricas')
|
1583
|
+
dilog(-x + 1)
|
1584
|
+
sage: fricas(dilog(-x + 1))
|
1585
|
+
dilog(x)
|
1586
|
+
sage: dilog._fricas_()(1.0)
|
1587
|
+
1.6449340668_4822643647_24152
|
1588
|
+
sage: dilog(1.0)
|
1589
|
+
1.64493406684823
|
1590
|
+
|
1591
|
+
Check that :issue:`25987` is fixed::
|
1592
|
+
|
1593
|
+
sage: integrate(lambert_w(x), x, algorithm='fricas')
|
1594
|
+
(x*lambert_w(x)^2 - x*lambert_w(x) + x)/lambert_w(x)
|
1595
|
+
|
1596
|
+
Check that :issue:`25838` is fixed::
|
1597
|
+
|
1598
|
+
sage: F = function('f'); f = SR.var('f')
|
1599
|
+
sage: FF = fricas(F(f)); FF
|
1600
|
+
f(f)
|
1601
|
+
sage: FF.D(f).sage()
|
1602
|
+
diff(f(f), f)
|
1603
|
+
sage: bool(FF.D(f).integrate(f).sage() == F(f))
|
1604
|
+
True
|
1605
|
+
|
1606
|
+
Check that :issue:`25602` is fixed::
|
1607
|
+
|
1608
|
+
sage: r = fricas.integrate(72000/(1+x^5), x).sage()
|
1609
|
+
sage: abs(n(r.subs(x=5) - r.subs(x=3)) - 193.020947266210) <= 0.1
|
1610
|
+
True
|
1611
|
+
|
1612
|
+
sage: var("a"); r = fricas.integrate(72000*a^8/(a^5+x^5), x).sage()
|
1613
|
+
a
|
1614
|
+
sage: abs(n(r.subs(a=1, x=5) - r.subs(a=1, x=3)) - 193.020947266268) <= 0.1
|
1615
|
+
True
|
1616
|
+
|
1617
|
+
Check conversions of sums and products::
|
1618
|
+
|
1619
|
+
sage: var("k, m, n")
|
1620
|
+
(k, m, n)
|
1621
|
+
sage: fricas("sum(1/factorial(k), k=1..n)").sage()
|
1622
|
+
sum(1/factorial(_...), _..., 1, n)
|
1623
|
+
sage: fricas("eval(sum(x/k, k=1..n), x=k)").sage()
|
1624
|
+
k*harmonic_number(n)
|
1625
|
+
sage: fricas("product(1/factorial(k), k=1..n)").sage()
|
1626
|
+
1/product(factorial(_...), _..., 1, n)
|
1627
|
+
|
1628
|
+
sage: f = fricas.guess([sum(1/k, k,1,n) for n in range(10)])[0]; f
|
1629
|
+
n - 1
|
1630
|
+
--+ 1
|
1631
|
+
> -------
|
1632
|
+
--+ s + 1
|
1633
|
+
s = 0 10
|
1634
|
+
10
|
1635
|
+
|
1636
|
+
sage: f.sage()
|
1637
|
+
harmonic_number(n)
|
1638
|
+
|
1639
|
+
sage: f = fricas.guess([0, 1, 3, 9, 33])[0]; f
|
1640
|
+
s - 1
|
1641
|
+
n - 1 5
|
1642
|
+
--+ ++-++
|
1643
|
+
> | | p + 2
|
1644
|
+
--+ | | 4
|
1645
|
+
s = 0 p = 0
|
1646
|
+
5 4
|
1647
|
+
|
1648
|
+
sage: f.sage()
|
1649
|
+
sum(factorial(_... + 1), _..., 0, n - 1)
|
1650
|
+
|
1651
|
+
Check that :issue:`26746` is fixed::
|
1652
|
+
|
1653
|
+
sage: _ = var('x, y, z')
|
1654
|
+
sage: f = sin(x^2) + y^z
|
1655
|
+
sage: f.integrate(x, algorithm='fricas')
|
1656
|
+
1/2*sqrt(2)*sqrt(pi)*(sqrt(2)*x*y^z/sqrt(pi) + fresnel_sin(sqrt(2)*x/sqrt(pi)))
|
1657
|
+
|
1658
|
+
sage: fricas(fresnel_sin(1))
|
1659
|
+
fresnelS(1)
|
1660
|
+
sage: fricas("fresnelS(1.0)")
|
1661
|
+
0.4382591473_9035476607_676
|
1662
|
+
|
1663
|
+
sage: fricas(fresnel_cos(1))
|
1664
|
+
fresnelC(1)
|
1665
|
+
sage: fricas("fresnelC(1.0)")
|
1666
|
+
0.7798934003_7682282947_42
|
1667
|
+
|
1668
|
+
Check that :issue:`17908` is fixed::
|
1669
|
+
|
1670
|
+
sage: fricas(abs(x)).sage().subs(x=-1783)
|
1671
|
+
1783
|
1672
|
+
|
1673
|
+
Check that :issue:`27310` is fixed::
|
1674
|
+
|
1675
|
+
sage: fricas.set("F", "operator 'f")
|
1676
|
+
sage: fricas("eval(D(F(x,y), [x, y], [2, 1]), x=x+y)").sage()
|
1677
|
+
D[0, 0, 1](f)(x + y, y)
|
1678
|
+
|
1679
|
+
Conversion of hypergeometric functions (:issue:`31298`)::
|
1680
|
+
|
1681
|
+
sage: a,b,c = var("a b c")
|
1682
|
+
sage: A = hypergeometric([a, b], [c], x)
|
1683
|
+
sage: fricas(A).sage() - A
|
1684
|
+
0
|
1685
|
+
sage: fricas(A).D(x).sage() - diff(A, x)
|
1686
|
+
0
|
1687
|
+
|
1688
|
+
Check that :issue:`31858` is fixed::
|
1689
|
+
|
1690
|
+
sage: fricas.Gamma(3/2).sage()
|
1691
|
+
1/2*sqrt(pi)
|
1692
|
+
sage: fricas.Gamma(3/4).sage()
|
1693
|
+
gamma(3/4)
|
1694
|
+
sage: fricas.Gamma(3, 2).sage()
|
1695
|
+
gamma(3, 2)
|
1696
|
+
|
1697
|
+
|
1698
|
+
Check that :issue:`32133` is fixed::
|
1699
|
+
|
1700
|
+
sage: var("y")
|
1701
|
+
y
|
1702
|
+
sage: f = fricas.zerosOf(y^4 + y + 1, y); f
|
1703
|
+
+-----------------------------+
|
1704
|
+
| 2 2
|
1705
|
+
\|- 3 %y1 - 2 %y0 %y1 - 3 %y0 - %y1 - %y0
|
1706
|
+
[%y0, %y1, --------------------------------------------,
|
1707
|
+
2
|
1708
|
+
+-----------------------------+
|
1709
|
+
| 2 2
|
1710
|
+
- \|- 3 %y1 - 2 %y0 %y1 - 3 %y0 - %y1 - %y0
|
1711
|
+
----------------------------------------------]
|
1712
|
+
2
|
1713
|
+
|
1714
|
+
sage: f[1].sage()
|
1715
|
+
-1/2*sqrt(1/3)*sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) + 1/2*sqrt(-(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3) + 6*sqrt(1/3)/sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) - 4/3/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3))
|
1716
|
+
"""
|
1717
|
+
# a FriCAS expressions may contain implicit references to a
|
1718
|
+
# rootOf expression within itself, as for example in the
|
1719
|
+
# result of integrate(1/(1+x^5), x). Each algebraic number
|
1720
|
+
# appearing in the expression is only introduced once and
|
1721
|
+
# assigned a variable (usually of the form %%...).
|
1722
|
+
rootOf = dict() # (variable, polynomial)
|
1723
|
+
rootOf_ev = dict() # variable -> (complex) algebraic number
|
1724
|
+
|
1725
|
+
def convert_rootOf(x, y):
|
1726
|
+
if y in rootOf:
|
1727
|
+
assert rootOf[y] == x
|
1728
|
+
else:
|
1729
|
+
rootOf[y] = x
|
1730
|
+
return y
|
1731
|
+
|
1732
|
+
register_symbol(convert_rootOf, {'fricas': 'rootOf'}, 2)
|
1733
|
+
|
1734
|
+
ex, _ = FriCASElement._parse_and_eval(fricas_InputForm)
|
1735
|
+
# postprocessing of rootOf
|
1736
|
+
from sage.rings.qqbar import QQbar
|
1737
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
1738
|
+
while rootOf:
|
1739
|
+
for var, poly in rootOf.items():
|
1740
|
+
pvars = poly.variables()
|
1741
|
+
rvars = [v for v in pvars if v not in rootOf_ev] # remaining variables
|
1742
|
+
uvars = [v for v in rvars if v in rootOf] # variables to evaluate
|
1743
|
+
if len(uvars) == 1:
|
1744
|
+
assert uvars[0] == var, "the only variable in uvars should be %s but is %s" % (var, uvars[0])
|
1745
|
+
break
|
1746
|
+
else:
|
1747
|
+
assert False, "circular dependency in rootOf expression"
|
1748
|
+
# substitute known roots
|
1749
|
+
poly = poly.subs(rootOf_ev)
|
1750
|
+
evars = set(poly.variables()).difference([var])
|
1751
|
+
del rootOf[var]
|
1752
|
+
if evars:
|
1753
|
+
# we just need any root per FriCAS specification -
|
1754
|
+
# however, if there are extra variables, we cannot
|
1755
|
+
# use QQbar.any_root
|
1756
|
+
rootOf_ev[var] = poly.roots(var, multiplicities=False)[0]
|
1757
|
+
else:
|
1758
|
+
R = PolynomialRing(QQbar, "x")
|
1759
|
+
# PolynomialRing does not accept variable names with
|
1760
|
+
# leading underscores
|
1761
|
+
poly = R(poly.subs({var: R.gen()}))
|
1762
|
+
# we just need any root per FriCAS specification
|
1763
|
+
rootOf_ev[var] = poly.any_root()
|
1764
|
+
|
1765
|
+
return ex.subs({var: (val.radical_expression()
|
1766
|
+
if val.parent() is QQbar else val)
|
1767
|
+
for var, val in rootOf_ev.items()})
|
1768
|
+
|
1769
|
+
def _sage_(self):
|
1770
|
+
r"""
|
1771
|
+
Convert ``self`` to a Sage object.
|
1772
|
+
|
1773
|
+
EXAMPLES:
|
1774
|
+
|
1775
|
+
Floats::
|
1776
|
+
|
1777
|
+
sage: fricas(2.1234).sage()
|
1778
|
+
2.12340000000000
|
1779
|
+
sage: _.parent()
|
1780
|
+
Real Field with 53 bits of precision
|
1781
|
+
sage: a = RealField(100)(pi)
|
1782
|
+
sage: fricas(a).sage()
|
1783
|
+
3.1415926535897932384626433833
|
1784
|
+
sage: _.parent()
|
1785
|
+
Real Field with 100 bits of precision
|
1786
|
+
sage: fricas(a).sage() == a
|
1787
|
+
True
|
1788
|
+
sage: fricas(2.0).sage()
|
1789
|
+
2.00000000000000
|
1790
|
+
sage: _.parent()
|
1791
|
+
Real Field with 53 bits of precision
|
1792
|
+
|
1793
|
+
Algebraic numbers::
|
1794
|
+
|
1795
|
+
sage: a = fricas('(1 + sqrt(2))^5'); a
|
1796
|
+
+-+
|
1797
|
+
29 \|2 + 41
|
1798
|
+
sage: b = a.sage(); b
|
1799
|
+
82.0121933088198?
|
1800
|
+
sage: b.radical_expression()
|
1801
|
+
29*sqrt(2) + 41
|
1802
|
+
|
1803
|
+
Integers modulo n::
|
1804
|
+
|
1805
|
+
sage: fricas("((42^17)^1783)::IntegerMod(5^(5^5))").sage() == Integers(5^(5^5))((42^17)^1783)
|
1806
|
+
True
|
1807
|
+
|
1808
|
+
Matrices over a prime field::
|
1809
|
+
|
1810
|
+
sage: fricas("matrix [[1::PF 3, 2],[2, 0]]").sage().parent()
|
1811
|
+
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3
|
1812
|
+
|
1813
|
+
We can also convert FriCAS's polynomials to Sage polynomials::
|
1814
|
+
|
1815
|
+
sage: a = fricas("x^2 + 1"); a.typeOf()
|
1816
|
+
Polynomial(Integer)
|
1817
|
+
sage: a.sage()
|
1818
|
+
x^2 + 1
|
1819
|
+
sage: _.parent()
|
1820
|
+
Univariate Polynomial Ring in x over Integer Ring
|
1821
|
+
sage: fricas('x^2 + y^2 + 1/2').sage()
|
1822
|
+
y^2 + x^2 + 1/2
|
1823
|
+
sage: _.parent()
|
1824
|
+
Multivariate Polynomial Ring in y, x over Rational Field
|
1825
|
+
|
1826
|
+
sage: fricas("1$Polynomial Integer").sage()
|
1827
|
+
1
|
1828
|
+
|
1829
|
+
sage: fricas("x^2/2").sage()
|
1830
|
+
1/2*x^2
|
1831
|
+
|
1832
|
+
sage: x = polygen(QQ, 'x')
|
1833
|
+
sage: fricas(x+3).sage()
|
1834
|
+
x + 3
|
1835
|
+
sage: fricas(x+3).domainOf()
|
1836
|
+
Polynomial(Integer...)
|
1837
|
+
|
1838
|
+
sage: fricas(matrix([[2,3],[4,x+5]])).diagonal().sage()
|
1839
|
+
(2, x + 5)
|
1840
|
+
|
1841
|
+
sage: f = fricas("(y^2+3)::UP(y, INT)").sage(); f
|
1842
|
+
y^2 + 3
|
1843
|
+
sage: f.parent()
|
1844
|
+
Univariate Polynomial Ring in y over Integer Ring
|
1845
|
+
|
1846
|
+
sage: fricas("(y^2+sqrt 3)::UP(y, AN)").sage()
|
1847
|
+
y^2 + 1.732050807568878?
|
1848
|
+
|
1849
|
+
Rational functions::
|
1850
|
+
|
1851
|
+
sage: fricas("x^2 + 1/z").sage()
|
1852
|
+
x^2 + 1/z
|
1853
|
+
|
1854
|
+
Expressions::
|
1855
|
+
|
1856
|
+
sage: fricas(pi).sage()
|
1857
|
+
pi
|
1858
|
+
|
1859
|
+
sage: fricas("sin(x+y)/exp(z)*log(1+%e)").sage()
|
1860
|
+
e^(-z)*log(e + 1)*sin(x + y)
|
1861
|
+
|
1862
|
+
sage: fricas("factorial(n)").sage()
|
1863
|
+
factorial(n)
|
1864
|
+
|
1865
|
+
sage: fricas("integrate(sin(x+y), x=0..1)").sage()
|
1866
|
+
-cos(y + 1) + cos(y)
|
1867
|
+
|
1868
|
+
sage: fricas("integrate(x*sin(1/x), x=0..1)").sage()
|
1869
|
+
'failed'
|
1870
|
+
|
1871
|
+
sage: fricas("integrate(sin((x^2+1)/x),x)").sage()
|
1872
|
+
integral(sin((x^2 + 1)/x), x)
|
1873
|
+
|
1874
|
+
.. TODO::
|
1875
|
+
|
1876
|
+
- Converting matrices and lists takes much too long.
|
1877
|
+
|
1878
|
+
Matrices::
|
1879
|
+
|
1880
|
+
sage: fricas("matrix [[x^n/2^m for n in 0..5] for m in 0..3]").sage() # long time
|
1881
|
+
[ 1 x x^2 x^3 x^4 x^5]
|
1882
|
+
[ 1/2 1/2*x 1/2*x^2 1/2*x^3 1/2*x^4 1/2*x^5]
|
1883
|
+
[ 1/4 1/4*x 1/4*x^2 1/4*x^3 1/4*x^4 1/4*x^5]
|
1884
|
+
[ 1/8 1/8*x 1/8*x^2 1/8*x^3 1/8*x^4 1/8*x^5]
|
1885
|
+
|
1886
|
+
Lists::
|
1887
|
+
|
1888
|
+
sage: fricas("[2^n/x^n for n in 0..5]").sage() # long time
|
1889
|
+
[1, 2/x, 4/x^2, 8/x^3, 16/x^4, 32/x^5]
|
1890
|
+
|
1891
|
+
sage: fricas("[matrix [[i for i in 1..n]] for n in 0..5]").sage() # long time
|
1892
|
+
[[], [1], [1 2], [1 2 3], [1 2 3 4], [1 2 3 4 5]]
|
1893
|
+
|
1894
|
+
Error handling::
|
1895
|
+
|
1896
|
+
sage: s = fricas.guessPade("[fibonacci i for i in 0..10]"); s
|
1897
|
+
n x
|
1898
|
+
[[[x ]- ----------]]
|
1899
|
+
2
|
1900
|
+
x + x - 1
|
1901
|
+
sage: s.sage()
|
1902
|
+
Traceback (most recent call last):
|
1903
|
+
...
|
1904
|
+
NotImplementedError: the translation of the FriCAS Expression 'FEseries' to sage is not yet implemented
|
1905
|
+
|
1906
|
+
sage: s = fricas("series(sqrt(1+x), x=0)"); s
|
1907
|
+
1 1 2 1 3 5 4 7 5 21 6 33 7 429 8
|
1908
|
+
1 + - x - - x + -- x - --- x + --- x - ---- x + ---- x - ----- x
|
1909
|
+
2 8 16 128 256 1024 2048 32768
|
1910
|
+
+
|
1911
|
+
715 9 2431 10 11
|
1912
|
+
----- x - ------ x + O(x )
|
1913
|
+
65536 262144
|
1914
|
+
|
1915
|
+
sage: s.sage()
|
1916
|
+
Traceback (most recent call last):
|
1917
|
+
...
|
1918
|
+
NotImplementedError: the translation of the FriCAS object
|
1919
|
+
<BLANKLINE>
|
1920
|
+
1 1 2 1 3 5 4 7 5 21 6 33 7 429 8
|
1921
|
+
1 + - x - - x + -- x - --- x + --- x - ---- x + ---- x - ----- x
|
1922
|
+
2 8 16 128 256 1024 2048 32768
|
1923
|
+
+
|
1924
|
+
715 9 2431 10 11
|
1925
|
+
----- x - ------ x + O(x )
|
1926
|
+
65536 262144
|
1927
|
+
<BLANKLINE>
|
1928
|
+
to sage is not yet implemented:
|
1929
|
+
An error occurred when FriCAS evaluated 'unparse(...::InputForm)':
|
1930
|
+
<BLANKLINE>
|
1931
|
+
Cannot convert the value from type Any to InputForm .
|
1932
|
+
"""
|
1933
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
1934
|
+
from sage.rings.real_double import RDF
|
1935
|
+
from sage.rings.imaginary_unit import I
|
1936
|
+
from sage.rings.real_mpfr import RealField
|
1937
|
+
from sage.symbolic.ring import SR
|
1938
|
+
from sage.matrix.constructor import matrix
|
1939
|
+
from sage.modules.free_module_element import vector
|
1940
|
+
from sage.structure.factorization import Factorization
|
1941
|
+
from sage.misc.sage_eval import sage_eval
|
1942
|
+
|
1943
|
+
# TODO: perhaps we should translate the type first?
|
1944
|
+
# TODO: perhaps we should get the InputForm as SExpression?
|
1945
|
+
|
1946
|
+
# remember: fricas.new gives a FriCASElement
|
1947
|
+
|
1948
|
+
# the coercion to Any gets rid of the Union domain
|
1949
|
+
P = self._check_valid()
|
1950
|
+
domain = P.new("dom((%s)::Any)" % self._name) # domain is now a fricas SExpression
|
1951
|
+
|
1952
|
+
# first translate dummy domains such as "failed". We must
|
1953
|
+
# not recurse here!
|
1954
|
+
if P.get_boolean("string?(%s)" % domain._name):
|
1955
|
+
return P.get_string("string(%s)" % domain._name)
|
1956
|
+
|
1957
|
+
# now translate domains which cannot be coerced to InputForm,
|
1958
|
+
# or where we do not need it.
|
1959
|
+
head = str(domain.car())
|
1960
|
+
if head == "Record":
|
1961
|
+
fields = fricas("[string symbol(e.2) for e in rest destruct %s]" % domain._name).sage()
|
1962
|
+
return {field: self.elt(field).sage() for field in fields}
|
1963
|
+
|
1964
|
+
if head == "List":
|
1965
|
+
n = P.get_integer('#(%s)' % self._name)
|
1966
|
+
return [self.elt(k).sage() for k in range(1, n + 1)]
|
1967
|
+
|
1968
|
+
if head == "Vector" or head == "DirectProduct":
|
1969
|
+
n = P.get_integer('#(%s)' % self._name)
|
1970
|
+
return vector([self.elt(k).sage() for k in range(1, n + 1)])
|
1971
|
+
|
1972
|
+
if head == "Matrix":
|
1973
|
+
base_ring = self._get_sage_type(domain[1])
|
1974
|
+
rows = self.listOfLists().sage()
|
1975
|
+
return matrix(base_ring, rows)
|
1976
|
+
|
1977
|
+
if head == "Fraction":
|
1978
|
+
return self.numer().sage() / self.denom().sage()
|
1979
|
+
|
1980
|
+
if head == "Complex":
|
1981
|
+
return self.real().sage() + self.imag().sage() * I
|
1982
|
+
|
1983
|
+
if head == "Factored":
|
1984
|
+
l = P.new('[[f.factor, f.exponent] for f in factors(%s)]' % self._name).sage()
|
1985
|
+
return Factorization(list(l))
|
1986
|
+
|
1987
|
+
if head == "UnivariatePolynomial":
|
1988
|
+
base_ring = self._get_sage_type(domain[2])
|
1989
|
+
vars = str(domain[1])
|
1990
|
+
R = PolynomialRing(base_ring, vars)
|
1991
|
+
return R([self.coefficient(i).sage()
|
1992
|
+
for i in range(ZZ(self.degree()) + 1)])
|
1993
|
+
|
1994
|
+
# finally translate domains with InputForm - we do this
|
1995
|
+
# lazily, because sometimes we can use unparse, sometimes we
|
1996
|
+
# need our custom sageprint
|
1997
|
+
|
1998
|
+
def unparsed_InputForm():
|
1999
|
+
try:
|
2000
|
+
return P.get_unparsed_InputForm(self._name)
|
2001
|
+
except RuntimeError as error:
|
2002
|
+
raise NotImplementedError("the translation of the FriCAS object\n\n%s\n\nto sage is not yet implemented:\n%s" % (self, error))
|
2003
|
+
|
2004
|
+
if head == "Boolean":
|
2005
|
+
return unparsed_InputForm() == "true"
|
2006
|
+
|
2007
|
+
if head in ["Integer", "NonNegativeInteger", "PositiveInteger"]:
|
2008
|
+
return ZZ(unparsed_InputForm())
|
2009
|
+
|
2010
|
+
if head == "String":
|
2011
|
+
return unparsed_InputForm()
|
2012
|
+
|
2013
|
+
if head == "Float":
|
2014
|
+
# Warning: precision$Float gives the current precision,
|
2015
|
+
# whereas length(mantissa(self)) gives the precision of
|
2016
|
+
# self.
|
2017
|
+
prec = max(P.new("length mantissa(%s)" % self._name).sage(), 53)
|
2018
|
+
R = RealField(prec)
|
2019
|
+
x, e, b = unparsed_InputForm().lstrip('float(').rstrip(')').split(',')
|
2020
|
+
return R(ZZ(x) * ZZ(b)**ZZ(e))
|
2021
|
+
|
2022
|
+
if head == "DoubleFloat":
|
2023
|
+
return RDF(unparsed_InputForm())
|
2024
|
+
|
2025
|
+
if head == "AlgebraicNumber":
|
2026
|
+
s = unparsed_InputForm()[:-len("::AlgebraicNumber()")]
|
2027
|
+
return sage_eval("QQbar(" + s + ")")
|
2028
|
+
|
2029
|
+
if head == "IntegerMod" or head == "PrimeField":
|
2030
|
+
# one might be tempted not to go via InputForm here, but
|
2031
|
+
# it turns out to be safer to do it.
|
2032
|
+
s = unparsed_InputForm()[len("index("):]
|
2033
|
+
s = s[:s.find(")")]
|
2034
|
+
return self._get_sage_type(domain)(s)
|
2035
|
+
|
2036
|
+
if head == 'DistributedMultivariatePolynomial':
|
2037
|
+
base_ring = self._get_sage_type(domain[2])
|
2038
|
+
vars = domain[1].car()
|
2039
|
+
R = PolynomialRing(base_ring, vars)
|
2040
|
+
return R(unparsed_InputForm())
|
2041
|
+
|
2042
|
+
if head == "Polynomial":
|
2043
|
+
base_ring = self._get_sage_type(domain[1])
|
2044
|
+
# Polynomial Complex is translated into SR
|
2045
|
+
if base_ring is SR:
|
2046
|
+
return FriCASElement._sage_expression(P.get_InputForm(self._name))
|
2047
|
+
|
2048
|
+
# the following is a bad hack, we should be getting a list here
|
2049
|
+
vars = P.get_unparsed_InputForm("variables(%s)" % self._name)[1:-1]
|
2050
|
+
s = unparsed_InputForm()
|
2051
|
+
if vars == "":
|
2052
|
+
return base_ring(s)
|
2053
|
+
|
2054
|
+
R = PolynomialRing(base_ring, vars)
|
2055
|
+
return R(s)
|
2056
|
+
|
2057
|
+
if head in ["OrderedCompletion", "OnePointCompletion"]:
|
2058
|
+
# it would be more correct to get the type parameter
|
2059
|
+
# (which might not be Expression Integer) and recurse
|
2060
|
+
return FriCASElement._sage_expression(P.get_InputForm(self._name))
|
2061
|
+
|
2062
|
+
if head == "Expression" or head == "Pi" or head == "PiDomain":
|
2063
|
+
# we treat Expression Integer and Expression Complex
|
2064
|
+
# Integer just the same
|
2065
|
+
return FriCASElement._sage_expression(P.get_InputForm(self._name))
|
2066
|
+
|
2067
|
+
raise NotImplementedError("the translation of the FriCAS object %s to sage is not yet implemented" % (unparsed_InputForm()))
|
2068
|
+
|
2069
|
+
|
2070
|
+
@instancedoc
|
2071
|
+
class FriCASFunctionElement(FunctionElement):
|
2072
|
+
def __init__(self, object, name):
|
2073
|
+
"""
|
2074
|
+
Make FriCAS operation names valid python function identifiers.
|
2075
|
+
|
2076
|
+
TESTS::
|
2077
|
+
|
2078
|
+
sage: a = fricas('"Hello"')
|
2079
|
+
sage: a.upperCase_q
|
2080
|
+
upperCase?
|
2081
|
+
sage: a.upperCase_e
|
2082
|
+
upperCase!
|
2083
|
+
sage: a.upperCase_e()
|
2084
|
+
"HELLO"
|
2085
|
+
"""
|
2086
|
+
if name.endswith("_q"):
|
2087
|
+
name = name[:-2] + "?"
|
2088
|
+
elif name.endswith("_e"):
|
2089
|
+
name = name[:-2] + "!"
|
2090
|
+
FunctionElement.__init__(self, object, name)
|
2091
|
+
|
2092
|
+
|
2093
|
+
@instancedoc
|
2094
|
+
class FriCASExpectFunction(ExpectFunction):
|
2095
|
+
def __init__(self, parent, name):
|
2096
|
+
"""
|
2097
|
+
Translate the pythonized function identifier back to a FriCAS
|
2098
|
+
operation name.
|
2099
|
+
|
2100
|
+
TESTS::
|
2101
|
+
|
2102
|
+
sage: fricas.upperCase_q
|
2103
|
+
upperCase?
|
2104
|
+
sage: fricas.upperCase_e
|
2105
|
+
upperCase!
|
2106
|
+
"""
|
2107
|
+
if name.endswith("_q"):
|
2108
|
+
name = name[:-2] + "?"
|
2109
|
+
elif name.endswith("_e"):
|
2110
|
+
name = name[:-2] + "!"
|
2111
|
+
ExpectFunction.__init__(self, parent, name)
|
2112
|
+
|
2113
|
+
|
2114
|
+
def is_FriCASElement(x):
|
2115
|
+
"""
|
2116
|
+
Return ``True`` if ``x`` is of type :class:`FriCASElement`.
|
2117
|
+
|
2118
|
+
EXAMPLES::
|
2119
|
+
|
2120
|
+
sage: from sage.interfaces.fricas import is_FriCASElement
|
2121
|
+
sage: is_FriCASElement(2)
|
2122
|
+
doctest:...: DeprecationWarning: the function is_FriCASElement is deprecated; use isinstance(x, sage.interfaces.abc.FriCASElement) instead
|
2123
|
+
See https://github.com/sagemath/sage/issues/34804 for details.
|
2124
|
+
False
|
2125
|
+
sage: is_FriCASElement(fricas(2))
|
2126
|
+
True
|
2127
|
+
"""
|
2128
|
+
from sage.misc.superseded import deprecation
|
2129
|
+
deprecation(34804, "the function is_FriCASElement is deprecated; use isinstance(x, sage.interfaces.abc.FriCASElement) instead")
|
2130
|
+
|
2131
|
+
return isinstance(x, FriCASElement)
|
2132
|
+
|
2133
|
+
|
2134
|
+
fricas = FriCAS()
|
2135
|
+
|
2136
|
+
|
2137
|
+
def reduce_load_fricas():
|
2138
|
+
"""
|
2139
|
+
Return the FriCAS interface object defined in :mod:`sage.interfaces.fricas`.
|
2140
|
+
|
2141
|
+
EXAMPLES::
|
2142
|
+
|
2143
|
+
sage: from sage.interfaces.fricas import reduce_load_fricas
|
2144
|
+
sage: reduce_load_fricas()
|
2145
|
+
FriCAS
|
2146
|
+
"""
|
2147
|
+
return fricas
|
2148
|
+
|
2149
|
+
|
2150
|
+
def fricas_console():
|
2151
|
+
"""
|
2152
|
+
Spawn a new FriCAS command-line session.
|
2153
|
+
|
2154
|
+
EXAMPLES::
|
2155
|
+
|
2156
|
+
sage: fricas_console() # not tested
|
2157
|
+
FriCAS (AXIOM fork) Computer Algebra System
|
2158
|
+
Version: FriCAS 1.0.5
|
2159
|
+
Timestamp: Thursday February 19, 2009 at 06:57:33
|
2160
|
+
-----------------------------------------------------------------------------
|
2161
|
+
Issue )copyright to view copyright notices.
|
2162
|
+
Issue )summary for a summary of useful system commands.
|
2163
|
+
Issue )quit to leave AXIOM and return to shell.
|
2164
|
+
-----------------------------------------------------------------------------
|
2165
|
+
"""
|
2166
|
+
from sage.repl.rich_output.display_manager import get_display_manager
|
2167
|
+
if not get_display_manager().is_in_terminal():
|
2168
|
+
raise RuntimeError('Can use the console only in the terminal. Try %%fricas magics instead.')
|
2169
|
+
os.system('fricas -nox')
|
2170
|
+
|
2171
|
+
|
2172
|
+
def __doctest_cleanup():
|
2173
|
+
"""
|
2174
|
+
EXAMPLES::
|
2175
|
+
|
2176
|
+
sage: from sage.interfaces.fricas import __doctest_cleanup
|
2177
|
+
sage: a = FriCAS()
|
2178
|
+
sage: two = a(2)
|
2179
|
+
sage: a.is_running()
|
2180
|
+
True
|
2181
|
+
sage: __doctest_cleanup()
|
2182
|
+
sage: a.is_running()
|
2183
|
+
False
|
2184
|
+
"""
|
2185
|
+
import sage.interfaces.quit
|
2186
|
+
sage.interfaces.quit.expect_quitall()
|