[BACK]Return to asir.py CVS log [TXT][DIR] Up to [local] / OpenXM / src / sage

File: [local] / OpenXM / src / sage / asir.py (download)

Revision 1.1, Sat Sep 8 05:35:35 2018 UTC (5 years, 8 months ago) by takayama
Branch: MAIN

The initial version of asir module to call asir from sagemath.

# $OpenXM: OpenXM/src/sage/asir.py,v 1.1 2018/09/08 05:35:35 takayama Exp $
from __future__ import print_function
from __future__ import absolute_import

import os
from .expect import Expect, ExpectElement
from sage.misc.misc import verbose

class Asir(Expect):
    r"""
    Interface to the Asir interpreter.

    EXAMPLES::

        sage: asir.eval("a = [ 1, 1, 2; 3, 5, 8; 13, 21, 33 ]")    # optional - asir
        'a =\n\n 1 1 2\n 3 5 8\n 13 21 33\n\n's
        sage: asir.eval("b = [ 1; 3; 13]")                         # optional - asir
        'b =\n\n 1\n 3\n 13\n\n'
        sage: asir.eval("c=a \\ b") # solves linear equation: a*c = b  # optional - asir; random output
        'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n\n'
        sage: asir.eval("c")                                 # optional - asir; random output
        'c =\n\n 1\n 7.21645e-16\n -7.21645e-16\n\n'
    """

    def __init__(self, maxread=None, script_subdirectory=None, logfile=None,
            server=None, server_tmpdir=None, seed=None, command=None):
        """
        EXAMPLES::

            sage: asir == loads(dumps(asir))
            True
        """
        if command is None:
            import os
            command = os.getenv('SAGE_ASIR_COMMAND') or 'openxm ox_texmacs --view sage --quiet --noCopyright'
        if server is None:
            import os
            server = os.getenv('SAGE_ASIR_SERVER') or None
        Expect.__init__(self,
                        name = 'asir',
                        # We want the prompt sequence to be unique to avoid confusion with syntax error messages containing >>>
#                        prompt = 'asir\:\d+> ',
                        prompt = 'asir>',
#                        prompt = '',
                        # We don't want any pagination of output
#                        command = command + " --no-line-editing --silent --eval 'PS2(PS1());more off' --persist",
                        command = 'openxm ox_texmacs --view sage --quiet --noCopyright',
#                        command = "openxm asir -quiet",
                        maxread = maxread,
                        server = server,
                        server_tmpdir = server_tmpdir,
                        script_subdirectory = script_subdirectory,
                        restart_on_ctrlc = False,
                        verbose_start = False,
                        logfile = logfile,
                        eval_using_file_cutoff=100)
        self._seed = seed

    def set_seed(self, seed=None):
        """
        Sets the seed for the random number generator
        for this asir interpreter.

        EXAMPLES::

            sage: o = Asir() # optional - asir
            sage: o.set_seed(1) # optional - asir
            1
            sage: [o.rand() for i in range(5)] # optional - asir
            [ 0.134364,  0.847434,  0.763775,  0.255069,  0.495435]
        """
        if seed is None:
            seed = self.rand_seed()
        self.eval("rand('state',%d)" % seed)
        self._seed = seed
        return seed

    def __reduce__(self):
        """
        EXAMPLES::

            sage: asir.__reduce__()
            (<function reduce_load_Asir at 0x...>, ())
        """
        return reduce_load_Asir, tuple([])

    def _read_in_file_command(self, filename):
        """
        EXAMPLES::

            sage: filename = tmp_filename()
            sage: asir._read_in_file_command(filename)
            'source("...");'
        """
        return 'source("%s");'%filename

    def _quit_string(self):
        """
        EXAMPLES::

            sage: asir._quit_string()
            'quit;'
        """
        return '!quit;'

    def _install_hints(self):
        """
        Returns hints on how to install Asir.

        EXAMPLES::

            sage: print(asir._install_hints())
            You must get ...
        """
        return """
        You must get the program "asir" in order to use Asir
        from Sage.   You can read all about Asir at
                http://www.gnu.org/software/asir/

        LINUX:
           Do apt-get install asir as root on your machine (Ubuntu/Debian).
           Other Linux systems have asir too.

        OS X:
           * This website has details on OS X builds of Asir:
                    http://wiki.asir.org/Asir_for_MacOS_X
           * Darwin ports and fink have Asir as well.
        """

    def _eval_line(self, line, reformat=True, allow_use_file=False,
                   wait_for_prompt=True, restart_if_needed=False):
        """
        EXAMPLES::

            sage: print(asir._eval_line('2+2'))  #optional - asir
              ans =  4
        """
        from pexpect.exceptions import EOF
        if not wait_for_prompt:
            return Expect._eval_line(self, line)
        if line == '':
            return ''
        if self._expect is None:
            self._start()
        if allow_use_file and len(line)>3000:
            return self._eval_line_using_file(line)
        try:
            E = self._expect
            # debug
            # self._synchronize(cmd='1+%s\n')
            verbose("in = '%s'"%line,level=3)
            E.sendline(line)
            E.expect(self._prompt)
            out = E.before
            # debug
            verbose("out = '%s'"%out,level=3)
        except EOF:
            if self._quit_string() in line:
                return ''
        except KeyboardInterrupt:
            self._keyboard_interrupt()
        try:
            if reformat:
                if 'syntax error' in out:
                    raise SyntaxError(out)
            out = "\n".join(out.splitlines()[1:])
            return out
        except NameError:
            return ''

    def _keyboard_interrupt(self):
        print("CntrlC: Interrupting %s..."%self)
        if self._restart_on_ctrlc:
            try:
                self._expect.close(force=1)
            except pexpect.ExceptionPexpect as msg:
                raise RuntimeError( "THIS IS A BUG -- PLEASE REPORT. This should never happen.\n" + msg)
            self._start()
            raise KeyboardInterrupt("Restarting %s (WARNING: all variables defined in previous session are now invalid)"%self)
        else:
            self._expect.send('\003') # control-c
            raise KeyboardInterrupt("Ctrl-c pressed while running %s"%self)

    def quit(self, verbose=False):
        """
        EXAMPLES::

            sage: o = Asir()
            sage: o._start()    # optional - asir
            sage: o.quit(True)  # optional - asir
            Exiting spawned Asir process.
        """
        # Don't bother, since it just hangs in some cases, and it
        # isn't necessary, since asir behaves well with respect
        # to signals.
        if not self._expect is None:
            if verbose:
                print("Exiting spawned %s process." % self)
        return

    def _start(self):
        """
        Starts the Asir process.

        EXAMPLES::

            sage: o = Asir()    # optional - asir
            sage: o.is_running()  # optional - asir
            False
            sage: o._start()      # optional - asir
            sage: o.is_running()  # optional - asir
            True
        """
        Expect._start(self)
#        self.eval("page_screen_output=0;")
#        self.eval("format none;")
        # set random seed
#        self.set_seed(self._seed)

    def _equality_symbol(self):
        """
        EXAMPLES::

            sage: asir('0 == 1')  # optional - asir
             0
            sage: asir('1 == 1')  # optional - asir
             1
        """
        return '=='

    def _true_symbol(self):
        """
        EXAMPLES::

            sage: asir('1 == 1')  # optional - asir
             1
        """
        return '1'

    def _false_symbol(self):
        """
        EXAMPLES::

            sage: asir('0 == 1')  # optional - asir
             0
        """
        return '0'

    def set(self, var, value):
        """
        Set the variable ``var`` to the given ``value``.

        EXAMPLES::

            sage: asir.set('x', '2') # optional - asir
            sage: asir.get('x') # optional - asir
            ' 2'
        """
        cmd = '%s=%s;'%(var,value)
        out = self.eval(cmd)
        if out.find("error") != -1 or out.find("Error") != -1:
            raise TypeError("Error executing code in Asir\nCODE:\n\t%s\nAsir ERROR:\n\t%s"%(cmd, out))

    def get(self, var):
        """
        Get the value of the variable ``var``.

        EXAMPLES::

            sage: asir.set('x', '2') # optional - asir
            sage: asir.get('x') # optional - asir
            ' 2'
        """
        s = self.eval('%s;'%var)
        i = s.find('=')
        return s[i+1:]

    def clear(self, var):
        """
        Clear the variable named var.

        EXAMPLES::

            sage: asir.set('x', '2') # optional - asir
            sage: asir.clear('x')    # optional - asir
            sage: asir.get('x')      # optional - asir
            "error: 'x' undefined near line ... column 1"
        """
        self.eval('clear %s'%var)

    def console(self):
        """
        Spawn a new Asir command-line session.

        This requires that the optional asir program be installed and in
        your PATH, but no optional Sage packages need be installed.

        EXAMPLES::

            sage: asir_console()         # not tested
            GNU Asir, version 2.1.73 (i386-apple-darwin8.5.3).
            Copyright (C) 2006 John W. Eaton.
            ...
            asir:1> 2+3
            ans = 5
            asir:2> [ctl-d]

        Pressing ctrl-d exits the asir console and returns you to Sage.
        asir, like Sage, remembers its history from one session to
        another.
        """
        asir_console()

    def version(self):
        """
        Return the version of Asir.

        OUTPUT: string

        EXAMPLES::

            sage: v = asir.version()   # optional - asir
            sage: v                      # optional - asir; random
            '2.13.7'

            sage: import re
            sage: assert re.match("\d+\.\d+\.\d+", v)  is not None # optional - asir
        """
        return str(self("version()")).strip()

    def solve_linear_system(self, A, b):
        r"""
        Use asir to compute a solution x to A\*x = b, as a list.

        INPUT:

        - ``A`` -- mxn matrix A with entries in `\QQ` or `\RR`

        - ``b`` -- m-vector b entries in `\QQ` or `\RR` (resp)

        OUTPUT: A list x (if it exists) which solves M\*x = b

        EXAMPLES::

            sage: M33 = MatrixSpace(QQ,3,3)
            sage: A   = M33([1,2,3,4,5,6,7,8,0])
            sage: V3  = VectorSpace(QQ,3)
            sage: b   = V3([1,2,3])
            sage: asir.solve_linear_system(A,b)    # optional - asir (and output is slightly random in low order bits)
            [-0.33333299999999999, 0.66666700000000001, -3.5236600000000002e-18]

        AUTHORS:

        - David Joyner and William Stein
        """
        m = A.nrows()
        if m != len(b):
            raise ValueError("dimensions of A and b must be compatible")
        from sage.matrix.all import MatrixSpace
        from sage.rings.all import QQ
        MS = MatrixSpace(QQ,m,1)
        b  = MS(list(b)) # converted b to a "column vector"
        sA = self.sage2asir_matrix_string(A)
        sb = self.sage2asir_matrix_string(b)
        self.eval("a = " + sA )
        self.eval("b = " + sb )
        soln = asir.eval("c = a \\ b")
        soln = soln.replace("\n\n ","[")
        soln = soln.replace("\n\n","]")
        soln = soln.replace("\n",",")
        sol  = soln[3:]
        return eval(sol)


    def sage2asir_matrix_string(self, A):
        """
        Return an asir matrix from a Sage matrix.

        INPUT: A Sage matrix with entries in the rationals or reals.

        OUTPUT: A string that evaluates to an Asir matrix.

        EXAMPLES::

            sage: M33 = MatrixSpace(QQ,3,3)
            sage: A = M33([1,2,3,4,5,6,7,8,0])
            sage: asir.sage2asir_matrix_string(A)   # optional - asir
            '[1, 2, 3; 4, 5, 6; 7, 8, 0]'

        AUTHORS:

        - David Joyner and William Stein
        """
        return str(A.rows()).replace('), (', '; ').replace('(', '').replace(')','')

    def de_system_plot(self, f, ics, trange):
        r"""
        Plots (using asir's interface to gnuplot) the solution to a
        `2\times 2` system of differential equations.

        INPUT:


        -  ``f`` - a pair of strings representing the
           differential equations; The independent variable must be called x
           and the dependent variable must be called y.

        -  ``ics`` - a pair [x0,y0] such that x(t0) = x0, y(t0)
           = y0

        -  ``trange`` - a pair [t0,t1]


        OUTPUT: a gnuplot window appears

        EXAMPLES::

            sage: asir.de_system_plot(['x+y','x-y'], [1,-1], [0,2])  # not tested -- does this actually work (on OS X it fails for me -- William Stein, 2007-10)

        This should yield the two plots `(t,x(t)), (t,y(t))` on the
        same graph (the `t`-axis is the horizontal axis) of the
        system of ODEs

        .. math::

                       x' = x+y, x(0) = 1;\qquad y' = x-y, y(0) = -1,                     \quad\text{for}\quad 0 < t < 2.
        """
        eqn1 = f[0].replace('x','x(1)').replace('y','x(2)')
        eqn2 = f[1].replace('x','x(1)').replace('y','x(2)')
        fcn = "function xdot = f(x,t) xdot(1) = %s; xdot(2) = %s; endfunction"%(eqn1, eqn2)
        self.eval(fcn)
        x0_eqn = "x0 = [%s; %s]"%(ics[0], ics[1])
        self.eval(x0_eqn)
        t_eqn = "t = linspace(%s, %s, 200)'"%(trange[0], trange[1])
        self.eval(t_eqn)
        x_eqn = 'x = lsode("f",x0,t);'
        self.eval(x_eqn)
        self.eval("plot(t,x)")

    def _object_class(self):
        """
        EXAMPLES::

            sage: asir._object_class()
            <class 'sage.interfaces.asir.AsirElement'>
        """
        return AsirElement


asir_functions = set()

def to_complex(asir_string, R):
    r"""
    Helper function to convert asir complex number

    TESTS::

        sage: from sage.interfaces.asir import to_complex
        sage: to_complex('(0,1)', CDF)
        1.0*I
        sage: to_complex('(1.3231,-0.2)', CDF)
        1.3231 - 0.2*I
    """
    real, imag = asir_string.strip('() ').split(',')
    return R(float(real), float(imag))

class AsirElement(ExpectElement):
    def _get_sage_ring(self):
        r"""
        TESTS::

            sage: asir('1')._get_sage_ring()  # optional - asir
            Real Double Field
            sage: asir('I')._get_sage_ring()  # optional - asir
            Complex Double Field
            sage: asir('[]')._get_sage_ring() # optional - asir
            Real Double Field
        """
        if self.isinteger():
            import sage.rings.integer_ring
            return sage.rings.integer_ring.ZZ
        elif self.isreal():
            import sage.rings.real_double
            return sage.rings.real_double.RDF
        elif self.iscomplex():
            import sage.rings.complex_double
            return sage.rings.complex_double.CDF
        else:
            raise TypeError("no Sage ring associated to this element.")

    def __nonzero__(self):
        r"""
        Test whether this element is nonzero.

        EXAMPLES::

            sage: bool(asir('0'))                 # optional - asir
            False
            sage: bool(asir('[]'))                # optional - asir
            False
            sage: bool(asir('[0,0]'))             # optional - asir
            False
            sage: bool(asir('[0,0,0;0,0,0]'))     # optional - asir
            False

            sage: bool(asir('0.1'))               # optional - asir
            True
            sage: bool(asir('[0,1,0]'))           # optional - asir
            True
            sage: bool(asir('[0,0,-0.1;0,0,0]'))  # optional - asir
            True
        """
        return str(self) != ' [](0x0)' and any(x != '0' for x in str(self).split())

    def _matrix_(self, R=None):
        r"""
        Return Sage matrix from this asir element.

        EXAMPLES::

            sage: A = asir('[1,2;3,4.5]')     # optional - asir
            sage: matrix(A)                     # optional - asir
            [1.0 2.0]
            [3.0 4.5]
            sage: _.base_ring()                 # optional - asir
            Real Double Field

            sage: A = asir('[I,1;-1,0]')      # optional - asir
            sage: matrix(A)                     # optional - asir
            [1.0*I   1.0]
            [ -1.0   0.0]
            sage: _.base_ring()                 # optional - asir
            Complex Double Field

            sage: A = asir('[1,2;3,4]')       # optional - asir
            sage: matrix(ZZ, A)                 # optional - asir
            [1 2]
            [3 4]
            sage: A = asir('[1,2;3,4.5]')     # optional - asir
            sage: matrix(RR, A)                 # optional - asir
            [1.00000000000000 2.00000000000000]
            [3.00000000000000 4.50000000000000]
        """
        if not self.ismatrix():
            raise TypeError('not an asir matrix')
        if R is None:
            R = self._get_sage_ring()

        s = str(self).strip('\n ')
        w = [u.strip().split(' ') for u in s.split('\n')]
        nrows = len(w)
        ncols = len(w[0])

        if self.iscomplex():
            w = [[to_complex(x,R) for x in row] for row in w]

        from sage.matrix.all import MatrixSpace
        return MatrixSpace(R, nrows, ncols)(w)

    def _vector_(self, R=None):
        r"""
        Return Sage vector from this asir element.

        EXAMPLES::

            sage: A = asir('[1,2,3,4]')       # optional - asir
            sage: vector(ZZ, A)                 # optional - asir
            (1, 2, 3, 4)
            sage: A = asir('[1,2.3,4.5]')     # optional - asir
            sage: vector(A)                     # optional - asir
            (1.0, 2.3, 4.5)
            sage: A = asir('[1,I]')           # optional - asir
            sage: vector(A)                     # optional - asir
            (1.0, 1.0*I)
        """
        oc = self.parent()
        if not self.isvector():
            raise TypeError('not an asir vector')
        if R is None:
            R = self._get_sage_ring()

        s = str(self).strip('\n ')
        w = s.strip().split(' ')
        nrows = len(w)

        if self.iscomplex():
            w = [to_complex(x, R) for x in w]

        from sage.modules.free_module import FreeModule
        return FreeModule(R, nrows)(w)

    def _scalar_(self):
        """
        Return Sage scalar from this asir element.

        EXAMPLES::

            sage: A = asir('2833')      # optional - asir
            sage: As = A.sage(); As       # optional - asir
            2833.0
            sage: As.parent()             # optional - asir
            Real Double Field

            sage: B = sqrt(A)             # optional - asir
            sage: Bs = B.sage(); Bs       # optional - asir
            53.2259
            sage: Bs.parent()             # optional - asir
            Real Double Field

            sage: C = sqrt(-A)            # optional - asir
            sage: Cs = C.sage(); Cs       # optional - asir
            53.2259*I
            sage: Cs.parent()             # optional - asir
            Complex Double Field
        """
        if not self.isscalar():
            raise TypeError("not an asir scalar")

        R = self._get_sage_ring()
        if self.iscomplex():
            return to_complex(str(self), R)
        else:
            return R(str(self))

    def _sage_(self):
        """
        Try to parse the asir object and return a sage object.

        EXAMPLES::

            sage: A = asir('2833')           # optional - asir
            sage: A.sage()                     # optional - asir
            2833.0
            sage: B = sqrt(A)                  # optional - asir
            sage: B.sage()                     # optional - asir
            53.2259
            sage: C = sqrt(-A)                 # optional - asir
            sage: C.sage()                     # optional - asir
            53.2259*I
            sage: A = asir('[1,2,3,4]')      # optional - asir
            sage: A.sage()                     # optional - asir
            (1.0, 2.0, 3.0, 4.0)
            sage: A = asir('[1,2.3,4.5]')    # optional - asir
            sage: A.sage()                     # optional - asir
            (1.0, 2.3, 4.5)
            sage: A = asir('[1,2.3+I,4.5]')  # optional - asir
            sage: A.sage()                     # optional - asir
            (1.0, 2.3 + 1.0*I, 4.5)
        """
        if self.isscalar():
            return self._scalar_()
        elif self.isvector():
            return self._vector_()
        elif self.ismatrix():
            return self._matrix_()
        else:
            raise NotImplementedError('asir type is not recognized')

# An instance
asir = Asir()

def reduce_load_Asir():
    """
    EXAMPLES::

        sage: from sage.interfaces.asir import reduce_load_Asir
        sage: reduce_load_Asir()
        Asir
    """
    return asir


def asir_console():
    """
    Spawn a new Asir command-line session.

    This requires that the optional asir program be installed and in
    your PATH, but no optional Sage packages need be installed.

    EXAMPLES::

        sage: asir_console()         # not tested
        GNU Asir, version 2.1.73 (i386-apple-darwin8.5.3).
        Copyright (C) 2006 John W. Eaton.
        ...
        asir:1> 2+3
        ans = 5
        asir:2> [ctl-d]

    Pressing ctrl-d exits the asir console and returns you to Sage.
    asir, like Sage, remembers its history from one session to
    another.
    """
    from sage.repl.rich_output.display_manager import get_display_manager
    if not get_display_manager().is_in_terminal():
        raise RuntimeError('Can use the console only in the terminal. Try %%asir magics instead.')
    os.system('openxm asir')    # with asir prompt
#    os.system('openxm asir -quiet')


def asir_version():
    """
    DEPRECATED: Return the version of Asir installed.

    EXAMPLES::

        sage: asir_version()    # optional - asir
        doctest:...: DeprecationWarning: This has been deprecated. Use
        asir.version() instead
        See http://trac.sagemath.org/21135 for details.
        '...'
    """
    from sage.misc.superseded import deprecation
    deprecation(21135, "This has been deprecated. Use asir.version() instead")
    return asir.version()