Friday, February 29, 2008

ANN: sympycore version 0.1 released

We are proud to present a new Python package:

sympycore - an efficient pure Python Computer Algebra System

Sympycore is available for download from

http://sympycore.googlecode.com/

Sympycore is released under the New BSD License.

Sympycore provides efficient data structures for representing symbolic expressions and methods to manipulate them. Sympycore uses a very clear algebra oriented design that can be easily extended.

Sympycore is a pure Python package with no external dependencies, it requires Python version 2.5 or higher to run. Sympycore uses Mpmath for fast arbitrary-precision floating-point arithmetic that is included into sympycore package.

Sympycore is to our knowledge the most efficient pure Python implementation of a Computer Algebra System. Its speed is comparable to Computer Algebra Systems implemented in compiled languages. Some comparison benchmarks are available in

* http://code.google.com/p/sympycore/wiki/Performance

* http://code.google.com/p/sympycore/wiki/PerformanceHistory

and it is our aim to continue seeking for more efficient ways to manipulate symbolic expressions:

http://cens.ioc.ee/~pearu/sympycore_bench/

Sympycore version 0.1 provides the following features:

* symbolic arithmetic operations
* basic expression manipulation methods: expanding, substituting, and pattern matching.
* primitive algebra to represent unevaluated symbolic expressions
* calculus algebra of symbolic expressions, unevaluated elementary functions, differentiation and polynomial integration methods
* univariate and multivariate polynomial rings
* matrix rings
* expressions with physical units
* SympyCore User's Guide and API Docs are available online.

Take a look at the demo for sympycore 0.1 release:

http://sympycore.googlecode.com/svn/trunk/doc/html/demo0_1.html

However, one should be aware that sympycore does not implement many
features that other Computer Algebra Systems do. The version number
0.1 speaks for itself:)

Sympycore is inspired by many attempts to implement CAS for Python and it is created to fix SymPy performance and robustness issues. Sympycore does not yet have nearly as many features as SymPy. Our goal is to work on in direction of merging the efforts with the SymPy project in the near future.

Enjoy!

* Pearu Peterson
* Fredrik Johansson

Acknowledgments:

* The work of Pearu Peterson on the SympyCore project is supported by a Center of Excellence grant from the Norwegian Research Council to Center for Biomedical Computing at Simula Research Laboratory.

Thursday, January 24, 2008

SympyCore Users and Reference Manual

Created in January 2008 by Pearu Peterson

Introduction

SympyCore projects home page is http://sympycore.googlecode.com/.

The recent version of this document can be downloaded in PDF, OpenOffice, Word, RTF, TXT formats.

Editorial notes

The following convention should be used when editing this Google Docs document:
  1. To expose Python sessions from the text flow, use Quote. The color of the Python session text should be dark navy blue. The font should be Trebuchet MS. Note that correct fonts show up only when exporting to PDF, OpenOffice or other formats.
  2. To expose Python code inside text, use Italic and dark navy blue.
  3. When adding a section to the document, put your name in the form "Created in Month Year by FirstName LastName" for reference. Noticable additions to this document will add your name to the list of Authors.
You can add/change these conventions if they are easy to use and the output looks nice. Note that the document should look nice at least in PDF and html formats (this ensures that also other formats will look nice as well).

Getting Started

To use SympyCore from Python, one needs to import the sympycore package:

>>> from sympycore import *

The sympycore package provides Symbol and Number functions to construct symbolic objects and numbers. By default, the symbolic objects represent the elements of Calculus algebra -- a commutative ring of symbolic expressions where exponent algebra is also Calculus algebra.
>>> x = Symbol('x)
>>> n = Number(2,5)
>>> x+n
Calculus('x + 2/5')

To construct expression from a string, use the corresponding algebra class, for example,
>>> Calculus('x+y+1/4 + x**2')+x
Calculus('y + 2*x + 1/4 + x**2')

XXX: need more examples on elementaty operations.

CAS model

Symbolic expressions represent mathematical concepts like numbers, constants, variables, functions, operators, and various relations between them. Symbolic objects, on the other hand, represent symbolic expressions in a running computer program. The aim of a Computer Algebra System (CAS) is to provide methods to manipulate symbolic objects and by that manipulate symbolic expressions. These manipulations of symbolic expressions have mathematical meaning when the methods are consistent with the rules and theories from mathematics.

There are many possible ways to represent a mathematical concept as a structure of a computer program. SympyCore mimics mathematical concepts via implementing the corresponding algebra and algebraic operations in a class, say Algebra, that is derived from the BasicAlgebra class. So, a symbolic object is an instance of the Algebra class. This instance contains information about the mathematical operator that when applied to operands forms the corresponding symbolic object. The operator and operands of the given symbolic object can be accessed via atrributes func and args. The value of func is a callable object and args is a sequence of symbolic objects. So, if A is a Algebra instance then
<symbolic object> = A.func(*A.args)
The actual value of func is defined by the Algebra class. For example, in the case of calculus algebra class Calculus, the func value can be Add, Mul, Pow, sin, log, etc. If the symbolic object represents a symbol (eg a variable) or a number of the algebra then func contains a callable that returns the symbolic object (the args in this case will be an empty sequence).

The symbolic objects representing symbols and numbers can be constructed via the Symbol and Number functions. Such symbolic objects are called atomic.
One should note that functions Add, Mul, Pow, Symbol, Number, etc are always specific to the given algebra (in fact, they are defined as classmethods of the corresponding algebra class).

While most of the algebra operators assume symbolic objects as their operands then Symbol and Number functions may take various Python objects as arguments. For example, the argument to Calculus.Symbol can be any python object that is immutable (this requirement comes from the fact terms of sums and factors of products are internally saved as Python dictionary keys), and the arguments to Calculus.Number can be Python number types such as int, long, float, complex as well as Fraction, Float, Complex instances (these are defined in sympycore.arithmetic package).

One can construct symbolic objects from Python strings using them as single arguments to algebra class constructor. For example,
>>> Calculus('a-3/4+b**2')
Calculus('a + b**2 - 3/4')
>>> Calculus('a-3/4+b**2').func
<bound method BasicType.Add of <class 'sympycore.calculus.algebra.Calculus'>>
>>> Calculus('a-3/4+b**2').args
[Calculus('a'), Calculus('-3/4'), Calculus('b**2')]

Package structure

SympyCore project provides a python package sympycore that consists of several modules and subpackages:
  1. core.py - provides a base class Basic to all symbolic objects. Note that almost any (hashable) python object can be used as an operand to algebraic operations (assuming the corresponding algebra class accepts it) and hence it is not always necessary to derive classes defining mathematical from Basic. Only classes that could be used by other parts of the sympycore should be derive from Basic. In such cases, these classes are available via classes holder (also defined in core.py). For example,
    >>> from sympycore.core import classes
    >>> classes.Calculus
    <class 'sympycore.calculus.algebra.Calculus'>
    >>> classes.Unit
    <class 'sympycore.physics.units.Unit'>
    >>> classes.CommutativeRingWithPairs
    <class 'sympycore.basealgebra.pairs.CommutativeRingWithPairs'>

  2. arithmetic/ - provides Fraction, Float, Complex classes that represent fractions, multiprecision floating point numbers, and complex numbers with rational parts. This package also defines symbols like oo, zoo, undefined that extend the number sets with infinities and undefined symbols (eg 0/0 -> undefined) to make the number sets closed with respect to all algebraic operations: +, -, *, /, **. For more information about the package, see [section on number theory support].
  3. basealgebra/ - provides abstract base classes representing algebras: BasicAlgebra, CommutativeRing, .., and base classes for algebras with implementations: Primitive, CommutativeRingWithPairs, ...
  4. calculus/ - provides class Calculus that represents the algebra of symbolic expressions. The Calculus provides the default algebra in sympycore. For more information, see [section on calculus].
    • calculus/functions/ - provides symbolic functions like exp, log, sin, cos, tan, cot, sqrt, ...
  5. physics/ - provides class Unit that represents the algebra of symbolic expressions of physical quantities. For more information, see [section on physics].
  6. polynomials/ - provides classes Polynomial, UnivariatePolynomial, MultivariatePolynomial to represent the algebras of polynomials with symbols, univariate polynomials in (coefficient:exponent) form, and multivariate polynomials in (coefficients:exponents) form, respectively. For more information, see [section on polynomials].

Generic informational and transformational methods

In sympycore all symbolic objects are assumed to be immutable. So, the manipulation of symbolic objects means creating new symbolic objects from the parts of existing ones.

There are many methods that can be used to retrive information and subexpressions from a symbolic object. The most generic method is to use attribute pair of func and args as described above. However, many such methods are also algebra specific, for example, classes of commutative rings have methods like as_Add_args, as_Mul_args etc for retriving operands and Add, Mul, etc for constructing new symbolic objects. For more information, see sections describing particular algebra classes. The generic informational methods are described below.
  1. str(<symbolic object>) - return a nice string representation of the symbolic object. For example,
    >>> expr = Calculus('-x + 2')
    >>> str(expr)
    '2 - x'

  2. <symbolic object>.as_tree() - return a tree string representation of the symbolic object. For example,
    >>> expr = Calculus('-x + 2+y**3')
    >>> print expr.as_tree()
    Calculus:
    ADD[
    -1:SYMBOL[x]
    1:MUL[
    1: 3:SYMBOL[y]
    1:]
    2:NUMBER[1]
    ]
    where the first line shows the name of a algebra class following the content of the symbolic object in tree form. Note how are represented the coefficients and exponents of the example subexpressions.


There are also methods that create new symbolic objects from existing ones. For example, substitutions, computing derivatives, integrals, etc are such methods and they also can be algebra specific. The generic ones are described below.
  1. <symbolic object>.subs(<subexpression>, <newexpression>) - return a copy of <symbolic object> with all occurances of <subexpression> replaced with <newexpression>. For example,
    >>> expr = Calculus('-x + 2+y**3')
    >>> expr
    Calculus('2 + y**3 - x')
    >>> expr.subs('y', '2*z')
    Calculus('2 + 8*z**3 - x')

  2. <symbolic object>.subs([(<subexpr1>, <newexpr1>), (<subexpr2>, <newexpr2>), ...]) is equivalent to <symbolic object>.subs(<subexp1>, <newexpr1>).subs(<subexpr2>, <newexpr2>).subs... For example,
    >>> expr
    Calculus('2 + y**3 - x')
    >>> expr.subs([('y', '2*z'),('z', 2)])
    Calculus('66 - x')

  3. <symbolic object>.as_primitive() - return symbolic object as an instance of PrimitiveAlgebra class. All algebra classes must implement as_primitive method as this allows converting symbolic objects from one algebra to another that is compatible with respect to algebraic operations. Also, producing the string representations of symbolic objects is done via converting them to PrimitiveAlgebra that implements the corresponding printing method. For example,
    >>> expr
    Calculus('2 + y**3 - x')
    >>> expr.as_primitive()
    PrimitiveAlgebra('2 + y**3 - x')

  4. <symbolic object>.as_algebra(<algebra class>) - return symbolic object as an instance of given algebra class. The transformation is done by first converting the symbolic object to PrimitiveAlgebra instance which in turn is converted to the instance of targer algebra class by executing the corresponding target algebra operators on operands. For example,
    >>> expr = Calculus('-x + 2')
    >>> print expr.as_tree()
    Calculus:
    ADD[
    -1:SYMBOL[x]
    2:NUMBER[1]
    ]
    >>> print expr.as_algebra(PrimitiveAlgebra).as_tree()
    PrimitiveAlgebra:
    ADD[
    NEG[
    SYMBOL[x]
    ]
    NUMBER[2]
    ]
    >>> print expr.as_algebra(CommutativeRingWithPairs).as_tree()
    CommutativeRingWithPairs:
    ADD[
    -1:SYMBOL[x]
    2:NUMBER[1]
    ]




XXX: Add sections Arithmetic (or Number Theory), Base Algebra, Calculus, Polynomials, Physics that describe the corresponding features in detail.








Monday, January 14, 2008

My dog Poiss.




Thursday, January 03, 2008

Sympy Core and Sympy performance history

The following page reports how the performance of SymPy and
Sympy Core has changed during the development if the
sympy package:

http://code.google.com/p/sympycore/wiki/PerformanceHistory

Sunday, January 28, 2007

Symbolic - symbolic manipulation package for Python

Author: Pearu Peterson <pearu.peterson@gmail.com>
Created: January 2007

Symbolic is a pure Python package that provides tools for performing symbolic manipulations with Symbolic objects such as symbols, rational numbers, functions, differentiation and integration operators, algebraic operations etc. The package provides a parser tool to create symbolic objects from a string with Python language like syntax.

Availability

Symbolic is currently available through contacting the author directly.

Getting started

Here is how to create a simple polynomial using the parser from Python session:
>>> from symbolic.api import *
>>> poly = Symbolic('2/3 + 3 * a + 1/4 * a ** 2')
>>> poly
Symbolic('2/3 + 3 * a + 1/4 * a ** 2')
Though the repr output of poly looks like input, the object poly is actually an Add object containing Number, Symbol, Mul, and Power objects. To display the internal object hierarhy of a symbolic object one can either use .torepr() method or set Symbolic.interactive to False value. For instance
>>> Symbolic.interactive = False
>>> poly
Add(Rational(2, 3), Mul(Integer(3), Symbol('a')), Mul(Rational(1, 4), Power(Symbol('a'), Integer(2))))
>>> Symbolic.interactive = True # restore default behavior of repr.
One can construct symbolic objects by performing operations with Python objects. But one must take into account that Python does not have rational numbers and integer division may not produce desired result when the division is not exact. The issue of creating rational number objects can be resolved by using Rational class or Symbolic parser or by proper operations with symbolic objects. For example
>>> a = Symbol('a')
>>> poly2 = Rational(2,3) + 3 * a + a ** 2 / 4

>>> poly2
Symbolic('2/3 + 3 * a + 1/4 * a ** 2')
The poly and poly2 objects are equal indeed:
>>> poly-poly2
Symbolic('0')
Here follows some examples what manipulations can be done with symbolic objects:
>>> poly.diff('a') # differentation
Symbolic('3 + 1/2 * a')
>>> poly.integrate('a') # antiderivative
Symbolic('1/12 * a ** 3 + 2/3 * a + 3/2 * a ** 2')
>>> poly.integrate(Range('a',0,1)) # definite integration
Symbolic('9/4')
>>> poly + 3 * poly # arithmetic operations
Symbolic('8/3 + a ** 2 + 12 * a')
>>> (poly ** 2).expand() # expansion
Symbolic('4/9 + 4 * a + 1/16 * a ** 4 + 3/2 * a ** 3 + 28/3 * a ** 2')
>>> poly.substitute('a','(b+1)/3') # substitution
Symbolic('5/3 + b + 1/4 * (1/3 + 1/3 * b) ** 2')
>>> exp(poly) # elementary functions exp, ln, log, sin, cos etc.
Symbolic('exp(2/3 + 3 * a + 1/4 * a ** 2)')
>>> lhs = Symbolic('(a and b).implies(c)') # propositional calculus
>>> rhs = Symbolic('a.implies(b.implies(c))')
>>> lhs == rhs
Symbolic('(c | ~(a & b)) == (c | ~a | ~b)')
>>> lhs.equiv(rhs).expand()
Symbolic('TRUE')

Symbolic manipulation model

In doing symbolic manipulations there are two basic types of objects, tables summarize the corresponding Symbolic classes:
  1. objects that represent field values:
    Field, F Class name
    Subclasses
    Singleton subclasses
    booleans, B
    Boolean, Symbol

    TRUE, FALSE
    set of numbers, N
    Number, Symbol
    Decimal, Rational

    set of rational numbers, Q
    Rational, Symbol
    Integer
    Half, Infinity, NegativeInfinity, NaN
    set of integers, Z
    Integer, Symbol

    Zero, One, NegativeOne
    set of real numbers, R
    Decimal, Symbol

    Pi, Exp1
    set of complex numbers, C


    ImaginaryUnit, NegativeImaginaryUnit
    set of functions, NN
    SymbolicFunction, ElementaryFunction, Symbol

    Ln, Log, Exp, Sin, Cos, Sqrt
    set of operators, FF ^ FF
    SymbolicOperator, Symbol
    SymbolicFunction, ElementaryFunction
    Differential, Integral, SymbolicFunctionGenerator
    Notes:
    • To model complex numbers a singleton ImaginaryUnit is introduced, for example, Symbolic('2/3 + 4 * I') is an object representing 3/4+4j.
    • Singletons Zero, One, NegativeOne, Half are defined for efficiency.
    • Singletons Infinity, NegativeInfinity, and NaN are represented as rational numbers 1/0, -1/0, 0/0, respectively.
    • To model quaternions, one should introduce additional singletons ImaginaryUnit2 and ImarinaryUnit3.
    TODO: define set of matrices, vectors, polynomials.
  2. objects that represent operations defining mapping between values of possibly different fields:
    Operation
    Class name
    From field
    To field
    arithmetic operations
    Add, Mul, Power
    N x N x .., N x N x .., N x N
    N
    boolean operations
    Not, And, XOr, Or
    B, B x B x ..., B x B x .., B x B x ... B
    relational operations
    Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual
    N x N B
    elementary functions
    Exp, Sin, Cos, Sqrt
    N N
    operators
    Differential, Integral
    NN NN
    applying functions
    Apply
    FF x F x F x ...
    F
    applying operators
    Apply
    FF ^ FF x F
    Notes:
    • all operations may return unevaluated mappings.

Symbolic parser

Symbolic provides a parser for symbolic expressions to ease creating symbolic objects. The syntax of symbolic expressions is borrowed from Python syntax rules with some extensions like parsing rational numbers. The syntax rules are the following:
<expr> = <or-test>
<or-test> = [ <or-test> or ] <xor-test>
<xor-test> = [ <xor-test> xor ] <and-test>
<and_test> = [ <and-test> and ] <not-test>
<not-test> = [ not ] <relational>
<relational> = [ <arith> <rel-op> ] <arith>
<rel-op> = == | <> | != | < | <= | > | >= | in | not in
<arith> = [ <arith> <add-op> ] <term>
| <factor>
<factor> = [ <add-op> ] <term>
<add-op> = + | -
<term> = [ <term> <mult-op> ] <power>
<mult-op> = * | /
<power> = <primary> [ <power-op> <power> ]
<power-op> = **
<primary> = <atom>
| <attr-ref>
| <slicing>
| <call>
<atom> = <identifier> | <literal> | <parenth>
<identifier> = <letter> [ <alphanumeric_character> ]...
<literal> = <int-literal>
| <float-literal>
| <logical-literal>
<logical-literal> =
True | False
<parenth> = ( <expr-list> )
<attr-ref> = <primary> . <identifier>
<slicing> = <primary> [ <subscript-list> ]
<subscript> = <expr> | <slice>
<slice> = [ <expr> ] : [ <expr> ] [ : <expr> ]
<call> = <primary> ( [ <argument-list> ] )
<argument> = [ <identifier> = ] <expr>
For each rule the symbolic.parser module provides a class to parse string containg an expression satisfying the particular syntax rule. For example, the most general parser class is Expr:
>>> from symbolic.parser import *
>>> Expr('a+1')
Arith('a + 1')
>>> Expr('a+1').torepr()
"Arith(Identifier('a'), '+', Int_Literal('1'))"
>>> Expr('4/5').torepr()
"Term(Int_Literal('4'), '/', Int_Literal('5'))"
Other parser classes are: Or_Test, XOr_Test, And_Test, Not_Test, Relational, Arith, Factor, Term, Power, Primary, Parenth, Identifier, Int_Literal, Float_Literal, Logical_Literal, Attr_Ref, Slicing, Subscript, Slice, Call, Argument. To translate parsed syntax tree to a symbolic object, use .tosymbolic() method:
>>> import symbolic.api
>>> Expr('a+1').tosymbolic()
Symbolic('1 + a')

Symbolic classes and the Symbolic class

Symbolic objects are instances of Symbolic classes which all have a common base class Symbolic:
symbolic_object = Symbolic('<string>') # parse '<string>' and construct the corresponding symbolic object
Symbolic objects are strictly ordered and hashable. The Symbolic class defines default methods for addition, multiplication, power, relational, boolean, and functional operations:
Operations
Results
+a, -a
a, Mul(-1, a)
a ** b
Power(a, b)
a * b, a / b
Mul(a, b), Mul(a, b ** -1)
a + b, a - b
Add(a, b), Add(a, -b)
a.ncmul(b), a.ncdiv(b)
NcMul(a, b), NcMul(a, b ** -1)
a == b
Equal(a, b)
a <> b, a != b
NotEqual(a, b)
a < b, a > b
Less(a, b), Less(b, a)
a <= b, b >= a
LessEqual(a, b), LessEqual(b, a)
a(b, c, ...)
Apply(a, b, c, ...)
~a, a & b, a | b
Not(a), And(a, b), Or(a, b)
not a
bool(a.is_false())
cmp(a, b)
a.compare(b)
In addition, the following methods are defined:
Method calls
Results
Comments
a.is_equal(b)
True, False
formal equality, the correctness of the False value may depend on the contents
a.is_positive(), a.is_negative(), a.is_true(), a.is_false()
True, False, None
value properties, None is returned when the result is undefined
a.todecimal()
symbolic object
transform all number values in a to Decimal objects; use Symbolic.set_precision(prec) to set the precision to prec, default is 28.
a.expand()
symbolic object
open parenthesis, expand integer powers, etc
a.substitute(b,c)
symbolic object
replace all occurances of b with c in a
a.symbols()
set object
find all Symbol objects in a
a.free_symbols()
set object
find all Symbol objects in a that are "free", i.e. can be considered as parameters. For example, the integration variable in a definite integral is not free.
a.diff(b)
Differential(b)(a)
apply differential operator with respect to b to a
a.integrate(b)
Integral(b)(a)
apply indefinite integral operator with respect to b to a
a.integrate(Range(b, c, d))
Integral(b, c, d)(a)
apply definte integral operator with respect to b over the interval [c, d] to a
Implementation notes
  1. All Symbolic subclasses call Symbolic.__new__() method to initialize a symbolic object. The Symbolic.__new__() method carries out two tasks: First, it calls the .init() method of a Symbolic subclass with the same arguments used in constructing the symbolic object. The .init() method initializes symbolic object internal state. Second, the .flags attribute is set to hold an AttributeHolder object. The AttributeHolder object is used to save the results of .calc_*() methods that may take too long time to compute and/or when these results are often needed.
  2. A Symbolic subclass must define .astuple() method that returns a tuple with the first item equal to the name of a class followed by the arguments used in constructing the symbolic object.
  3. To make Symbolic subclasses available to all usage cases and avoiding various importing issues then Symbolic subclasses are set as the attrributes of the Symbolic class. For example, doing from symbolic.api import Symbolic is sufficient to have access to all Symbolic subclasses as follows: Symbolic.Symbol, Symbolic.Number, etc.

The Symbol class and the Symbolic_namespace dictionary

Symbol object represents an arbitrary symbol with a label:
symbol_obj = Symbol(label = None)
If label is not specified then an unique label will be generated:
>>> a = Symbol('a')
>>> a
Symbolic('a')
>>> b = Symbol()
>>> b
Symbolic('Symbol_object_3')
Symbol objects are created within a namespace of symbols that is a dictionary named as Symbolic_namespace. If there exists no Symbolic_namespace variable in locals() then it will be automatically created. Only those Symbol objects are saved in Symbolic_namespace that have constructed with specified label. For example,
>>> from symbolic.api import *
>>> Symbolic_namespace # assume that no symbolic objects has been created yet
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'Symbolic_namespace' is not defined
>>> a = Symbol('a')
>>> Symbolic_namespace
{'a': Symbolic('a')}
The idea of saving Symbol objects to Symbolic_namespace is that when a symbolic expression contains symbols with the same label then in a created symbolic object these symbols refer to the same Symbol object:
>>> expr = Symbolic('1+a')
>>> expr.seq[0] is a
True
To perform symbolic manipulations isolated from global scope one must define Symbolic_namespace object within the current scope explicitly. For example,
>>> def foo():
... b = Symbol('b')
... return Symbol('a')
...
>>> a = Symbol('a') # this will create a Symbolic_namespace dictonary
>>> Symbolic_namespace
{'a': Symbolic('a')}
>>> foo() is a
True
>>> Symbolic_namespace # note that the Symbol object b in foo() is saved in the parent Symbolic_namespace dictonary
{'a': Symbolic('a'), 'b': Symbolic('b')}
>>> def bar():
... Symbolic_namespace = {}
... c = Symbol('c')
... return Symbol('a')
...
>>> bar() is a
False
>>> Symbolic_namespace # note that the Symbol object c in bar() is saved in the local Symbolic_namespace dictonary
{'a': Symbolic('a'), 'b': Symbolic('b')}
>>> bar() == a # though the Symbol objects are different, they are considered equal if their labels are equal
True

Here function foo() uses global Symbolic_namespace ("global" in the sense that it is the first Symbolic_namespace object found in parent scope of calling foo()) while function bar() defines isolated Symbolic_namespace for holding local symbol objects.

The Number class and its subclasses

Number object represents an arbitrary number with the given value information:
number_obj = Number(numer, denom) # construct rational number, equivalent to Rational(numer, denom); numer, denom must by Python int or long
number_obj = Number(numer)
# construct integer, equivalent to Integer(numer); numer must be Python int or long
number_obj = Number(num)
# construct arbitrary precision decimal floating point number, equivalent to Decimal(num); num must be Python float, decimal.Decimal, or str
In addition to finite number values, concepts like infinity, not-a-number, imaginary-unit and few irrational number constants are defined:
infinity = Infinity() # represents Rational(1,0) == oo, available as infinity in symbolic.api namespace
neg_infinity = NegativeInfinity() # represents Rational(-1,0) == -oo
nan = NaN()
# represents Rational(0, 0), available as NaN in symbolic.api namespace
I = ImaginaryUnit()
# represents sqrt(-1), available as I in symbolic.api namespace
neg_I = NegativeImaginaryUnit()
# represents -sqrt(-1)
E = Exp1()
# represents exp(1), avaliable as E in symbolic.api namespace
pi = Pi()
# represents pi, available as Pi in symbolic.api namespace
The number objects have additional methods:
Method call
Comment
int(n)
return integer part of n
float(n)
return Python float value of n
abs(n)
return absolute value of n
Examples
>>> n=Number(3,4)
>>> n
Symbolic('3/4')
>>> n + 1
Symbolic('7/4')
>>> n / 0
Symbolic('Inf')
>>> Number('1.2') + 1

Symbolic('2.2')
The default precision, 28, for Decimal number operations is determined by the decimal module context. The value of the precision can be obtained and reset with the Symbolic.set_precision(prec=None) function:
>>> Symbolic.set_precision() # the current precision
28
>>> 1/Decimal(3)
Symbolic('0.3333333333333333333333333333')
>>> prev_precision = Symbolic.set_precision(8) # decrease the precision
>>> 1/Decimal(3)
Symbolic('0.33333333')
>>> Symbolic.set_precision(prev_precision) # restore the original precision
8
Implementation notes
  1. Numbers 0, 1, +1, 1/2 are represented by singleton number subclasses Zero, One, NegativeOne, Half, respectively.
  2. ImaginaryUnit, NegativeImaginaryUnit, Exp1, Pi are not subclasses of Number.
  3. The nominator and denominator of a rational number is automatically normalized.
  4. decimal.Decimal is used to represent decimal floating point numbers. For integers and rational numbers Python int is used. The internal values of Decimal and Rational numbers are available via attributes .num and .numer, .denum, respectively. Integer class is a subclass of Rational class.
  5. TODO: when gmpy module is avaliable then use its integer, rational, floating point numbers for efficiency.

Propositional classes

Symbolic has basic support for propositional calculus. The truth and false boolean values are represented with singleton constants TRUE and FALSE, respectively. The following table summaries the use of propositional operators and methods:
Operations
Result
Comments
~a
Not(a)
Negation
a & b
And(a, b)
Conjuction
a ^ b
XOr(a, b)
Exlusive disjunction
a | b
Or(a, b)
Disjunction
a.implies(b)
Or(Not(a), b)
Implication
a.equiv(b)
Not(XOr(a, b))
Equivalence
bool(TRUE), bool(FALSE)
True, False
conversion of boolean values to Python boolean values
When using Symbolic parser then instead of symbols ~, |, ^, & one can use keywords not, or, xor, and, respectively. However, these keywords cannot be used in a Python expression in which case they are interpreted differently.

Example: The Truth Table

>>> st = lambda s: s.tostr()[0]
>>> expr_list = ['p', 'q', '~p', 'p & q', 'p ^ q', 'p | q', 'p.implies(q)', 'p.equiv(q)']
>>> table = [[s.center(min(max(len(s)+2,8),14)) for s in expr_list]]
>>> for p, q in zip([TRUE, TRUE, FALSE, FALSE],[TRUE, FALSE, TRUE, FALSE]):
... table.append([expr.substitute('p',p).substitute('q',q).tostr().center(len(label))
... for expr,label in zip(map(Symbolic, expr_list),table[0])])
>>> print 'n'.join([''.join(row) for row in table])
p q ~p p & q p ^ q p | q p.implies(q) p.equiv(q)
TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE
TRUE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
FALSE TRUE TRUE FALSE TRUE TRUE TRUE FALSE
FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE

aadad

Monday, July 24, 2006

f2py history and future

  1. Before there existed any tool for wrapping Fortran codes to Python, Python C/API modules for interfacing Fortran programs had to be written by hand. In 1999 I had to create several modules in such a way to wrap various Fortran tools from the Netlib. This work was tedious (some of functions had more than 20 arguments, only few of them made sense for the problems that they solved). I realized that most of the writing could be done automatically.
  2. On 9th of July, 1999, the first lines of the tool was written. A prototype of the tool was ready to use in only three weeks. During this time Travis Oliphant joined to the project and shared his valuable knowledge and experience; the call-back mechanism was his major contribution. Then I gave the tool to public under the name FPIG - Fortran to Python Interface Generator. The tool contained only one file f2py.py. This code is available here.
  3. By autumn, 1999, it was clear that a better implementation was needed as the debugging process became very tedious. So, I reserved some time and rewrote the tool from scratch. The most important result of this rewriting was that the code was able to read real Fortran codes and determines the signatures of the Fortran routines. The main attention was concentrated in this particular part so that the tool could read arbitrary Fortran 77/90/95 codes. On the other hand, the other important task of the tool, that is, generating Python C/API functions, needed some work. In public, this version of the tool was called f2py2e - Fortran to Python C/API generator, the Second Edition.
  4. So, a month before The New Year 2000, I started the third iteration of the f2py development. Now the main attention was to have a good C/API module constructing code. By 21st of January, 2000, the tool of generating wrapper functions for Fortran routines was ready. It had many new features and was more robust than ever.
  5. In 25th of January, 2000, the first public release of f2py was announced (version 1.116).
  6. Starting from 20th of June, 2000, f2py users could discuss about f2py in a newly created mailing list, f2py-users at cens dot ioc dot ee, that is still active. The arvhives are available here.
  7. In 12th of September, 2000, the second public release of f2py was announced (version 2.264). It now has among other changes a support for Fortran 90/95 module routines. The old f2py User Guide is available here.
  8. During the period of 2000-2002 f2py was actively developed and three more public releases was made. The history of this period is available here.
  9. By the sixth public release of f2py in December 8, 2002, the f2py users guide was completely revised. From that date until January 30, 2005, f2py was actively maintained as the code base was rather stable.
  10. In October 28, 2005, the f2py2e code base (f2py version 2.46.243.2022) from the CENS CVS server was copied to SciPy SVN server under the numpy source tree (f2py version 2.1381). From that moment on all the development of f2py was assuming numpy array backend, the support for Numeric and numarray backends where dropped as obsolete and numpy array protocols handled these old array objects. The old f2py2e code is kept in the CVS server for backwards compatipility.
  11. By the spring of 2006 the f2py was stable and well suited for wrapping Fortran 77, simple Fortran 95 and even C codes. The only feature that f2py was missing was wrapping Fortran 95 derived types. Though I got some patches from f2py users that implemented some basic support for derived types but they never got applied to the f2py code base due to lack of time of reviewing them and my gut feeling that it would be too tedious and difficult with the current f2py code base - f2py never had a strict Fortran parser that would be necessary for interpreting Fortran codes.
  12. In May 2006, I started to write Fortran 77/90/95/2000 code parser code in Python. By now the code is stable but still under active development. The code is available in numpy/f2py/lib directory. The work is supported by the Enthought Inc.