From 0ab0f924f297669e203031ab9f74677eee7c8361 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 16 Aug 2016 02:06:40 +0200 Subject: [PATCH] further improve translation to sage objects, make restart work, fix doctests --- src/sage/interfaces/axiom.py | 53 --- src/sage/interfaces/fricas.py | 499 ++++++++++++++------ src/sage/rings/finite_rings/integer_mod.pyx | 2 +- src/sage/rings/infinity.py | 4 +- src/sage/rings/real_mpfr.pyx | 2 +- src/sage/symbolic/constants.py | 2 +- src/sage/symbolic/integration/external.py | 18 +- 7 files changed, 361 insertions(+), 219 deletions(-) diff --git a/src/sage/interfaces/axiom.py b/src/sage/interfaces/axiom.py index 32012f7661b..2fe87ed075c 100644 --- a/src/sage/interfaces/axiom.py +++ b/src/sage/interfaces/axiom.py @@ -383,10 +383,6 @@ def set(self, var, value): sage: axiom.get('xx') #optional - axiom '2' - sage: fricas.set('xx', '2') #optional - fricas - sage: fricas.get('xx') #optional - fricas - '2' - """ cmd = '%s := %s'%(var, value) out = self._eval_line(cmd, reformat=False) @@ -423,20 +419,6 @@ def _eval_line(self, line, reformat=True, allow_use_file=False, sage: print(axiom._eval_line('2+2')) # optional - axiom 4 Type: PositiveInteger - sage: fricas._eval_line(")set output algebra off") #optional - fricas - '' - sage: fricas._eval_line(")set output tex on") #optional - fricas - '' - sage: print(fricas._eval_line("2+2")) # optional - fricas - $$ - 4 - \leqno(11) - $$ - Type: PositiveInteger - sage: fricas._eval_line(")set output tex off") #optional - fricas - '' - sage: fricas._eval_line(")set output algebra on") #optional - fricas - '' """ if not wait_for_prompt: return Expect._eval_line(self, line) @@ -499,8 +481,6 @@ def _equality_symbol(self): sage: a = axiom(x==6); a #optional axiom x= 6 - sage: a = fricas(x==6); a #optional fricas - x= 6 """ return "=" @@ -709,14 +689,6 @@ def comma(self, *args): sage: _.type() #optional - axiom Tuple PositiveInteger - sage: two = fricas(2) #optional - fricas - sage: two.comma(3) #optional - fricas - [2,3] - sage: two.comma(3,4) #optional - fricas - [2,3,4] - sage: _.type() #optional - fricas - Tuple(PositiveInteger) - """ P = self._check_valid() args = list(args) @@ -734,9 +706,6 @@ def _latex_(self): sage: latex(a) #optional - axiom \frac{1}{2} - sage: a = fricas(1/2) #optional - fricas - sage: latex(a) #optional - fricas - 1 \over 2 """ self._check_valid() P = self.parent() @@ -767,15 +736,6 @@ def as_type(self, type): sage: _.type() #optional - axiom DoubleFloat - :: - - sage: a = fricas(1.2); a #optional - fricas - 1.2 - sage: a.as_type(fricas.DoubleFloat) #optional - fricas - 1.2 - sage: _.type() #optional - fricas - DoubleFloat - """ P = self._check_valid() type = P(type) @@ -794,9 +754,6 @@ def unparsed_input_form(self): sage: a.unparsed_input_form() #optional - axiom 'x*x+1' - sage: a = fricas(x^2+1) #optional - fricas - sage: a.unparsed_input_form() #optional - fricas - 'x^2+1' """ P = self._check_valid() s = P.eval('unparse(%s::InputForm)'%self._name) @@ -832,9 +789,6 @@ def _sage_(self): sage: gp(axiom(1/2)) #optional - axiom 1/2 - sage: fricas(1/2).sage() #optional - fricas - 1/2 - DoubleFloat's in Axiom are converted to be in RDF in Sage. :: @@ -920,19 +874,12 @@ def _sage_domain(self): sage: axiom('Integer').sage() #optional - axiom Integer Ring - sage: fricas('Integer').sage() #optional - fricas - Integer Ring sage: axiom('Fraction Integer').sage() #optional - axiom Rational Field - sage: fricas('Fraction Integer').sage() #optional - fricas - Rational Field sage: axiom('DoubleFloat').sage() #optional - axiom Real Double Field - sage: fricas('DoubleFloat').sage() #optional - fricas - Real Double Field - """ P = self._check_valid() name = str(self) diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index a082a1bea13..9157d4f8c8e 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -27,9 +27,9 @@ :: - sage: fricas('3 * 5') # optional - fricas + sage: fricas('3 * 5') # optional - fricas 15 - sage: a = fricas(3) * fricas(5); a # optional - fricas + sage: a = fricas(3) * fricas(5); a # optional - fricas 15 The type of a is FriCASElement, i.e., an element of the FriCAS @@ -37,15 +37,15 @@ :: - sage: type(a) # optional - fricas + sage: type(a) # optional - fricas - sage: a.parent() # optional - fricas + sage: a.parent() # optional - fricas FriCAS The underlying FriCAS type of a is also available, via the type method:: - sage: a.typeOf() # optional - fricas + sage: a.typeOf() # optional - fricas PositiveInteger We factor `x^5 - y^5` in FriCAS in several different ways. @@ -53,23 +53,23 @@ :: - sage: F = fricas.factor('x^5 - y^5'); F # optional - fricas + sage: F = fricas.factor('x^5 - y^5'); F # optional - fricas 4 3 2 2 3 4 - (y - x)(y + x y + x y + x y + x ) - sage: type(F) # optional - fricas + sage: type(F) # optional - fricas - sage: F.typeOf() # optional - fricas + sage: F.typeOf() # optional - fricas Factored(Polynomial(Integer)) Note that FriCAS objects are normally displayed using "ASCII art". :: - sage: a = fricas(2/3); a # optional - fricas + sage: a = fricas(2/3); a # optional - fricas 2 - 3 - sage: a = fricas('x^2 + 3/7'); a # optional - fricas + sage: a = fricas('x^2 + 3/7'); a # optional - fricas 2 3 x + - 7 @@ -81,7 +81,7 @@ :: - sage: print(fricas.eval('factor(x^5 - y^5)')) # optional - fricas + sage: print(fricas.eval('factor(x^5 - y^5)')) # optional - fricas 4 3 2 2 3 4 - (y - x)(y + x y + x y + x y + x ) @@ -92,11 +92,11 @@ :: - sage: f = fricas('x^5 - y^5') # optional - fricas - sage: f^2 # optional - fricas + sage: f = fricas('x^5 - y^5') # optional - fricas + sage: f^2 # optional - fricas 10 5 5 10 y - 2x y + x - sage: f.factor() # optional - fricas + sage: f.factor() # optional - fricas 4 3 2 2 3 4 - (y - x)(y + x y + x y + x y + x ) @@ -106,40 +106,65 @@ :: - sage: f = fricas('(x^5 - y^5)^10000') # not tested - fricas + sage: f = fricas('(x^5 - y^5)^10000') # not tested - fricas Interrupting FriCAS... ... : Ctrl-c pressed while running FriCAS :: - sage: fricas('1/100 + 1/101') # optional - fricas + sage: fricas('1/100 + 1/101') # optional - fricas 201 ----- 10100 - sage: a = fricas('(1 + sqrt(2))^5'); a # optional - fricas + sage: a = fricas('(1 + sqrt(2))^5'); a # optional - fricas +-+ 29\|2 + 41 -TESTS: +Let us demonstrate some features of FriCAS. FriCAS can guess a +differential equation for the generating function for integer +partitions:: + + sage: fricas("guessADE([partition n for n in 0..40], homogeneous==4)") # optional - fricas + [ + [ + n + [x ]f(x): + 2 3 (iv) 2 2 , 3 ,,, 2 2 ,, 2 + x f(x) f (x) + (20x f(x) f (x) + 5x f(x) )f (x) - 39x f(x) f (x) + + + + 2 , 2 2 , 3 ,, 2 , 4 + (12x f(x)f (x) - 15x f(x) f (x) + 4f(x) )f (x) + 6x f (x) + + + + , 3 2 , 2 + 10x f(x)f (x) - 16f(x) f (x) + + = + 0 + , + 2 3 4 + f(x)= 1 + x + 2x + 3x + O(x )] + ] + +FriCAS can solve linear ordinary differential equations:: + + sage: fricas.set("y", "operator y") # optional - fricas + 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") # optional - fricas + sage: fricas.set("sol", "solve(deq, y, x)"); fricas("sol") # optional - fricas + 5 3 2 3 2 3 3 2 + x - 10x + 20x + 4 2x - 3x + 1 x - 1 x - 3x - 1 + [particular= --------------------,basis= [-------------,------,------------]] + 15x x x x + + sage: fricas("sol.particular").sage() # optional - fricas + 1/15*(x^5 - 10*x^3 + 20*x^2 + 4)/x + sage: fricas("sol.basis").sage() # optional - fricas + [(2*x^3 - 3*x^2 + 1)/x, (x^3 - 1)/x, (x^3 - 3*x^2 - 1)/x] + sage: fricas.eval(")clear values y deq sol") # optional - fricas + '' -We check to make sure the subst method works with keyword -arguments. - -:: - - sage: a = fricas(x+2); a #optional - fricas - x + 2 - sage: a.subst(x=3) #optional - fricas - 5 - -We verify that FriCAS floating point numbers can be converted to -Python floats. - -:: - - sage: float(fricas(2)) #optional - fricas - 2.0 """ ########################################################################### @@ -152,26 +177,27 @@ # # http://www.gnu.org/licenses/ ########################################################################### -# from __future__ import print_function +from __future__ import print_function # from __future__ import absolute_import -from sage.interfaces.axiom import PanAxiomElement, PanAxiomFunctionElement, PanAxiomExpectFunction from sage.interfaces.tab_completion import ExtraTabCompletion from sage.interfaces.expect import Expect, ExpectElement, FunctionElement, ExpectFunction from sage.env import DOT_SAGE import re import six -FRICAS_SINGLE_LINE_START = 3 # where the output starts -FRICAS_MULTI_LINE_START = 2 -FRICAS_LINE_LENGTH = 245 # length of a line -FRICAS_LINE_BREAK = "\r\n" # line ending -# beware that lisp distinguishes between ' and ". +FRICAS_SINGLE_LINE_START = 3 # where the output starts when it fits next to the line number +FRICAS_MULTI_LINE_START = 2 # and when it doesn't +FRICAS_LINE_LENGTH = 80 # length of a line, should match the line length in sage +FRICAS_LINE_BREAK = "\r\n" # line ending character +# only the last command should be necessary to make the interface +# work, the other are optimizations. Beware that lisp distinguishes +# between ' and ". FRICAS_INIT_CODE = ( -")lisp (princ \"running FriCAS intialisation code\")", ")set functions compile on", -")set output length " + str(FRICAS_LINE_LENGTH), ")set message autoload off", +")set message type off", +")set output length " + str(FRICAS_LINE_LENGTH), ")lisp (setf |$ioHook|" " (lambda (x &optional args)" " (when (member x '(|startAlgebraOutput| |endOfAlgebraOutput|" @@ -225,19 +251,117 @@ def _start(self): EXAMPLES:: - sage: a = FriCAS() - sage: a.is_running() + sage: a = FriCAS() # optional - fricas + sage: a.is_running() # optional - fricas False - sage: a._start() #optional - axiom - sage: a.is_running() #optional - axiom + sage: a._start() # optional - fricas + sage: a.is_running() # optional - fricas True - sage: a.quit() #optional - axiom + sage: a.quit() # optional - fricas """ + # this is necessary for restarting FriCAS + self._prompt = FRICAS_FIRST_PROMPT Expect._start(self) # switching off the line numbers also modified the prompt self._prompt = FRICAS_LINENUMBER_OFF_PROMPT self.eval(FRICAS_LINENUMBER_OFF_CODE) + def _quit_string(self): + """ + Returns the string used to quit FriCAS. + + EXAMPLES:: + + sage: fricas._quit_string() # optional - fricas + ')quit' + sage: a = FriCAS() # optional - fricas + sage: a.is_running() # optional - fricas + False + sage: a._start() # optional - fricas + sage: a.is_running() # optional - fricas + True + sage: a.quit() # optional - fricas + sage: a.is_running() # optional - fricas + False + """ + return ')quit' + + def _commands(self): + """ + Returns a list of commands available. This is done by parsing the + result of the first section of the output of ')what things'. + + EXAMPLES:: + + sage: cmds = fricas._commands() # optional - fricas + sage: len(cmds) > 100 # optional - fricas + True + sage: '<' in cmds # optional - fricas + True + sage: 'factor' in cmds # optional - fricas + True + """ + s = self.eval(")what things") + start = '\r\n\r\n#' + i = s.find(start) + end = "To get more information about" + j = s.find(end) + s = s[i+len(start):j].split() + return s + + def _tab_completion(self, verbose=True, use_disk_cache=True): + """ + Returns a list of all the commands defined in Fricas and optionally + (per default) store them to disk. + + EXAMPLES:: + + sage: c = fricas._tab_completion(use_disk_cache=False, verbose=False) # optional - fricas + sage: len(c) > 100 # optional - fricas + True + sage: 'factor' in c # optional - fricas + True + sage: '**' in c # optional - fricas + False + sage: 'upperCase?' in c # optional - fricas + False + sage: 'upperCase_q' in c # optional - fricas + True + sage: 'upperCase_e' in c # optional - fricas + True + """ + try: + return self.__tab_completion + except AttributeError: + import sage.misc.persist + if use_disk_cache: + try: + self.__tab_completion = sage.misc.persist.load(self._COMMANDS_CACHE) + return self.__tab_completion + except IOError: + pass + if verbose: + print("\nBuilding %s command completion list (this takes" % self) + print("a few seconds only the first time you do it).") + print("To force rebuild later, delete %s." % self._COMMANDS_CACHE) + v = self._commands() + + #Process we now need process the commands to strip out things which + #are not valid Python identifiers. + valid = re.compile('[^a-zA-Z0-9_]+') + names = [x for x in v if valid.search(x) is None] + + #Change everything that ends with ? to _q and + #everything that ends with ! to _e + names += [x[:-1]+"_q" for x in v if x.endswith("?")] + names += [x[:-1]+"_e" for x in v if x.endswith("!")] + + self.__tab_completion = names + if len(v) > 200: + # Fricas is actually installed. + sage.misc.persist.save(v, self._COMMANDS_CACHE) + return names + def eval(self, code, strip=True, synchronize=False, locals=None, allow_use_file=True, split_lines="nofile", **kwds): """Send the code to the FriCAS interpreter and return the pretty @@ -277,8 +401,8 @@ def set(self, var, value): EXAMPLES:: - sage: fricas.set('xx', '2') #optional - fricas - sage: fricas.get('xx') #optional - fricas + sage: fricas.set('xx', '2') # optional - fricas + sage: fricas.get('xx') # optional - fricas '2' """ @@ -296,11 +420,11 @@ def get(self, var): EXAMPLES:: - sage: fricas.set('xx', '2') #optional - fricas - sage: fricas.get('xx') #optional - fricas + sage: fricas.set('xx', '2') # optional - fricas + sage: fricas.get('xx') # optional - fricas '2' - sage: a = fricas('(1 + sqrt(2))^5') #optional - fricas - sage: fricas.get(a.name()) #optional - fricas + sage: a = fricas('(1 + sqrt(2))^5') # optional - fricas + sage: fricas.get(a.name()) # optional - fricas ' +-+\r\n29\\|2 + 41' """ # print "running get", var @@ -319,7 +443,7 @@ def _equality_symbol(self): EXAMPLES:: - sage: a = fricas(x==6); a #optional fricas + sage: a = fricas(x==6); a # optional - fricas x= 6 """ return "=" @@ -328,7 +452,7 @@ def __repr__(self): """ EXAMPLES:: - sage: fricas + sage: fricas # optional - fricas FriCAS """ return "FriCAS" @@ -337,10 +461,10 @@ def __reduce__(self): """ EXAMPLES:: - sage: fricas.__reduce__() + sage: fricas.__reduce__() # optional - fricas (, ()) - sage: f, args = _ - sage: f(*args) + sage: f, args = _ # optional - fricas + sage: f(*args) # optional - fricas FriCAS """ return reduce_load_fricas, tuple([]) @@ -351,9 +475,9 @@ def _function_class(self): EXAMPLES:: - sage: fricas._function_class() + sage: fricas._function_class() # optional - fricas - sage: type(fricas.gcd) + sage: type(fricas.gcd) # optional - fricas """ return FriCASExpectFunction @@ -362,9 +486,9 @@ def _object_class(self): """ EXAMPLES:: - sage: fricas._object_class() + sage: fricas._object_class() # optional - fricas - sage: type(fricas(2)) #optional - fricas + sage: type(fricas(2)) # optional - fricas """ return FriCASElement @@ -375,9 +499,9 @@ def _function_element_class(self): EXAMPLES:: - sage: fricas._function_element_class() + sage: fricas._function_element_class() # optional - fricas - sage: type(fricas(2).gcd) #optional - fricas + sage: type(fricas(2).gcd) #optional - fricas # optional - fricas """ return FriCASFunctionElement @@ -388,7 +512,7 @@ def console(self): EXAMPLES:: - sage: fricas.console() #not tested + sage: fricas.console() # not tested FriCAS (AXIOM fork) Computer Algebra System Version: FriCAS 1.0.5 Timestamp: Thursday February 19, 2009 at 06:57:33 @@ -402,15 +526,14 @@ def console(self): class FriCASElement(ExpectElement): - def __len__(self): """ Return the length of a list. EXAMPLES:: - sage: v = axiom('[x^i for i in 0..5]') # optional - axiom - sage: len(v) # optional - axiom + sage: v = fricas('[x^i for i in 0..5]') # optional - fricas + sage: len(v) # optional - fricas 6 """ P = self._check_valid() @@ -445,6 +568,11 @@ def __long__(self): raise NotImplementedError def __float__(self): + """ + TEST:: + sage: float(fricas(2)) # optional - fricas + 2.0 + """ return float(self.sage()) def _integer_(self, ZZ=None): @@ -456,7 +584,7 @@ def _rational_(self): def gen(self, n): raise NotImplementedError - def _get_1d_output(self): + def _unparsed_InputForm(self): """Return the output from FriCAS as a string without the quotes. TODO: @@ -472,24 +600,25 @@ def _get_1d_output(self): We test that strings are returned properly: - sage: r = fricas('concat([concat(string(i)," ") for i in 0..299])')._get_1d_output() - sage: r == " ".join([str(i) for i in range(300)]) + ' ' + sage: r = fricas('concat([concat(string(i)," ") for i in 0..299])')._unparsed_InputForm() # optional - fricas + sage: r == " ".join([str(i) for i in range(300)]) + ' ' # optional - fricas True - sage: fricas('concat([string(1) for i in 1..5])')._get_1d_output() == "1"*5 + sage: fricas('concat([string(1) for i in 1..5])')._unparsed_InputForm() == "1"*5 # optional - fricas True - sage: fricas('concat([string(1) for i in 1..10000])')._get_1d_output() == "1"*10000 + sage: fricas('concat([string(1) for i in 1..10000])')._unparsed_InputForm() == "1"*10000 # optional - fricas True """ P = self._check_valid() return P.eval('unparse(%s::InputForm)' %self._name).replace("\r\n", "")[1:-1] - def _get_sage_type(self, type): + + def _get_sage_type(self, domain): """ INPUT: - - type, a FriCAS SExpression + - domain, a FriCAS SExpression OUTPUT: @@ -500,9 +629,10 @@ def _get_sage_type(self, type): from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.real_mpfr import RealField from sage.symbolic.ring import SR + from sage.matrix.constructor import matrix # first implement domains without arguments - head = str(type.car()) + head = str(domain.car()) if head in ["Integer", "NonNegativeInteger", "PositiveInteger"]: return ZZ if head == "String": @@ -516,18 +646,15 @@ def _get_sage_type(self, type): return QQbar # now implement "functorial" types - if head == "Union": - return self._get_sage_type(type[1][2]) - if head == "OrderedCompletion": # this is a workaround, I don't know how translate this return SR - + if head == "IntegerMod": - return Integers(type[1].integer().sage()) + return Integers(domain[1].integer().sage()) if head == "Fraction": - return FractionField(self._get_sage_type(type[1])) + return FractionField(self._get_sage_type(domain[1])) if head == "Expression": return SR @@ -546,30 +673,30 @@ def _sage_(self): Floats: - sage: fricas(2.1234).sage() # optional - fricas + sage: fricas(2.1234).sage() # optional - fricas 2.12340000000000 - sage: _.parent() # optional - fricas + sage: _.parent() # optional - fricas Real Field with 53 bits of precision sage: a = RealField(100)(pi) - sage: fricas(a).sage() # optional - fricas + sage: fricas(a).sage() # optional - fricas 3.1415926535897932384626433833 - sage: _.parent() # optional - fricas + sage: _.parent() # optional - fricas Real Field with 100 bits of precision - sage: fricas(a).sage() == a # optional - fricas + sage: fricas(a).sage() == a # optional - fricas True - sage: fricas(2.0).sage() # optional - fricas + sage: fricas(2.0).sage() # optional - fricas 2.00000000000000 - sage: _.parent() # optional - fricas + sage: _.parent() # optional - fricas Real Field with 53 bits of precision Algebraic numbers: - sage: a = fricas('(1 + sqrt(2))^5'); a # optional - fricas + sage: a = fricas('(1 + sqrt(2))^5'); a # optional - fricas +-+ 29\|2 + 41 - sage: b = a.sage(); b # optional - fricas + sage: b = a.sage(); b # optional - fricas 82.0121933088198? - sage: b.radical_expression() # optional - fricas + sage: b.radical_expression() # optional - fricas 29*sqrt(2) + 41 Integers modulo n: @@ -577,48 +704,59 @@ def _sage_(self): sage: fricas("((42^17)^1783)::IntegerMod(5^(5^5))").sage() == Integers(5^(5^5))((42^17)^1783) # optional - fricas True - We can also convert Fricas's polynomials to Sage polynomials. + We can also convert FriCAS's polynomials to Sage polynomials. - sage: a = fricas(x^2 + 1); a.typeOf() # optional - fricas + sage: a = fricas(x^2 + 1); a.typeOf() # optional - fricas Polynomial(Integer) - sage: a.sage() # optional - fricas + sage: a.sage() # optional - fricas x^2 + 1 - sage: _.parent() # optional - fricas + sage: _.parent() # optional - fricas Univariate Polynomial Ring in x over Integer Ring - sage: fricas('x^2 + y^2 + 1/2').sage() # optional - fricas + sage: fricas('x^2 + y^2 + 1/2').sage() # optional - fricas y^2 + x^2 + 1/2 - sage: _.parent() # optional - fricas + sage: _.parent() # optional - fricas Multivariate Polynomial Ring in y, x over Rational Field - sage: fricas("1$Polynomial Integer").sage() # optional - fricas + sage: fricas("1$Polynomial Integer").sage() # optional - fricas 1 - sage: fricas("x^2/2").sage() # optional - fricas + sage: fricas("x^2/2").sage() # optional - fricas 1/2*x^2 Rational functions: - sage: fricas("x^2 + 1/z").sage() # optional - fricas + sage: fricas("x^2 + 1/z").sage() # optional - fricas x^2 + 1/z Expressions: - sage: fricas("sin(x+y)/exp(z)*log(1+%e)").sage() # optional - fricas + sage: fricas("sin(x+y)/exp(z)*log(1+%e)").sage() # optional - fricas e^(-z)*log(e + 1)*sin(x + y) - sage: fricas("factorial(n)").sage() # optional - fricas + sage: fricas("factorial(n)").sage() # optional - fricas factorial(n) - sage: fricas("integrate(sin(x+y), x=0..1)").sage() # optional - fricas + sage: fricas("integrate(sin(x+y), x=0..1)").sage() # optional - fricas -cos(y + 1) + cos(y) - sage: fricas("integrate(x*sin(1/x), x=0..1)").sage() # optional - fricas + sage: fricas("integrate(x*sin(1/x), x=0..1)").sage() # optional - fricas + 'failed' + + Matrices: + + sage: fricas("matrix [[x^n/2^m for n in 0..5] for m in 0..3]").sage() # optional - fricas + [ 1 x x^2 x^3 x^4 x^5] + [ 1/2 1/2*x 1/2*x^2 1/2*x^3 1/2*x^4 1/2*x^5] + [ 1/4 1/4*x 1/4*x^2 1/4*x^3 1/4*x^4 1/4*x^5] + [ 1/8 1/8*x 1/8*x^2 1/8*x^3 1/8*x^4 1/8*x^5] - sage: fricas("guessPade [1,1,2,3,5,8]") # optional - fricas - n 1 - [[[x ]- ----------]] - 2 - x + x - 1 + Lists: + + sage: fricas("[2^n/x^n for n in 0..5]").sage() # optional - fricas + [1, 2/x, 4/x^2, 8/x^3, 16/x^4, 32/x^5] + + sage: fricas("[matrix [[i for i in 1..n]] for n in 0..5]").sage() # optional - fricas + [[], [1], [1 2], [1 2 3], [1 2 3 4], [1 2 3 4 5]] """ from sage.rings.all import ZZ, QQ, QQbar, PolynomialRing, RDF @@ -626,47 +764,62 @@ def _sage_(self): from sage.rings.finite_rings.integer_mod_ring import Integers from sage.rings.real_mpfr import RealField from sage.symbolic.ring import SR + from sage.matrix.constructor import matrix from sage.misc.sage_eval import sage_eval - domain = self.dom() # type is now a fricas SExpression + # TODO: perhaps we should translate the type first? + # TODO: perhaps we should get the InputForm as SExpression? + + # the coercion to Any gets rid of the Union domain + P = self._check_valid() + domain = P.new("dom(%s::Any)" % self._name) # domain is now a fricas SExpression + + # first translate dummy domains such as "failed" + # we must not recurse here! + if P.eval("string?(%s)" % domain._name) == "true": + return P.eval("string(%s)" % domain._name)[1:-1] + + # now translate domains without arguments head = str(domain.car()) - # the special domain Union needs special treatment - if head == "Union": - domain = domain[1][2] - head = str(domain.car()) + if head == "Boolean": + return self._unparsed_InputForm() == "true" - # first implement domains without arguments if head in ["Integer", "NonNegativeInteger", "PositiveInteger"]: - return ZZ(self._get_1d_output()) + return ZZ(self._unparsed_InputForm()) if head == "String": - return self._get_1d_output() + return self._unparsed_InputForm() if head == "Float": + # Warning: precision$Float gives the current precision, + # whereas length(mantissa(self)) gives the precision of + # self. prec = max(self.mantissa().length().sage(), 53) R = RealField(prec) - x, e, b = self._get_1d_output().lstrip('float(').rstrip(')').split(',') + x, e, b = self._unparsed_InputForm().lstrip('float(').rstrip(')').split(',') return R(ZZ(x)*ZZ(b)**ZZ(e)) if head == "DoubleFloat": - return RDF(self._get_1d_output()) + return RDF(self._unparsed_InputForm()) if head == "AlgebraicNumber": - s = self._get_1d_output()[:-len("::AlgebraicNumber()")] + s = self._unparsed_InputForm()[:-len("::AlgebraicNumber()")] return sage_eval("QQbar(" + s + ")") - # now implement "functorial" types - + # finally implement "functorial" types if head == "List": - P = self._check_valid() - n = P.new('# %s'%self.name()).sage() + n = int(P.eval('# %s'%self._name)) return [P.new('%s(%s)'%(self._name, k)).sage() for k in range(1, n+1)] + if head == "Matrix": + base_ring = self._get_sage_type(domain[1]) + rows = P.new('listOfLists(%s)' %self._name).sage() + return matrix(base_ring, rows) + if head == "IntegerMod": - # one might be tempted not to go via unparse and - # InputForm here, but it turns out to be safer to do - # it. - n = self._get_1d_output()[len("index("):] + # one might be tempted not to go via InputForm here, but + # it turns out to be safer to do it. + n = self._unparsed_InputForm()[len("index("):] n = n[:n.find(")")] return self._get_sage_type(domain)(n) @@ -675,37 +828,75 @@ def _sage_(self): if head == "Polynomial": base_ring = self._get_sage_type(domain[1]) - vars = self.variables()._get_1d_output()[1:-1] + vars = self.variables()._unparsed_InputForm()[1:-1] if vars == "": - return base_ring(self._get_1d_output()) + return base_ring(self._unparsed_InputForm()) else: R = PolynomialRing(base_ring, vars) - return R(self._get_1d_output()) + return R(self._unparsed_InputForm()) if head == "OrderedCompletion": # this is a workaround, I don't know how translate this if str(domain[1].car()) == "Expression": - s = self._get_1d_output() - return SR(s) - + s = self._unparsed_InputForm() + return SR(s.replace("pi()", "pi")) + if head == "Expression": # TODO: we also have Expression Complex Integer and the like if str(domain[1].car()) == "Integer": - s = self._get_1d_output() - return SR(s) + s = self._unparsed_InputForm() + return SR(s.replace("pi()", "pi")) if head == 'DistributedMultivariatePolynomial': base_ring = self._get_sage_type(domain[2]) vars = domain[1].car() R = PolynomialRing(base_ring, vars) - return R(self._get_1d_output()) + return R(self._unparsed_InputForm()) -class FriCASFunctionElement(PanAxiomFunctionElement): +class FriCASFunctionElement(FunctionElement): + def __init__(self, object, name): + """Make FriCAS operation names valid python function identifiers. + + TESTS:: + + sage: a = fricas('"Hello"') # optional - fricas + sage: a.upperCase_q # optional - fricas + upperCase? + sage: a.upperCase_e # optional - fricas + upperCase! + sage: a.upperCase_e() # optional - fricas + "HELLO" + + """ + if name.endswith("_q"): + name = name[:-2] + "?" + elif name.endswith("_e"): + name = name[:-2] + "!" + FunctionElement.__init__(self, object, name) + pass -class FriCASExpectFunction(PanAxiomExpectFunction): +class FriCASExpectFunction(ExpectFunction): + def __init__(self, parent, name): + """Translate the pythonized function identifier back to a FriCAS + operation name. + + TESTS:: + + sage: fricas.upperCase_q # optional - fricas + upperCase? + sage: fricas.upperCase_e # optional - fricas + upperCase! + + """ + if name.endswith("_q"): + name = name[:-2] + "?" + elif name.endswith("_e"): + name = name[:-2] + "!" + ExpectFunction.__init__(self, parent, name) + pass def is_FriCASElement(x): @@ -714,10 +905,10 @@ def is_FriCASElement(x): EXAMPLES:: - sage: from sage.interfaces.fricas import is_FriCASElement #optional - fricas - sage: is_FriCASElement(fricas(2)) #optional - fricas + sage: from sage.interfaces.fricas import is_FriCASElement # optional - fricas + sage: is_FriCASElement(fricas(2)) # optional - fricas True - sage: is_FriCASElement(2) #optional - fricas + sage: is_FriCASElement(2) # optional - fricas False """ return isinstance(x, FriCASElement) @@ -731,8 +922,8 @@ def reduce_load_fricas(): EXAMPLES:: - sage: from sage.interfaces.fricas import reduce_load_fricas - sage: reduce_load_fricas() + sage: from sage.interfaces.fricas import reduce_load_fricas # optional - fricas + sage: reduce_load_fricas() # optional - fricas FriCAS """ return fricas @@ -745,7 +936,7 @@ def fricas_console(): EXAMPLES:: - sage: fricas_console() #not tested + sage: fricas_console() # not tested FriCAS (AXIOM fork) Computer Algebra System Version: FriCAS 1.0.5 Timestamp: Thursday February 19, 2009 at 06:57:33 @@ -764,13 +955,13 @@ def __doctest_cleanup(): """ EXAMPLES:: - sage: from sage.interfaces.fricas import __doctest_cleanup #optional - fricas - sage: a = FriCAS() #optional - fricas - sage: two = a(2) #optional - fricas - sage: a.is_running() #optional - fricas + sage: from sage.interfaces.fricas import __doctest_cleanup # optional - fricas + sage: a = FriCAS() # optional - fricas + sage: two = a(2) # optional - fricas + sage: a.is_running() # optional - fricas True - sage: __doctest_cleanup() #optional - fricas - sage: a.is_running() #optional - fricas + sage: __doctest_cleanup() # optional - fricas + sage: a.is_running() # optional - fricas False """ import sage.interfaces.quit diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 58c6b5f1ee3..2eaf0335c3e 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -488,7 +488,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): sage: aa = fricas(a); aa #optional - fricas 4 - sage: aa.type() #optional - fricas + sage: aa.typeOf() #optional - fricas IntegerMod(15) """ diff --git a/src/sage/rings/infinity.py b/src/sage/rings/infinity.py index 3b043a575d6..96e2066173b 100644 --- a/src/sage/rings/infinity.py +++ b/src/sage/rings/infinity.py @@ -283,9 +283,11 @@ def _fricas_init_(self): TESTS:: sage: fricas(-oo) # optional - fricas - %minusInfinity + - infinity sage: [x._fricas_init_() for x in [unsigned_infinity, oo, -oo]] # optional - fricas ['%infinity', '%plusInfinity', '%minusInfinity'] + sage: [fricas(x) for x in [unsigned_infinity, oo, -oo]] # optional - fricas + [infinity, + infinity, - infinity] """ if self._sign_char == '': return r"%infinity" diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index ce2864ef499..9d39f73f758 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -3032,7 +3032,7 @@ cdef class RealNumber(sage.structure.element.RingElement): sage: axiom(R(pi)) # optional - axiom # indirect doctest 3.1415926535 8979323846 26433833 sage: fricas(R(pi)) # optional - fricas - 3.1415926535 8979323846 26433833 + 3.1415926535_8979323846_26433833 """ prec = self.parent().prec() diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index 08b4c085bf4..a0eabde2853 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -568,7 +568,7 @@ def __init__(self, name="pi"): π """ - conversions = dict(axiom='%pi', maxima='%pi', giac='pi', gp='Pi', kash='PI', + conversions = dict(axiom='%pi', fricas='%pi', maxima='%pi', giac='pi', gp='Pi', kash='PI', mathematica='Pi', matlab='pi', maple='pi', octave='pi', pari='Pi', pynac='Pi') Constant.__init__(self, name, conversions=conversions, diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 044ff0b3ab3..d1fc35ada5a 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -76,7 +76,7 @@ def mma_free_integrator(expression, v, a=None, b=None): raise ValueError("Unable to parse: %s" % mexpr) -def fricas_integrator(expression, v, a=None, b=None): +def fricas_integrator(expression, v, a=None, b=None, noPole=True): """ Integration using FriCAS @@ -88,9 +88,9 @@ def fricas_integrator(expression, v, a=None, b=None): sage: fricas_integrator(cos(x), x) # optional - fricas sin(x) sage: fricas_integrator(1/(x^2-2), x, 0, 1) # optional - fricas - 1/4*(log(3*sqrt(2) - 4) - log(sqrt(2)))*sqrt(2) + 1/4*sqrt(2)*(log(3*sqrt(2) - 4) - log(sqrt(2))) sage: fricas_integrator(1/(x^2+6), x, -oo, oo) # optional - fricas - 1/6*pi*sqrt(6) + 1/6*sqrt(6)*pi """ if not isinstance(expression, Expression): expression = SR(expression) @@ -107,14 +107,16 @@ def fricas_integrator(expression, v, a=None, b=None): elif b == sage.rings.infinity.MinusInfinity(): b = "%minusInfinity" - result = expression._fricas_().integrate("{}={}..{}".format(v, a, b)) + if noPole: + result = expression._fricas_().integrate("{}={}..{}".format(v, a, b), '"noPole"') + else: + result = expression._fricas_().integrate("{}={}..{}".format(v, a, b)) + locals = {str(v): v for v in expression.variables()} if str(result) == "potentialPole": raise ValueError("The integrand has a potential pole" " in the integration interval") - parsed_result = result.unparsed_input_form() - import sage.misc.sage_eval try: - return sage.misc.sage_eval.sage_eval(parsed_result, locals=locals) + return result.sage() except: - raise ValueError("Unable to parse: {}".format(parsed_result)) + raise ValueError("Unable to parse: {}".format(result))