symbolic solution of linear equations using Sympy - python

Basically I have [5x5][5x1]=[0] and would like to have the symbolic expression of the solution.
Here is my code.
from sympy import symbols, solve
gm1, gm2, gm4 = symbols(['gm1', 'gm2', 'gm4'])
gds1, gds2, gds3, gds4, gds5 = symbols(['gds1', 'gds2', 'gds3', 'gds4', 'gds5'])
s = symbols(['s'])
Cd, CF , Cin, Ct = symbols(['Cd', 'CF', 'Cin', 'Ct'])
K = symbols(['K'])
vb, vc, ve, vout, iin = symbols(['vb', 'vc', 've', 'vout', 'iin'])
sol = solve([-(gds1+gds3+(s*Cd))*vb + (gm1+gds1)*ve + -gm1*vout, \
-gm4*vb + (gds4-gds2-(s*Cin)-(s*CF))*vc + (gds2+gm2)*ve + s*CF*vout + iin, \
gds1*vb + gds2*vc + (-(s*Ct)-gds5-gds1-gm1-gm2-gds2)*ve + gm1*vout, \
K*vc + vout], [vout])
print(sol)
but, I got this error
TypeError: can't multiply sequence by non-int of type 'Symbol'
From here, symbolic multiplication seems working just fine.
I am not sure whether I describe my problem in a way that does not comply with Sympy or something else.
What did I miss here?

The problem is in the assignment of the single symbols s and K. If instead you do:
s, K = symbols(['s', 'K'])
Or:
s = symbols('s')
K = symbols('K')
Whether you get the right answer or not is another matter :)

When you pass a list to symbols you get a list back. You can unpack that like [s] = symbols(['s']) or you can just pass a string of space or comma separated strings like x, y = symbols('x y') or x, y = symbols(','.join(['x', 'y']).
If you select manual=True you will get a solution vout=K*vc which sets the 4th equation to 0. But that was almost obvious, right? And you didn't need the other 3 equations to tell you that. So go ahead and pick up to 3 other variables for which you want to solve. There are lots of possibilities:
>>> from sympy.functions.combinatorial.numbers import nC
>>> allsym = Tuple(*eqs).free_symbols
>>> nfree = len(allsym) - 1 # always take vout
>>> print(nC(nfree, 3)) # want 3 others
816
For example, selecting (vout, gds4, gm1, gds5) gives a solution of
[{gds4: (CF*K*s*vc + CF*s*vc + Cin*s*vc + gds2*vc -
gds2*ve - gm2*ve + gm4*vb - iin)/vc,
gm1: (Cd*s*vb + gds1*vb - gds1*ve + gds3*vb)/(K*vc + ve),
gds5: -(Cd*s*vb + Ct*s*ve - gds2*vc + gds2*ve + gds3*vb + gm2*ve)/ve,
vout: -K*vc}]

Related

solving for a ratio while eliminating specific variables in a set of linear equations

I am still having a problem, a different one than my previous post.
Basically I have [5x5][5x1]=[0] and would like to have the symbolic expression of the solution.
Here is my code.
from sympy import symbols, solve
gm1, gm2, gm4 = symbols(['gm1', 'gm2', 'gm4'])
gds1, gds2, gds3, gds4, gds5 = symbols(['gds1', 'gds2', 'gds3', 'gds4', 'gds5'])
s = symbols('s')
Cd, CF , Cin, Ct = symbols(['Cd', 'CF', 'Cin', 'Ct'])
K = symbols('K')
vb, vc, ve, vout, iin = symbols(['vb', 'vc', 've', 'vout', 'iin'])
sol = solve([-(gds1+gds3+(s*Cd))*vb + (gm1+gds1)*ve + -gm1*vout, \
-gm4*vb + (gds4-gds2-(s*Cin)-(s*CF))*vc + (gds2+gm2)*ve + s*CF*vout + iin, \
gds1*vb + gds2*vc + (-(s*Ct)-gds5-gds1-gm1-gm2-gds2)*ve + gm1*vout, \
K*vc + vout, 0], [vout])
print(sol)
I am expecting
vout/iin = f(gm1, gm2, gm4, gds1, gds2, gds3, gds4, gds5, s, Cd, CF , Cin, Ct, K)
In other words, vb, vc, ve are eliminated.
Is there any specific command to do that?
The problem is that you have 4 equations and you've only specified one unknown (vout). The system is generically unsolvable for most values of vb, vc, ve so asking to solve only for vout leads to no solution (in the generic case).
Ask to solve for vout, vb, vc, ve as 4 unknowns for the 4 equations and you can get a solution for all 4:
linsolve(eqs, [vout, vb, vc, ve])
(The output is long so I have omitted it)
I'm using linsolve since the equations are linear although solve will also work.
It's not entirely clear what you want but some variation of the above should do it. You said you wanted vout and vin in terms of the other symbols but with vb, vc and ve eliminated. That would require 5 independent equations but since your 5th equation is just 0 you don't have that.
If you suspect that you should be able to solve for the ratio, go ahead and solve for one of the dividends and the others that you don't want in the solution for it:
>>> sol = solve(eqs, iin, vb, vc, ve, check=False, simplify=False)
You suspect that you should be able to form the ratio of vout/iin which means that the solution for iin should be linear in vout:
>>> sol[iin].diff(vout,2)
0
It is. The constant should also be 0:
>>> sol[iin].subs(vout,0)
0
It is. So iin = vout*sol[iin].diff(vout) or
>>> r = 1/sol[iin].diff(vout) # vout/iin
This is an expression that is in the form K*{quadratic in s}/{cubic in s}. All other solutions for vb,vc,ve are also directly proportional to vout so they can all be expressed as a ratio vout/vi in a similar fashion.
The problems you are having basically boil down to this: your equations are not written in terms of ratios but you are interested in solving for ratios. In your case, the variables of interest should all be normalized by vout. You can get your system into that form and then it will work, too:
>>> V = iin, vb, vc, ve # variables you have
>>> r = symbols('r:4') # variables for the ratios
>>> reps = dict([(v, r[i]*vout) for i,v in enumerate(V)])
>>> req = Tuple(*eqs).xreplace(reps) # e.g. replace iin with r0*vout
>>> rsol = solve(req, r, check=False, simplify=False, dict=True)[0]
>>> explicit_ratio_solutions = Dict(rsol).xreplace({ri: v/vout for ri,v in zip(r, V)}) # restore keys to be iin/vout, vb/vout, etc...
You can confirm that the solutions are free of vout
>>> vout in Dict(rsol).free_symbol
False
When is this going to work? When the additive term independent of the symbols of interest is itself proportional to the normalizing factor and the normalizing factor does not appear in the terms containing the symbol of interest. I'm going to use simpler symbols for your system so it is easy to see explicitly that this is the case:
>>> neq = Tuple(*eqs)
>>> syms = neq.free_symbols - {iin,vout,vb,vc,ve}
>>> neq = neq.xreplace({o:n for o,n in zip(ordered(syms), var('a:z'))})
>>> x = symbols('x:4')
>>> neq = neq.xreplace({i:j for i,j in zip((iin,vb,vc,ve), x)})
>>> neq = neq.xreplace({vout:y})
>>> for i in neq:
... t = ind, dep = i.as_independent(*x, as_Add=True)
... t + (dep.has(y),)
(-k*y, x1*(-b*n - f - h) + x3*(f + k), False)
(a*n*y, -m*x1 + x0 + x2*(-a*n - c*n - g + i) + x3*(g + l), False)
(k*y, f*x1 + g*x2 + x3*(-d*n - f - g - j - k - l), False)
(y, e*x2, False)
Notice that y (for vout) appears as a factor in all the xi-independent terms and that it does not appear in the xi-dependent terms. That means you can do a change of variables from xi to xi/y.

How to order terms in polynomial expression (sympy, python) according to increasing degree (univariate case)?

In sympy (python) it seems that, by default, terms in univarate polynomials are ordered according to decreasing degrees: highest degree first, then second to highest, and so on. So, for example, a polynomial like
x + 1 + x^3 + 3x^6
will be printed out as 3x^6 + x^3 + x + 1.
I would like to reverse this order of polynomial terms in sympy to be increasing in the degrees. For the same example, the print-out should read 1 + x + x^3 + 3x^6. A solution that globally changes some parameter in program preamble is preferred but other options are also welcome.
Here is an MWE to play around with. It is different from the actual program I am working with. One part of the actual program (not the MWE) is printing out a list of recursively defined polynomials, e.g., P_n(x) = P_(n-1)(x) + a_n * x^n. It is easier for me to compare them when they are ordered by increasing degree. This is the motivation to change the order; doing it globally would probably just keep the code more readable (aesthetically pleasing). But the MWE is just for the same simple polynomial given in example above.
import sympy as sym
from sympy import *
x = sym.Symbol('x')
polynomial = x + 1 + x**3 + 3*x**6
print(polynomial)
Output of MWE:
>>> 3*x**6 + x**3 + x + 1
Desired output for MWE:
>>> 1 + x + x**3 + 3*x**6
You can get the leading term using sympy.polys.polytools.LT:
LT(3x ** 6 + x ** 3 + x + 1) == 3x**6
So at least you can churn out terms recursively and print it in your own way.
Unfortunately I’ve been trying to find some way to print the terms in some fix order for a long while and find no solution better than this
It's seems that there isn't an explicit way to do that and I found this approach to the problem:
to modify the print-representation of the object you can subclass its type and override the corresponding printing method (for LaTeX, MathML, ...) see documentation.
In this case _sympystr is used to "generates readable representations of SymPy expressions."
Here a basic implementation:
from sympy import Poly, symbols, latex
class UPoly(Poly):
"""Modified univariative polynomial"""
def _sympystr(self, printer) -> str:
"""increasing order of powers"""
if self.is_multivariate: # or: not self.is_univariate
raise Exception('Error, Polynomial is not univariative')
x = next(iter(expr.free_symbols))
poly_print = ""
for deg, coef in sorted(self.terms()):
term = coef * x**deg[0]
if coef.is_negative:
term = -term # fix sign
poly_print += " - "
else:
poly_print += " + "
poly_print += printer._print(term)
return poly_print.lstrip(" +-")
def _latex(self, printer):
return latex(self._sympystr(printer)) # keep the order
x = symbols('x')
expr = 2*x + 6 - x**5
up = UPoly(expr)
print(up)
#6 + 2*x - x**5
print(latex(up))
#6 + 2 x - x^{5}

Sympy simplification of polynomial with complex coefficients

Working on a problem with polynomials with complex coefficients,
I am stuck with the following problem:
Let's say I have a polynomial P = λ^16*z + λ^15*z^2, where λ is complex.
I want to simplify having the following constraint: λ^14 = 1.
So, plugging in, we should get:
P = λ^2*z + λ*z^2.
I have tried P.subs(λ**14,1) but it doesn't work, because it assumes λ is real I guess. So it returns the original expression: P = λ^16*z + λ^15*z^2, without factoring out λ^14...
I don't know any simple way to achieve what you want in sympy, but you could substitute each value explicitly:
p = (λ**16)*z + (λ**15)*(z**2)
p = p.subs(λ**16, λ**2).subs(λ**15, λ**1)
>>> z**2*λ + z*λ**2
Why subs fails to work here:
subs only substitutes an expression x**m in x**n when m is a factor of n, e.g.:
p.subs(λ, 1)
>>> z**2 + z
p.subs(λ**2, 1)
>>> z**2*λ**15 + z
p.subs(λ**3, 1)
>>> z**2 + z*λ**16
p.subs(λ**6, 1)
>>> z**2*λ**15 + z*λ**16
etc.
If you assume that λ is real, this works:
lambda_, z = sym.symbols('lambda z', real=True)
print((lambda_**16*z + lambda_**15*z**2).subs(lambda_**14, 1))
z**2 + z
Edit:
It shouldn't actually work anyway because λ may be negative. What you want is only true if λ is a positive number.
You can use the ratsimpmodprime() function to reduce a polynomial modulo a set of other polynomials. There is also the reduce() function, which does something similar.
>>> P = λ**16*z + λ**15*z**2
>>> ratsimpmodprime(P, [λ**14 - 1])
z**2*λ + z*λ**2
This works:
P.simplify().subs(λ**15,1).expand()

how to pattern match and change all occurrences of subexpression in expression?

Using sympy, I need to replace all occurrence of exp(C+anything) with C*exp(anything). Because exp(C) is constant, I just write at as C.
I can do this for one occurrence of exp in the expression. But do not how to do it if there are than one instance.
For example, for one instance, as in x+exp(C_0+3*x)+3*y, I need to change it to x+C_0*exp(3*x)+3*y
For one instance, this seems to work after some trial and error
from sympy import *
x,y,C_0 = symbols('x y C_0')
expr=x+exp(C_0+3*x)+3*y
#first check if exp is in the expression
if any([isinstance(a, exp) for a in preorder_traversal(expr)]):
p_1=Wild('p1');p_2=Wild('p_2');p_3=Wild('p_3')
r=(p_1+exp(C_0+p_2)+p_3).matches(expr)
expr.subs(exp(C_0+r[p_2]),C_0*exp(r[p_2]))
Which gives
C_0*exp(3*x) + x + 3*y
But what about something like x+exp(C_0+3*x)+3*y+exp(C_0+30*x+y) which I need to change to x+C_0*exp(3*x)+3*y+C_0*exp(30*x+y) I can't make special pattern match for each possible case. I need a way to change all occurrences
In Mathematica, I do the above as follows
expr = x + Exp[c + 3*x]*3*y + 3*y + Exp[c + 30*x + y]
expr /. Exp[c + any_] :> (c Exp[any])
Which gives
I actually prefer to tell Python just to change exp(C+anything) to C*exp(anything) without having to give pattern for the overall expression, since that can change in many way.
I am sure the above is also possible in python/sympy. Any hints how to do it?
I would look for function exp inside of the expression, check whether its argument is Add, and then whether C_0 is among the arguments of Add. Then build a thing to replace exp with. Consider the following:
from sympy import *
x, y, C_0 = symbols('x y C_0')
expr = x + exp(C_0+3*x) + 3*y + exp(y+C_0+30*x) - exp(x+y-C_0) + exp(x*y)
exp_sum = [(a, a.args[0].args) for a in preorder_traversal(expr) if a.func == exp and a.args[0].func == Add]
exp_sum = [p for p in exp_sum if C_0 in p[1]]
new_exp = [C_0*exp(Add(*[x for x in p[1] if x != C_0])) for p in exp_sum]
for (old, new) in zip(exp_sum, new_exp):
expr = expr.subs(old[0], new)
Initially, exp_sum contains all parts of the form exp(Add(...)). After that it's filtered down to sums containing C_0. New exponentials are formed by taking all summands that are not C_0, adding them, applying exp and multiplying by C_0. Then substitution happens.
To clarify the process, here is what exp_sum is in the above example: a list of tuples (exponential and the summands inside):
[(exp(C_0 + 3*x), (C_0, 3*x)), (exp(C_0 + 30*x + y), (C_0, y, 30*x))]
And this is new_exp
[C_0*exp(3*x), C_0*exp(30*x + y)]
Finally, expr at the end:
C_0*exp(3*x) + C_0*exp(30*x + y) + x + 3*y + exp(x*y) - exp(-C_0 + x + y)
Notice that exp(-C_0...) is not affected by the change; it's not a part of the pattern.

python: changing symbol variable and assign numerical value

In order to calculate derivatives and other expressions I used the sympy package and said that T = sy.Symbol('T') now that I have calculated the right expression:
E= -T**2*F_deriv_T(T,rho)
where
def F_deriv_rho(T,rho):
ret = 0
for n in range(5):
for m in range(4):
inner= c[n,m]*g_rho_deriv_rho_np*g_T_np
ret += inner
return ret
that looks like this:
F_deriv_rho: [0.0 7.76971e-5*T 0.0001553942*T**2*rho
T*(-5.14488e-5*log(rho) - 5.14488e-5)*log(T) + T*(1.22574e-5*log(rho)+1.22574e-5)*log(T) + T*(1.89488e-5*log(rho) + 1.89488e-5)*log(T) + T(2.29441e-5*log(rho) + 2.29441e-5)*log(T) + T*(7.49956e-5*log(rho) + 7.49956e-5)*log(T)
T**2*(-0.0001028976*rho*log(rho) - 5.14488e-5*rho)*log(T) + T**2*(2.45148e-5*rho*log(rho) + 1.22574e-5*rho)*log(T) + T**2*(3.78976e-5*rho*log(rho) + 1.89488e-5*rho)*log(T) + T**2*(4.58882e-5*rho*log(rho) + 2.29441e-5*rho)*log(T) + T**2*(0.0001499912*rho*log(rho) + 7.49956e 5*rho)*log(T)]
with python I would like to change T (and rho) as a symbol to a value. How could I do that?
So, I would like to create 10 numbers like T_def = np.arange(2000, 10000, 800)and exchange all my sy.symbol(T) by iterating through the 10 values I created in the array.
Thanks for your help
I have found the solution according to this post:
How to substitute multiple symbols in an expression in sympy?
by usings "subs":
>>> from sympy import Symbol
>>> x, y = Symbol('x y')
>>> f = x + y
>>> f.subs({x:10, y: 20})
>>> f
30
There's more for this kinda thing here: http://docs.sympy.org/latest/tutorial/basic_operations.html
EDIT: A faster way would be by using "lamdify" as suggested by #Bjoern Dahlgren

Categories