# Ordering Products

Kay Schluehr kay.schluehr at gmx.net
Tue Jul 19 08:00:47 CEST 2005

```Ron Adam wrote:
> Kay Schluehr wrote:
> > Here might be an interesting puzzle for people who like sorting
> > algorithms ( and no I'm not a student anymore and the problem is not a
> > students 'homework' but a particular question associated with a
> > computer algebra system in Python I'm currently developing in my
> > sparetime ).
> >
> > For motivation lets define some expression class first:
>
>
> This works for (simple) expressions with mixed multiplication and addition.
>
>
> class F(list):
>      def __init__(self,*x):
>          #print '\nF:',x
>          list.__init__(self,x)
>          return A(self,other)
>          return A(other,self)
>      def __mul__(self, other):
>          return M(self,other)
>      def __rmul__(self, other):
>          return M(other,self)
>      def __repr__(self):
>          return str(self[0])
>      def __order__(self):
>          for i in self:
>              if isinstance(i,A) \
>                      or isinstance(i,M):
>                  i.__order__()
>          self.sort()
>
> class A(F):
>      def __init__(self, *x):
>          #print '\nA:',x
>          list.__init__(self, x)
>      def __repr__(self):
>          self.__order__()
>          return "+".join([str(x) for x in self])
>
> class M(F):
>      def __init__(self,*x):
>          #print '\nM:',x
>          list.__init__(self,x)
>      def __repr__(self):
>          self.__order__()
>          return "*".join([str(x) for x in self])
>
>
> a = F('a')
> b = F('b')
> c = F('c')
> d = F('d')
>
> print '\n a =', a
>
> print '\n b+a+2 =', b+a+2
>
> print '\n c*b+d*a+2 =', c*b+d*a+2
>
> print '\n 7*a*8*9+b =', 7*a*8*9+b
>
>
>
>  >>>
>
>   a = a
>
>   b+a+2 = 2+a+b
>
>   c*b+d*a+2 = 2+a*d+b*c
>
>   7*a*8*9+b = 9*8*7*a+b      <--  reverse sorted digits?
>  >>>
>
>
> The digits sort in reverse for some strange reason I haven't figured out
> yet, but they are grouped together.  And expressions of the type a*(c+b)
> don't work in this example.
>
> It probably needs some better logic to merge adjacent like groups.  I
> think the reverse sorting my be a side effect of the nesting that takes
> place when the expressions are built.
>
> Having the digits first might be an advantage as you can use a for loop
> to add or multiply them until you get to a not digit.
>
> Anyway, interesting stuff. ;-)
>
> Cheers,
> Ron

Hi Ron,

I really don't want to discourage you in doing your own CAS but the
stuff I'm working on is already a bit more advanced than my
mono-operational multiplicative algebra ;)

Mixing operators is not really a problem, but one has to make initial
decisions ( e.g about associativity i.e. flattening the parse-tree )
and sub-algebra generation by means of inheritance:

>>> a,b = seq(2,Expr)
>>> type(a+b)
<class '__main__.Expr'>

>>> class X(Expr):pass
>>> x,y = seq(2,X)
>>> type(x+y)
<class '__main__.X'>

This is not particular hard. It is harder to determine correspondence
rules between operations on different levels. On subalgebras the
operations of the parent algebra are induced. But what happens if one
mixes objects of different algebras that interoperate with each other?
It would be wise to find a unified approach to make distinctive
operations visually distinctive too. Infix operators may be
re-introduced just for convenience ( e.g. if we can assume that all
algebras supporting __mul__ that are relevant in some computation have
certain properties e.g. being associative ).

##########################################################################

After thinking about M ( or Expr ;) a little more I come up with a
solution of the problem of central elements of an algebra ( at least
the identity element e is always central ) that commute with all other
elements.

Here is my approach:

# Define a subclass of list, that provides the same interface as list
and
# a customized sorting algorithm

import sets

class Factors(list):
def __init__(self,li):
list.__init__(self,li)
self.elems   = sets.Set(li)   # raw set of factors used in the
__mul__
self._center = ()             # storing central elements
commuting with
# with all others

def _get_center(self):
return self._center

def _set_center(self,center):
Center = sets.Set(center)
if not Center<=self.elems:
raise ValueError,"Subset required"
else:
self._center = Center

center = property(_get_center, _set_center)

def sort(self):
center = list(self.center)
def commutator(x,y):
if isinstance(x,(int,float,long)):  # numeral literals
should
return -1                       # always commute
if isinstance(y,(int,float,long)):
return 1
if x == y:
return 0
if x in center:
if y in center:
if center.index(x)<center.index(y):   # induce an
aritrary
return -1                         # order on
central
else:                                 # elements by
center
return 1
else:
return -1
return 0
list.sort(self,commutator)

# Define an associative multiplicative algebra

class M(object):
def __init__(self, name=""):
self.name = name
self.factors = Factors([self])    # implement factor list as
Factors

def _get_center(self):
return self.factors.center

def _set_center(self,center):
self.factors.center = center

center = property(_get_center, _set_center)

def __mul__(self, other):
p = M()
if isinstance(other,M):
other_factors = other.factors
else:
other_factors = Factors([other])
p.factors = self.factors+other_factors
return p

def __rmul__(self,other):
p = M()
p.factors = Factors([other])+self.factors
return p

def __repr__(self):
if self.name:
return self.name
else:
return "*".join([str(x) for x in self.factors])

>>> a,b,c,d = M("a"),M("b"),M("c"),M("d")
>>> y = c*3*a*d*c*b*7*c*d*a
>>> y
c*3*a*d*c*b*7*c*d*a

>>> y.center = (c,d)
>>> y.factors.sort()
>>> y
7*3*c*c*c*d*d*a*b*a

Regards,
Kay

```