GSOC 2012

15 views
Skip to first unread message

Alan Bromborsky

unread,
Mar 16, 2012, 7:00:13 AM3/16/12
to sympy
One more suggestion with applications to groups, algebra, rings,
differential forms, etc. One should allow the evaluation of user
defined functions of operations on symbols (especially noncommutative
symbols). Consider the following where a1, a2, b1, and b2 are
commutative symbols and e1 and e2 are noncommutative symbols and let:

A = a1*e1+a2*e2
B = b1*e1+b2*e2
expand(A*B) = a1*b1*e1**2+a1*b2*e1*e2+a2*b1*e2*e1+a2*b2*e2**2

Normally no further simplification is possible, but if the user could
define a function q of the e's that gives:

q(e1,e1) = g111*e1+g112*e2
q(e1,e2) = g121*e1+g122*e2
q(e2,e1) = g211*e1+g212*e2
q(e2,e2) = g221*e1+g222*e2

that could be associated with the product of the e's in expand(A*B) to
do automatic expansion rather than use subs and a dictionary.
Especially since then are cases where a dictionary is not the best way
to perform the operation such as in differential forms (let * represent
^) where e1 = dx1 and e2 = dx2 and

q(a,b) = 0 if a == b and q(dx1,dx2) = dx1*dx2 and q(dx2,dx1) = -dx1*dx2
(put the product in normal order)

Likewise consider the function:

F = f1(x,y)*e1+f2(x,y)*e2

Normally:

diff(F,x) = Derivative(f1(x,y),x)*e1+Derivative(f2(x,y),x)*e2

similarly for diff(F,y), but let there be a user defined function:

d(e1) = g11(x,y)*e1+g12(x,y)*e2
d(e2) = g21(x,y)*e1+g22(x,y)*e2

so that diff(F,x) gives

diff(F,x) =
Derivative(f1(x,y),x)*e1+Derivative(f2(x,y),x)*e2+f1(x,y)*(g11(x,y)*e1+g12(x,y)*e2)+f2(x,y)*(g21(x,y)*e1+g22(x,y)*e2)

A generalization of the first type of expansion for binary products
would be for nary products where q(*args) could have a variable number
of arguments so that products of the form e1*e2*...*er could be
automatically evaluated.

Finally there are times when additional products for symbols would be
very useful such as ^ and | operators that different user supplied
evaluation functions could be applied to. The problem here is that
during expansion operations the operator precedence
defined by user supplied parenthesis in the original expression would
have to be respected since the python default precedence for the
operators might have no relation to the precedence requires by the
mathematics.

Joachim Durchholz

unread,
Mar 16, 2012, 4:02:29 PM3/16/12
to sy...@googlegroups.com
Am 16.03.2012 12:00, schrieb Alan Bromborsky:
> One more suggestion with applications to groups, algebra, rings,
> differential forms, etc. One should allow the evaluation of user defined
> functions of operations on symbols (especially noncommutative symbols).
> Consider the following where a1, a2, b1, and b2 are commutative symbols
> and e1 and e2 are noncommutative symbols and let:
>
> A = a1*e1+a2*e2
> B = b1*e1+b2*e2
> expand(A*B) = a1*b1*e1**2+a1*b2*e1*e2+a2*b1*e2*e1+a2*b2*e2**2
>
> Normally no further simplification is possible, but if the user could
> define a function q of the e's that gives:
>
> q(e1,e1) = g111*e1+g112*e2
> q(e1,e2) = g121*e1+g122*e2
> q(e2,e1) = g211*e1+g212*e2
> q(e2,e2) = g221*e1+g222*e2
>
> that could be associated with the product of the e's in expand(A*B) to
> do automatic expansion rather than use subs and a dictionary.

That sounds very useful.
In fact I see this done in mathematical texts all the time - if the
result of a simplification is too complicated, identify patterns and
push them away into helper functions.

I'm not sure how simplification would work if user-defined functions can
be added.
For example, sometimes you'd want to complicate an expression slightly
so you have more places where you can use such a function.
This might be a second step after the first one. I'm pretty sure that
the ability to put common subexpressions into functions would already be
useful, even withouth the ability to massage expressions to get more
common subexpressions.

Message has been deleted

Alan Bromborsky

unread,
Mar 17, 2012, 11:36:50 AM3/17/12
to sy...@googlegroups.com
Here is an implementation of part of the idea. Criticism is welcome. I
wish it could be done at a lower level (in the core).

#GAsympy.py

from sympy import expand,Mul,Add,Symbol,S,Expr,Wild

ONE = S(1)
ZERO = S(0)

def linear_function(expr,fct):
"""
If a sympy 'Expr' is of the form:

expr = expr_0+expr_1*a_1+...+expr_n*a_n

where all the a_j are noncommuting symbols in basis then

f(expr) = expr_0+expr_1*f(a_1)+...+expr_n*f(a_n)

is returned
"""
if expr.is_commutative:
return(expr)
expr = expand(expr)
if isinstance(expr,Mul):
x = (coefs,bases) = expr.args_cnc()
return(Mul(*coefs)*fct(bases[0]))
elif isinstance(expr,Symbol):
return(fct(expr))
elif isinstance(expr,Add):
result = ZERO
for arg in expr.args:
term = arg.args_cnc()
if term[1] == []:
result += Mul(*term[0])
else:
result += Mul(*term[0])*fct(term[1][0])
return(result)

def bilinear_function(expr,fct):
"""
If a sympy 'Expr' is of the form:

expr = expr_0+expr_1*a_1+...+expr_n*a_n+expr_11*a_1*a_1
+...+expr_ij*a_i*a_j+...+expr_nn*a_n*a_n

where all the a_j are noncommuting symbols in basis then

f(expr) = expr_0+expr_1*fa_1+...+expr_n*a_n+expr_11*fct(a_1,a_1)
+...+expr_ij*fct(a_i,a_j)+...+expr_nn*fct(a_n,a_n)

is returned
"""
p = Wild('p')

if expr.is_commutative:
return(expr)
expr = expand(expr)
if isinstance(expr,Mul): #only one additive term
x = (coefs,bases) = expr.args_cnc()
if len(bases) == 1:
W = bases[1][0].match(p**2)
if W != None: #square test
x = W[p]
return(Mul(*coefs)*fct(x,x))
else: #no bilinear term
return(expr)
else: #nonsquare bilinear term
return(Mul(*coefs)*fct(bases[0],bases[1]))
elif isinstance(expr,Symbol):
return(expr)
elif isinstance(expr,Add): #multiple additive terms
result = ZERO
for arg in expr.args:
term = arg.args_cnc()
if len(term[1]) == 0: #scalar term
result += Mul(*term[0])
elif len(term[1]) == 1: #vector or square term
W = term[1][0].match(p**2)
if W != None: #square test
x = W[p]
result += Mul(*term[0])*fct(x,x)
else: #linear term
result += Mul(*term[0])*term[1][0]
else: #bilinear term
result += Mul(*term[0])*fct(term[1][0],term[1][1])
return(result)

def derivation_function(expr,fct,x):
"""
If a sympy 'Expr' is of the form:

expr = expr_0+expr_1*a_1+...+expr_n*a_n

where all the a_j are noncommuting symbols in basis then

df(expr) = diff(expr_0,x)+diff(expr_1,x)*a_1+...+diff(expr_n,x)*f(a_n)+
expr_1*fct(a_1,x)+...+expr_n*fct(a_n,x)

is returned
"""
if expr.is_commutative:
return(diff(expr,x))
expr = expand(expr)
if isinstance(expr,Mul):
x = (coefs,bases) = expr.args_cnc()
coef = Mul(*coefs)
return(diff(coef,x)*bases[0]+coef*fct(bases[0],x))
elif isinstance(expr,Symbol):
return(fct(expr,x))
elif isinstance(expr,Add):
result = ZERO
for arg in expr.args:
term = arg.args_cnc()
coef = Mul(*term[0])
if term[1] == []:
result += diff(coef,x)
else:
result += diff(coef,x)*term[1][0]+coef*fct(term[1][0],x)
return(result)

from sympy import *

def f(e):
return(e*e)

def f2(e_a,e_b):
if e_a == e_b:
return(e_a)
else:
return(ZERO)

def df(e,x):
return(x*e)

(a1,a2,b1,b2,x) = symbols('a1,a2,b1,b2,x')
(e1,e2) = symbols('e1,e2',commutative=False)

A = e1*a1+e2*a2+a1
B = e1*b1+e2*b2+a2
AB = expand(A*B)
print 'A =',A,
print 'B =',B
print 'AB =',AB
print 'linear_function(A,f) =',linear_function(A,f)
print 'bilinear_function(AB,f2) =',bilinear_function(AB,f2)
f1 = Function('f1')(x)
f2 = Function('f2')(x)
F = f1*e1+f2*e2
print 'F =',F
print 'derivation_function(F,df,x) =',derivation_function(F,df,x)

Reply all
Reply to author
Forward
0 new messages