Using SympyCore for custom algebra types

17 views
Skip to first unread message

nikolas

unread,
Feb 9, 2011, 6:20:41 PM2/9/11
to SympyCore
Hi there,

I am in the process of creating a software that deals with various
different interrelated algebras.

I want to describe a symbolic Operator-Algebra (featuring commutative
addition and in general non-commutative multiplication) which supports
scalar coefficients. Now your Calculus algebra would be ideal for my
scalar coefficients and it seems that my Operator Algebra could be
subclassed from your Ring algebra (ring.algebra.Ring).

from sympycore import Ring, Calculus, Symbol

class Operator(Ring):
coefftypes = (Calculus,)


However I am running into a couple of problems.
If I introduce the symbols

x = Symbol('x')
y = Symbol('y')
H = Operator('H')
K = Operator('K')

1) The multiplication of operators and 'scalar' Calculus symbols does
not commute.
x*H # --> Calculus('x*H')
H*x # --> Operator

How can I enforce that the result is always an Operator instance?

2) The expression

H*x - x*H

yields

Operator('x*H - x*H')


How can I change this behavior?
I am assuming that the solution to 1) will also solve 2) because when
I print
print (H*x - x*H).as_lowlevel()

I find that
(TERM_COEFF_DICT, {Operator('H'): Calculus('x'), Operator('1'):
Calculus('-x*H')})

Hence, it seems that the second summand is not correctly identified as
a product of an Operator H and a Calculus x but rather an identity
operator applied to a Calculus instance (-x*H).

In addition to the operator algebra, I would also like to implement an
algebra of
Operator-valued Matrices and ultimately a more abstract algebra
defined in terms of all of the above with two certain "product"-
operations (one of which is invertible, but neither is commutative).

In general, would you recommend that I try another CAS (such as
sympy?).
Also, will I run into trouble if I implement my own constructor for
the Operator algebra? If so, how can I pass custom arguments to
initialize my algebraic objects?
Some pointers in the right direction would be greatly appreciated.
Thanks,
Nikolas

Pearu Peterson

unread,
Feb 10, 2011, 7:01:21 AM2/10/11
to symp...@googlegroups.com
Hi Nikolas,

On Thu, Feb 10, 2011 at 1:20 AM, nikolas <nte...@stanford.edu> wrote:
Hi there,

I am in the process of creating a software that deals with various
different interrelated algebras.

I want to describe a symbolic Operator-Algebra (featuring commutative
addition and in general non-commutative multiplication) which supports
scalar coefficients. Now your Calculus algebra would be ideal for my
scalar coefficients and it seems that my Operator Algebra could be
subclassed from your Ring algebra (ring.algebra.Ring).

from sympycore import Ring, Calculus, Symbol

class Operator(Ring):
   coefftypes = (Calculus,)

  H
However I am running into a couple of problems.
If I introduce the symbols

x = Symbol('x')
y = Symbol('y')
H = Operator('H')
K = Operator('K')

1) The multiplication of operators and 'scalar' Calculus symbols does
not commute.
x*H # --> Calculus('x*H')
H*x # --> Operator

How can I enforce that the result is always an Operator instance?


The problem is that H.as_algebra(Calculus) converts H to Calculus
instance while it should not, it should return NotImplemented.
I guess the default as_algebra is too nice - it tries hard to convert
any symbolic object to the algebra asked. It should be changed.
For your case, there is an easy fix:


class Operator(Ring):
    coefftypes = (Calculus,)
    def as_algebra(self, cls, typeerror=True):
        return NotImplemented

With this, both problems are resolved:
>>> H*x-x*H
    Operator('0')

2) The expression

H*x - x*H

yields

Operator('x*H - x*H')
 
How can I change this behavior?

see above for a fix.
 
I am assuming that the solution to 1) will also solve 2)

yes
 
because when I print
print (H*x - x*H).as_lowlevel()
 
For debugging, as_tree can also be useful:

>>> print (H*x-y*H).as_tree()
Verbatim:
MUL[
  SYMBOL[H]
  TERM_COEFF_DICT[
    MUL[
      SYMBOL[y]
      NUMBER[-1]
    ]
    MUL[
      SYMBOL[x]
      NUMBER[1]
    ]
  ]
]


In addition to the operator algebra, I would also like to implement an
algebra of
Operator-valued Matrices and ultimately a more abstract algebra
defined in terms of all of the above with two certain "product"-
operations (one of which is invertible, but neither is commutative).

That's interesting. Hopefully sympycore will be usable for this.
 
In general, would you recommend that I try another CAS (such as
sympy?).
Also, will I run into trouble if I implement my own constructor for
the Operator algebra? If so, how can I pass custom arguments to
initialize my algebraic objects?

Yes, all sympycore instances are derived from Expr that constructor
takes either one or two arguments. When two arguments ate given,
then they must be head and data parts of an instance ---- this is used
internally. When one argument is given, then it will be converted via
the convert method to Expr instance. So, to define a custom constructor,
you have two options:
1) collect all custom arguments to a tuple and redefine convert classmethod for
interpreting this tuple.
2) define __new__ method with your choice of constructor arguments
and explicitly create Expr instance.
I guess the last option would be more general, so, here is a (meaningless) example:

>>> class MyRing(Ring):
    def __new__(cls, arg1, arg2 = 'a', arg3 = 'b'):
        s = '%s_%s_%s' % (arg1, arg2, arg3)
        obj = Expr.__new__(cls, heads.SYMBOL, s)
        return obj
...    
...    
>>> MyRing('A')
    MyRing('A_a_b')
>>> MyRing('A', 'c')
    MyRing('A_c_b')

 HTH,
Pearu

Pearu Peterson

unread,
Feb 10, 2011, 7:16:14 AM2/10/11
to symp...@googlegroups.com
On Thu, Feb 10, 2011 at 2:01 PM, Pearu Peterson <pearu.p...@gmail.com> wrote:

Also, will I run into trouble if I implement my own constructor for
the Operator algebra? If so, how can I pass custom arguments to
initialize my algebraic objects?

Yes, all sympycore instances are derived from Expr that constructor
takes either one or two arguments. When two arguments ate given,
then they must be head and data parts of an instance ---- this is used
internally. When one argument is given, then it will be converted via
the convert method to Expr instance. So, to define a custom constructor,
you have two options:
1) collect all custom arguments to a tuple and redefine convert classmethod for
interpreting this tuple.
2) define __new__ method with your choice of constructor arguments
and explicitly create Expr instance.
I guess the last option would be more general, so, here is a (meaningless) example:

>>> class MyRing(Ring):
    def __new__(cls, arg1, arg2 = 'a', arg3 = 'b'):
        s = '%s_%s_%s' % (arg1, arg2, arg3)
        obj = Expr.__new__(cls, heads.SYMBOL, s)
        return obj

Here is a more useful example:


class MyRing(Ring):
    def __new__(cls, arg1, arg2='a', arg3='b'):
        if isinstance(arg1, heads.base.Head):
            # support for internal constructor
            return Expr.__new__(cls, arg1, arg2)
        # process arguments and create Expr instance:
        obj = Expr.__new__(cls, heads.SYMBOL, '%s_%s_%s' % (arg1, arg2, arg3))
        return obj

So, a general rule of redefining Expr constructors is that the __new__ method shoud start with the `if isinstance(arg1, heads.base.Head)`
block and one must create Expr instance explicitly as shown above.

Pearu
Reply all
Reply to author
Forward
0 new messages