You can create Sage pseudo-tty interfaces that allow Sage to
work with almost any command-line program,
and which don't require any modification or extensions to that
program. They are also surprisingly fast
and flexible (given how they work!), because all IO is
buffered, and because interaction between Sage and the
command line program can be non-blocking (asynchronous); this is
because they all derive from the Sage class Expect, which
handles the communication between Sage and the external process.
For example, here is part of the file
SAGE_ROOT/devel/sage/sage/interfaces/octave.py, which defines
an interface between Sage and Octave, an open-source program
for doing numerical computations, among other things.
import os from expect import Expect, ExpectElement class Octave(Expect):
os, which contains
operating system routines, and also class Expect, which is the
basic class for interfaces. The third line defines the class
Octave: it derives from Expect. After this comes a
docstring, which we omit here - see the file for details. Next comes:
def __init__(self, maxread=100, script_subdirectory="", logfile=None,
server=None, server_tmpdir=None):
Expect.__init__(self,
name = 'octave',
prompt = '>',
command = "octave --no-line-editing --silent",
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)
Expect to set up the Octave interface.
def set(self, var, value):
"""
Set the variable var to the given value.
"""
cmd = '%s=%s;'%(var,value)
out = self.eval(cmd)
if out.find("error") != -1:
raise TypeError, "Error executing code in Octave\nCODE:\n\t%s\nOctave ERROR:\n\t%s"%(cmd, out)
def get(self, var):
"""
Get the value of the variable var.
"""
s = self.eval('%s'%var)
i = s.find('=')
return s[i+1:]
def console(self):
octave_console()
octave.set('x', 3), after which
octave.get('x') returns ' 3'. Running
octave.console() dumps the user into Octave interactive shell.
def solve_linear_system(self, A, b):
"""
Use octave 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:
An 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: octave.solve_linear_system(A,b) # requires optional octave
[-0.33333299999999999, 0.66666700000000001, -3.5236600000000002e-18]
AUTHOR: David Joyner and William Stein
"""
m = A.nrows()
n = A.ncols()
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.sage2octave_matrix_string(A)
sb = self.sage2octave_matrix_string(b)
self.eval("a = " + sA )
self.eval("b = " + sb )
soln = octave.eval("c = a \\ b")
soln = soln.replace("\n\n ","[")
soln = soln.replace("\n\n","]")
soln = soln.replace("\n",",")
sol = soln[3:]
return eval(sol)
solve_linear_system, which works
as documented.
These are only excerpts from octave.py; check that file for
more definitions and examples. Look at other files in the directory
SAGE_ROOT/devel/sage/sage/interfaces/ for examples of
interfaces to other software packages.
See About this document... for information on suggesting changes.