SymPy `subs` Not Doing Anything - sympy

I have a differential equation for which I use sympy.solvers.ode.dsolve to solve, I get out
___________ ___________
-x⋅╲╱ E - V_max x⋅╲╱ E - V_max
ψ(x) = C₁⋅ℯ + C₂⋅ℯ
From (I put some of the code to I used to generate this equation at the end):
2
d
-ψ(x)⋅E + ψ(x)⋅V_max + ───(ψ(x)) = 0
2
dx
This is all well and good, the problem comes when I know that C₁ and C₂ happen to be equal and want to substitute one for the other. So I try something like
psi_high.subs( sp.Symbol( "C_2" ), sp.Symbol( "C_1" ) )
However it just comes out the same as before
___________ ___________
-x⋅╲╱ E - V_max x⋅╲╱ E - V_max
ψ(x) = C₁⋅ℯ + C₂⋅ℯ
I am thinking this may be a memory issue, that a reference to a sympy.Symbol object must refer to not only a sympy.Symbol object with the same value/symbol but which also must be the same underlying object.
This is only speculation (but I can say, do psi_high.subs( x, 0 ) and it works), but my question is how do I resolve it?
Curiously, it seems to work here (I did try this using the sympy.symbols function and by enclosing the symbol references in a tuple and list like shown in the question)
Thanks!
well_length = sq.Quantity( 'L' )
highest_potential = sq.Quantity( "V_max" )
x = sp.Symbol( 'x' )
m = sq.Quantity( 'm' )
hbar = sq.Quantity( "hbar" )
total_energy = sq.Quantity( 'E' )
inverse_total_energy = 1.0 / total_energy
psi_symbol = ud.lookup( "GREEK SMALL LETTER PSI" )
psi = sp.Function( "psi" )
second_derivative = sp.Derivative( psi( x ), x, 2 )
make_shrodinger_left = lambda potential, psi_parameter : ( second_derivative + ( psi( psi_parameter ) * potential ) )
make_shrodinger_right = lambda psi_parameter : total_energy * psi( psi_parameter )
make_psi_equal = lambda input_value, value : sp.Eq( psi( sp.Eq( x, input_value ) ), value )
set_equal = lambda to_set, value : sp.Eq( to_set, value )
shrodinger_left_high = sp.simplify( make_shrodinger_left( highest_potential, x ) )
shrodinger_right = make_shrodinger_right( x )
high_diff = sp.simplify( set_equal( shrodinger_left_high - shrodinger_right, 0 ) )

Here's a simpler example:
In [3]: eq = Eq(f(x).diff(x, 2), 0)
In [4]: eq
Out[4]:
2
d
───(f(x)) = 0
2
dx
In [5]: sol = dsolve(eq)
In [7]: sol
Out[7]: f(x) = C₁ + C₂⋅x
We can inspect these symbols:
In [8]: sol.free_symbols
Out[8]: {C₁, C₂, x}
In [9]: [s.name for s in sol.free_symbols]
Out[9]: ['C2', 'C1', 'x']
Note that there are no underscores in the symbol names. What we want to do then is:
In [10]: sol.subs(Symbol("C1"), Symbol("C2"))
Out[10]: f(x) = C₂⋅x + C₂

Related

Sympy: Is it possible use function collect() to IndexedBase variables?

I'm trying to use the function collect() to simplify mi expression . My desired result is
My code:
from sympy import *
#index
i = symbols('i' , integer = True )
#constants
a = symbols( 'a' )
#variables
alpha = IndexedBase('alpha', positive=True, domain=QQ)
index = (i, 1, 3)
rho = symbols( 'rho')
U = product( alpha[i]**(1/(rho-1)) , index )
U
:
My solution attempt:
U = U.subs(1/(rho-1),a)
collect(U,rho, evaluate=False)[1]
:
What I'm doing wrong?
You must be using a fairly old version of SymPy because in recent versions the form that you wanted arises automatically. In any case you should be able to use powsimp:
In [9]: U
Out[9]:
a a a
alpha[1] ⋅alpha[2] ⋅alpha[3]
In [10]: powsimp(U, force=True)
Out[10]:
a
(alpha[1]⋅alpha[2]⋅alpha[3])
https://docs.sympy.org/latest/tutorials/intro-tutorial/simplification.html#powsimp

Sympy tensorproduct and tensorcontraction

I want to do two computations with sympy and one substitution:
The first is: "E:E" or in index notation "E_{ij}E{ji}"
The second is: "T_{ijmn} T_{mnkl}"
After I do these calculations I would like to replace the symbols with numeric values.
I have the following code:
import sympy as sp
import numpy as np
sp.init_printing(pretty_print=False)
# e = sp.MatrixSymbol('e',3,3)
# E = sp.Matrix(e)
def foo():
e = sp.MatrixSymbol('e',3,3)
E = sp.Matrix(e)
result1 = sp.tensorcontraction( sp.tensorproduct(E, E), (0, 2), (1,3))
T = sp.tensorproduct(E, E)
result2 = sp.tensorcontraction( sp.tensorproduct(T, T), (2,4), (3,5))
return [result1, result2]
# Verification
res1, res2 = foo()
E_num = np.array([[1,2,3],
[4,5,6],
[7,8,12]])
res1 = res1.subs({ E[0,0]:E_num[0,0], E[0,1]:E_num[0,1], E[0,2]:E_num[0,2],
E[1,0]:E_num[1,0], E[1,1]:E_num[1,1], E[1,2]:E_num[1,2],
E[2,0]:E_num[2,0], E[2,1]:E_num[2,1], E[2,2]:E_num[2,2]})
res2 = res2.subs({ E[0,0]:E_num[0,0], E[0,1]:E_num[0,1], E[0,2]:E_num[0,2],
E[1,0]:E_num[1,0], E[1,1]:E_num[1,1], E[1,2]:E_num[1,2],
E[2,0]:E_num[2,0], E[2,1]:E_num[2,1], E[2,2]:E_num[2,2]})
check1 = np.einsum("ij,ji", E_num, E_num) - res1
T1 = np.einsum("ij,mn->ijmn", E_num, E_num)
T2 = np.einsum("mn,kl->mnkl", E_num, E_num)
check2 = np.einsum("ijmn,mnkl->ijkl", T1,T2) - res2
In the above code I cannot replace the E matrix with numerical values as the symbolic variable is defined inside the function. I can do it if I define them outside. Any way to look into the expression and get the symbols to replace?
Additionally, check1 is not zero as it appears to be doing E_{ij}E_{ij}, whilst check2 appears to be correct. Am I not asking to contract the correct indices?
Best Regards
Your res1 - before numeric substitute is
In [18]: res1
Out[18]:
2 2 2 2 2 2 2 2 2
e₀₀ + e₀₁ + e₀₂ + e₁₀ + e₁₁ + e₁₂ + e₂₀ + e₂₁ + e₂₂
That's the same as
np.einsum('ij,ij', E_num, E_num)
or
np.sum(E_num * E_num)
The einsum contraction of the array with its transpose:
In [18]: np.einsum("ij,ji", E_num, E_num)
Out[18]: 324
In [19]: np.einsum("ij,ij", E_num, E_num.T)
Out[19]: 324
Using E.T in your res1 calcultion gets the same thing
In [24]: sp.tensorcontraction( sp.tensorproduct(E, E.T), (0, 2), (1,3))
Out[24]:
2 2 2
e₀₀ + 2⋅e₀₁⋅e₁₀ + 2⋅e₀₂⋅e₂₀ + e₁₁ + 2⋅e₁₂⋅e₂₁ + e₂₂
In [25]: _.subs({ E[0,0]:E_num[0,0], E[0,1]:E_num[0,1], E[0,2]:E_num[0,2],
...: E[1,0]:E_num[1,0], E[1,1]:E_num[1,1], E[1,2]:E_num[1,2],
...: E[2,0]:E_num[2,0], E[2,1]:E_num[2,1], E[2,2]:E_num[2,2]})
Out[25]: 324
I haven't read the docs for tensorproduct or tensorcontraction.

Symbolic entropy maximization in SymPy

A simple problem of entropy maximization in statistical mechanics in Physics, is formulated as follows.
The goal is to maximize an entropy function (LaTeX is still missing in stackexchange?):
H = - sum_x P_x ln P_x
subject to the following constraints: the normalization constraint
1 = sum_x P_x
and the constraint of average energy
U = sum_i E_x P_x
where the index i runs over x=1,2,...,n. E_x represents the energy of the system when it is in microscopic state x and P_x is the probability for the system to be in the microscopic state x.
The solution to such a problem can be obtained by the method of Lagrange multipliers. In this context, it works as follows...
Firstly, the Lagrangian is defined as
L = H + a( 1 - sum_i P_x ) + b( U - sum_i P_x E_x )
Here, a and b are the Lagrange multipliers. The Lagrangian L is a function of a, b and the probabilities P_x for x=1,2,...,n. The term a( 1 - sum_x P_x ) correspond to the normalization constraint and the term b( E - sum_x P_x E_x ) to the average energy constraint.
Secondly, the partial derivatives of L with respect to a, b and the P_x for the different x=1,2,...,n are calculated. These result in
dL/da = 1 - sum_x P_x
dL/db = E - sum_x E_x P_x
dL/P_x = dH/P_x - a - b E_x
= - ln P_x - 1 - a - b E_x
Thirdly, we find the solution by equating these derivatives to zero. This makes sense since there are 2+n equations and we have 2+n unknowns: the P_x, a and b. The solution from these equations read
P_x = exp( - b E_x ) / Z
where
Z = sum_x exp( - b E_x )
and b is implicitly determined by the relation
E = sum_x P_x E_x = ( 1 / Z ) sum_x exp( -b E_x ) E_x
Now that the "mathematical" problem is defined, lets state the "computational" problem (which is the one I want to ask here).
The computational problem is the following: I would like to reproduce the previous derivation in Sympy.
The idea is to automatize the process so, eventually, I can attack similar but more complicate problems.
I already made certain progress. Still, I think I haven't used the best approach. This is my solution.
# Lets attempt to derive these analytical result using SymPy.
import sympy as sy
import sympy.tensor as syt
# Here, n is introduced to specify an abstract range for x and y.
n = sy.symbols( 'n' , integer = True )
a , b = sy.symbols( 'a b' ) # The Lagrange-multipliers.
x = syt.Idx( 'x' , n ) # Index x for P_x
y = syt.Idx( 'y' , n ) # Index y for P_y; this is required to take derivatives according to SymPy rules.
>>> P = syt.IndexedBase( 'P' ) # The unknowns P_x.
>>> E = syt.IndexedBase( 'E' ) # The knowns E_x; each being the energy of state x.
>>> U = sy.Symbol( 'U' ) # Mean energy.
>>>
>>> # Entropy
>>> H = sy.Sum( - P[x] * sy.log( P[x] ) , x )
>>>
>>> # Lagrangian
>>> L = H + a * ( 1 - sy.Sum( P[x] , x ) ) + b * ( U - sy.Sum( E[x] * P[x] , x ) )
>>> # Lets compute the derivatives
>>> dLda = sy.diff( L , a )
>>> dLdb = sy.diff( L , b )
>>> dLdPy = sy.diff( L , P[y] )
>>> # These look like
>>>
>>> print dLda
-Sum(P[x], (x, 0, n - 1)) + 1
>>>
>>> print dLdb
U - Sum(E[x]*P[x], (x, 0, n - 1))
>>>
>>> print dLdPy
-a*Sum(KroneckerDelta(x, y), (x, 0, n - 1)) - b*Sum(KroneckerDelta(x, y)*E[x], (x, 0, n - 1)) + Sum(-log(P[x])*KroneckerDelta(x, y) - KroneckerDelta(x, y), (x, 0, n - 1))
>>> # The following approach does not work
>>>
>>> tmp = dLdPy.doit()
>>> print tmp
-a*Piecewise((1, 0 <= y), (0, True)) - b*Piecewise((E[y], 0 <= y), (0, True)) + Piecewise((-log(P[y]) - 1, 0 <= y), (0, True))
>>>
>>> sy.solve( tmp , P[y] )
[]
>>> # Hence, we try an ad-hoc procedure
>>> Px = sy.Symbol( 'Px' )
>>> Ex = sy.Symbol( 'Ex' )
>>> tmp2 = dLdPy.doit().subs( P[y] , Px ).subs( E[y] , Ex ).subs( y , 0 )
>>> print tmp2
-Ex*b - a - log(Px) - 1
>>> Px = sy.solve( tmp2 , Px )
>>> print Px
[exp(-Ex*b - a - 1)]
Is there a "better" way to proceed? In particular, I don't like the idea of substituting P[y] with Px and E[y] with Ex in order to solve the equations. Why the equations cannot be solved in terms of P[y]?

Gradient Descent in Python 2

Part of my assignment is to implement the Gradient Descent to find the best approximation of values c_1, c_2 and r_1 for the function
.
Given is only a list of 30 y-values corresponding to x from 0 to 30. I am implementing this in Enthought Canopy like this:
First I start with random values:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as pyplt
c1 = -0.1
c2 = 0.1
r1 = 0.1
x = np.linspace(0,29,30) #start,stop,numitems
y = c1*np.exp(r1*x) + (c1*x)**3.0 - (c2*x)**2.0
pyplt.plot(x,y)
values_x = np.linspace(0,29,30)
values_y = np.array([0.2, -0.142682939241718, -0.886680607211679, -2.0095087143494, -3.47583798747496, -5.24396052331554, -7.2690008846359, -9.50451068338581, -11.9032604272567, -14.4176327390446, -16.9998176236069, -19.6019094345634, -22.1759550265352, -24.6739776668383, -27.0479889096801, -29.2499944927101, -31.2319972651608, -32.945998641919, -34.3439993255969, -35.3779996651013, -35.9999998336943, -36.161999917415, -35.8159999589895, -34.9139999796348, -33.4079999898869, -31.249999994978, -28.3919999975061, -24.7859999987616, -20.383999999385, -15.1379999996945])
pyplt.plot(values_x,values_y)
The squared error is quite high:
def Error(y,y0):
return ( (1.0)*sum((y-y0)**2.0) )
print Error(y,values_y)
Now, to implement the gradient descent, I derived the partial derivative functions for c_1, c_2 and r_1 and implemented the Gradient Descent:
step_size = 0.0000005
accepted_Error = 50
dc1 = c1
dc2 = c2
dr1 = r1
y0 = values_y
previous_Error = 100000
left = True
for _ in range(1000):
gc1 = (2.0) * sum( ( y - dc1*np.exp(dr1*x) - (dc1*x)**3 + (dc2*x)**2 ) * ( -1*np.exp(dr1*x) - (3*(dc1**2)*(x**3)) ) )
gc2 = (2.0) * sum( ( y - dc1*np.exp(dr1*x) - (dc1*x)**3 + (dc2*x)**2 ) * ( 2*dc2*(x**2) ) )
gr1 = (2.0) * sum( ( y - dc1*np.exp(dr1*x) - (dc1*x)**3 + (dc2*x)**2 ) * ( -1*dc1*x*np.exp(dr1*x) ) )
dc1 = dc1 - step_size*gc1
dc2 = dc2 - step_size*gc2
dr1 = dr1 - step_size*gr1
y1 = dc1*np.exp(dr1*x) + (dc1*x)**3.0 - (dc2*x)**2.0
current_Error = Error(y0,y1)
if (current_Error > accepted_Error):
print currentError
else:
break
if (current_Error > previous_Error):
print currentError
print "DIVERGING"
break
if (current_Error==previous_Error):
print "CAN'T IMPROVE"
break
previous_Error = current_Error
However, the error is not improving at all, and I tried to vary the step size. Is there a mistake in my code?

CVXOpt op argument error

I am testing CVXOpt with the following model
>>> from cvxopt.modeling import op
>>> x = variable()
>>> y = variable()
>>> c1 = ( 2*x+y >> c2 = ( x+2*y >> c3 = ( x >= 0 )
>>> c4 = (y >= 0 )
>>> lp1 = op(-4*x-5*y, [c1,c2,c3,c4])
However, I get two problems:
Invalid argument for constraints for the last line of code. I've checked the CVXOpt documentation and the way is coded seems to be the right way to do it.
Less important but still it will be nice if someone could tell me why i get a syntax error when writing all constraints (c1, c2,..) in the same line as shown here. Instead i've had to use different lines for each.
There seem to be some problems with the syntax you're using. Also please always make sure your code snipped allows to run the code. The correct way to write your optimization problem is:
>>> from cvxopt.modeling import op
>>> from cvxopt.modeling import variable
>>> x = variable()
>>> y = variable()
>>> c1 = ( 2*x+y <= 7) # You forgot to add the (in-) equality here and below
>>> c2 = ( x+2*y >= 2)
>>> c3 = ( x >= 0 )
>>> c4 = (y >= 0 )
>>> lp1 = op(-4*x-5*y, [c1,c2,c3,c4])
>>> lp1.solve()
pcost dcost gap pres dres k/t
0: -1.0900e+01 -2.3900e+01 1e+01 0e+00 8e-01 1e+00
1: -1.3034e+01 -2.0322e+01 9e+00 1e-16 5e-01 9e-01
2: -2.4963e+01 -3.5363e+01 3e+01 3e-16 8e-01 3e+00
3: -3.3705e+01 -3.3938e+01 2e+00 3e-16 4e-02 4e-01
4: -3.4987e+01 -3.4989e+01 2e-02 5e-16 4e-04 5e-03
5: -3.5000e+01 -3.5000e+01 2e-04 3e-16 4e-06 5e-05
6: -3.5000e+01 -3.5000e+01 2e-06 4e-16 4e-08 5e-07
Optimal solution found.
In the fifth and sixth line, you forgot to add the (in-) equality. You have to state explicitly what you are comparing to, i.e. the inequality sign and the value you want to compare to.
You can write all (in-) equalities on one line if you use a semicolon to separate them. Again, be careful with your syntax, you forgot to close some brackets in your example code.
>>> c1 = ( 2*x+y <= 7); c2 = ( x+2*y >= 2); c3 = ( x >= 0 ); c4 = (y >= 0 )