Translation from Mathcad to Python - python

I can't translate a formula from Mathcad to Python. Stuck on "a".
Here's what I was able to do:
from matplotlib import pyplot as plt
import numpy as np
k1 = 1
b = 1.51
D = (1/b) * (np.sqrt(k1/np.pi))
x0 = 10 * b
myArray = np.arange(0, 24, 0.1)
for t in myArray:
S1_t = (k1) / (1 + np.e ** (-(D * myArray - 5)))
S1_deistv = S1_t.real
plt.plot(myArray, S1_deistv, color="black")
plt.show()

As you can see, MathCad is going to:
create an expression containing the symbolic derivative of S1.
finding the root of that expression.
In Python, we have to use different libraries to achieve the same result. In this particular case it is a little bit more convoluted (requires more steps). In particular:
Use SymPy to create a symbolic expression. We can then compute the symbolic derivative.
Use SciPy's root finding algorithms, such as root or bisect, ...
Here is the code: I've added some comments to help you understand.
from matplotlib import pyplot as plt
import numpy as np
from scipy.optimize import root
import sympy as sp
k1 = 1
b = 1.51
D = (1/b) * np.sqrt(k1 / np.pi)
# create a symbol
t = sp.symbols("t")
# create a symbolic expression. Note that we are using the
# exponential function of SymPy (because it is symbolic)
S1 = k1 / (1 + sp.exp(-(D * t - 5)))
print("S1 = ", S1)
# write the expression
# with `diff` we are computing the derivative with respect to `t`
expr = S1 - t * sp.diff(S1, t)
print("expr = ", expr)
# convert the expression to a numerical function so that it
# can be evaluated by Numpy/Scipy
f = sp.lambdify([t], expr)
# plot the symbolic expression to help us decide a good initial
# guess for the root finding algorithm
sp.plot(expr, (t, 0, 24))
# in the interval t in [0, 24] we can see two roots, one at
# about 2 and the other at about 18.
# Let's find the second root.
result = root(f, 18)
print("result", result)
a = result.x[0]
print("a = ", a)
# remember that S1 is a symbolic expression: we can substitute
# t (the symbol) with a (the number)
b = float(S1.subs(t, a))
k = b / a
print("k = ", k)
t_array = np.arange(0, 24, 0.1)
plt.figure()
S1_t = (k1) / (1 + np.e ** (-(D * t_array - 5)))
S1_deistv = S1_t.real
plt.plot(t_array, S1_deistv, color="black")
plt.plot(t_array, k * t_array)
plt.show()
This is the output of the following code:
S1 = 1/(exp(5 - 0.373635485793216*t) + 1)
expr = -0.373635485793216*t*exp(5 - 0.373635485793216*t)/(exp(5 - 0.373635485793216*t) + 1)**2 + 1/(exp(5 - 0.373635485793216*t) + 1)
Function of which we want to find the roots
result fjac: array([[-1.]])
fun: array([-6.66133815e-16])
message: 'The solution converged.'
nfev: 6
qtf: array([3.5682568e-13])
r: array([-0.22395716])
status: 1
success: True
x: array([18.06314347])
a = 18.063143471730815
k = 0.04715849105203411

Related

Sympy: Drop terms without a specific variable

I'm trying to compute some (a lot) of multivariate conditional densities (i.e., the multiplication of several multivariate probability density functions). I'm able to set up and expand the matrices properly but now would like to drop terms that, for example in the equation (and code) below, don't contain wg. With help from the posted answer, I was able to develop a hacky solution; improvements are welcome.
UPDATE: MWE
import sympy as sym
from IPython.display import display as disp
N = 211
wg = sym.MatrixSymbol('w_g', N, 1)
wg_n = sym.MatrixSymbol('w_gn', N, 1)
Z_wg = sym.MatrixSymbol('Z_wg', N, N)
# pdf wg
pdf_wg = ((wg - wg_n).T * Z_wg.I * (wg - wg_n))
pdf_full = sym.expand(pdf_wg)
# pdf_full.collect(wg) # NotImplementedError: noncommutative scalars in MatMul are not supported.
# print (wg in pdf_full.atoms()) # False
# this gives what I want
terms = pdf_full.as_terms()[0]
for term in terms:
if 'w_g,' in str(term[0].atoms()):
disp (term[0])
UPDATE 2: More Complex MWE
Here I'm trying to grab just the terms with b in them.
import sympy as sym
from IPython.display import display as disp, Math
mu = sym.symbols('mu') # mean non GIA SSH trend
N = 211
vec1 = sym.MatrixSymbol('1', N, 1)
u = sym.MatrixSymbol('u', N, 1)
Pi = sym.MatrixSymbol('Pi', N, N)
b = sym.MatrixSymbol('b', N, 1)
wg = sym.MatrixSymbol('w_g', N, 1)
wm = sym.MatrixSymbol('w_m', N, 1)
bhat = mu*vec1 + wg + wm + u # convenience
pdf = sym.expand((b - bhat).T * Pi.I * (b-bhat))
terms = pdf.as_terms()[0]
good_terms = []
for term in terms:
if b.args[0] in term[0].atoms():
good_terms.append(term[0])
print ('Good terms:'); disp(sym.Add(*good_terms))
UPDATE 4: Solved
For more complex expressions adding doit() to the expand will prevent a bunch of extra loops (e.g.):
pdf = sym.expand((b - bhat).T * Pi.I * (b-bhat)).doit()
More information can be found in the comments to the various answers.
Thanks!
You could extract the atoms of the expression and test whether the variable is among them:
from sympy import symbols
a, b, mug = symbols('a b mu_g')
expr1 = a * b + a * mug
expr2 = a * b
for expr in [expr1, expr2]:
if mug in expr.atoms():
print(expr, 'contains', mug)
else:
print(expr, 'does not contain', mug)
PS: An update for your new question. For a MatrixSymbol the symbol is stored as wg.args[0] (args[1] and args[2] are the dimensions):
import sympy as sym
N = 211
wg = sym.MatrixSymbol('w_g', N, 1)
wg_n = sym.MatrixSymbol('w_gn', N, 1)
Z_wg = sym.MatrixSymbol('Z_wg', N, N)
pdf_wg = ((wg - wg_n).T * Z_wg.I * (wg - wg_n))
pdf_full = sym.expand(pdf_wg)
print (wg.args[0] in pdf_full.atoms()) # True
Note that the hacky solution is the question could go wrong when w_g would be the last item or another name would end in the same string.
You can get the terms not containing wg like:
In [53]: pdf_full.subs(wg, ZeroMatrix(N, 1)).doit()
Out[53]:
T -1
w_gn ⋅Z_wg ⋅w_gn
Then you can subtract those from pdf_full:
In [54]: pdf_full - pdf_full.subs(wg, ZeroMatrix(N, 1)).doit()
Out[54]:
T -1 T -1 T -1
w_g ⋅Z_wg ⋅w_g -w_g ⋅Z_wg ⋅w_gn -w_gn ⋅Z_wg ⋅w_g

Error because of exponential function when using mpmath and sympy modules

I have the following code where I need to solve an expression to find the roots. The expression needs to be solved for omega.
import numpy as np
from sympy import Symbol,lambdify
import scipy
from mpmath import findroot, exp
eta = 1.5
tau = 5 /1000
omega = Symbol("omega")
Tf = exp(1j * omega * tau)
symFun = 1 + Tf * (eta - 1)
denom = lambdify((omega), symFun, "scipy")
Tf_high = 1j * 2 * np.pi * 1000 * tau
sol = findroot(denom, [0+1j,Tf_high])
The program gives an error and I am not able to correct. The error is : TypeError: cannot create mpf from 0.005Iomega
Edit 1 - I have tried to implement different approach based on comments. First approach was to use the sympy.solveset module. Second approach was to use fsolve from scipy.optimise. Both are not giving proper output.
For clarity, I am copying the relevant code to each approach along with the output I am getting.
Approach 1 - Sympy
import numpy as np
from sympy import Symbol,exp
from sympy.solvers.solveset import solveset,solveset_real,solveset_complex
import matplotlib.pyplot as plt
def denominator(eta,Tf):
return 1 + Tf * (eta - 1)
if __name__ == "__main__":
eta = 1.5
tau = 5 /1000
omega = Symbol("omega")
n = 1
Tf = exp(1j * omega * tau)
denom = 1 + Tf * (eta - 1)
symFun = denominator(eta,Tf)
sol = solveset_real(denom,omega)
sol1 = solveset_complex(denom,omega)
print('In real domain', sol)
print('In imaginary domain',sol1)
Output:
In real domain EmptySet
In imaginary domain ImageSet(Lambda(_n, -200.0*I*(I*(2*_n*pi + pi) + 0.693147180559945)), Integers)
Approach 2 Scipy
import numpy as np
from scipy.optimize import fsolve, root
def denominator(eta,tau,n, omega):
Tf = n * np.exo(1j * omega * tau)
return 1 + Tf * (eta - 1)
if __name__ == "__main__":
eta = 1.5
tau = 5 /1000
n = 1
func = lambda omega : 1 + (eta - 1) * (n * np.exp( 1j * omega * tau))
sol = fsolve(func,10)
print(sol)
Output:
Cannot cast array data from dtype('complex128') to dtype('float64') according to the rule 'safe'
How do I correct the program? Please suggest me the approach that will give proper results.
SymPy is a computer algebra system and solves the equation like a human would. SciPy uses numeric optimization. If you want ALL the solutions, I suggest going with SymPy. If you want one solution, I suggest going with SciPy.
Approach 1 - SymPy
The solutions SymPy gives will be more "interactive" for you as the developer. But it will be perfectly correct almost all the time.
from sympy import *
eta = S(3)/2
tau = S(5) / 1000
omega = Symbol("omega")
n = 1
Tf = exp(I * omega * tau)
denom = 1 + Tf * (eta - 1)
sol = solveset(denom, omega)
print(sol)
Giving
ImageSet(Lambda(_n, -200*I*(I*(2*_n*pi + pi) + log(2))), Integers)
This is the true mathematical solution.
Notice how I put S around an integer before dividing it. When dividing integers in Python, it loses accuracy because it uses floating point numbers. Converting it to SymPy objects keep all the accuracy.
Since we know we have an ImageSet over integers, we can start listing a few solutions:
for n in range(-3, 3):
print(complex(sol.lamda(n)))
Which gives
(-3141.5926535897934-138.62943611198907j)
(-1884.9555921538758-138.62943611198907j)
(-628.3185307179587-138.62943611198907j)
(628.3185307179587-138.62943611198907j)
(1884.9555921538758-138.62943611198907j)
(3141.5926535897934-138.62943611198907j)
With some experience, you could automate it so that the whole program only returns 1 solution no matter on the type of output returned by solveset.
Approach 2 - SciPy
The solutions SciPy gives will be more automated. You will never have a perfect answer and different choices of the initial conditions may not converge all the time.
import numpy as np
from scipy.optimize import root
eta = 1.5
tau = 5 / 1000
n = 1
def f(omega: Tuple):
omega_real, omega_imag = omega
omega: complex = omega_real + omega_imag*1j
result: complex = 1 + (eta - 1) * (n * np.exp(1j * omega * tau))
return result.real, result.imag
sol = root(f, [100, 100])
print(sol)
print(sol.x[0]+sol.x[1]*1j)
Which gives
fjac: array([[ 0.00932264, 0.99995654],
[-0.99995654, 0.00932264]])
fun: array([-2.13074003e-12, -8.86389816e-12])
message: 'The solution converged.'
nfev: 30
qtf: array([ 2.96274855e-09, -6.82780898e-10])
r: array([-0.00520194, -0.00085702, -0.00479143])
status: 1
success: True
x: array([ 628.31853072, -138.62943611])
(628.3185307197314-138.62943611241522j)
Looks like that's one of the solutions SymPy found. So we must be doing something right. Note that there are many initial values that don't converge, for example, sol = root(f, [1, 1]).

What is the most efficient way to find the root of a complex function in Sympy (a Python library for symbolic mathematics)?

I am using the root_scalar function from the scipy.optimize module to find the root of a complex function defined in sympy. However, the function takes around 15-20 seconds to return the root, and I need to find a way to speed up this computation. Is it possible to convert the entire sympy function to scipy for faster processing, or is there any other way to optimize this process and reduce the computation time?
from sympy.stats import Gamma, density, cdf, E, variance
from sympy import Symbol, pprint, simplify
import numpy as np
l = 7
m = 30
p = 17
w = 6
K = 500
c = 6
h = 0.1
mean = 500
std = 296
def calculate_mean(days):
return mean*days
def calculate_std(days):
return std*np.sqrt(days)
def calculate_mean_std(days):
mean = calculate_mean(days)
std = calculate_std(days)
return mean, std
mean_m, std_m = calculate_mean_std(m)
mean_l, std_l = calculate_mean_std(l)
shape_m = (mean_m/std_m)**2
scale_m = std_m**2/mean_m
shape_l = (mean_l/std_l)**2
scale_l = std_l**2/mean_l
k = Symbol("k", positive=True)
theta = Symbol("theta", positive=True)
x = Symbol("x")
X = Gamma("z", k, theta)
P = density(X)(x)
C = cdf(X, meijerg=True)(x)
cdf_m_symb = C.subs([(theta, scale_m) , (k, shape_m)])
cdf_l_symb = C.subs([(theta, scale_l) , (k, shape_l)])
pdf_m_symb = P.subs([(theta, scale_m) , (k, shape_m)])
pdf_l_symb = P.subs([(theta, scale_l) , (k, shape_l)])
max_Q = np.ceil(mean*(m+l)).astype(int)
def g(r: float) -> float:
result = sp.N(-p + (p + w * cdf_m_symb.subs(x, max_Q)) * cdf_l_symb.subs(x, r) + \
w * sp.Integral(cdf_l_symb * pdf_m_symb.subs(x, (r + max_Q - x)), (x, 0, r)))
return result
from scipy.optimize import root_scalar
import sympy as sp
import time
start_time = time.time()
r0 = 200 # initial estimate for the root
bracket = (-10, 5000) # the upper and lower bounds of where the root is
solution = root_scalar(g, x0=r0, bracket=bracket)
print(solution) # info about the convergence
print("Results: ",solution.root) # the actual number
end_time = time.time()
print("Time taken:", end_time - start_time)
Here is the output from the above code
converged: True
flag: 'converged'
function_calls: 10
iterations: 9
root: 3966.9429368680453
Results: 3966.9429368680453
Time taken: 13.81236743927002
I have provided the code that I am currently using and the output that it produces. Any suggestions or examples of how to optimize this process would be greatly appreciated.
Compute the integral numerically, tabulate g(x) and interpolate x(g). Then your root-finding is nothing but evaluating a spline at a given point. Can't get any faster then that.

How can I simplify this more?

I am trying to apply numpy to this code I wrote for trapezium rule integration:
def integral(a,b,n):
delta = (b-a)/float(n)
s = 0.0
s+= np.sin(a)/(a*2)
for i in range(1,n):
s +=np.sin(a + i*delta)/(a + i*delta)
s += np.sin(b)/(b*2.0)
return s * delta
I am trying to get the return value from the new function something like this:
return delta *((2 *np.sin(x[1:-1])) +np.sin(x[0])+np.sin(x[-1]) )/2*x
I am trying for a long time now to make any breakthrough but all my attempts failed.
One of the things I attempted and I do not get is why the following code gives too many indices for array error?
def integral(a,b,n):
d = (b-a)/float(n)
x = np.arange(a,b,d)
J = np.where(x[:,1] < np.sin(x[:,0])/x[:,0])[0]
Every hint/advice is very much appreciated.
You forgot to sum over sin(x):
>>> def integral(a, b, n):
... x, delta = np.linspace(a, b, n+1, retstep=True)
... y = np.sin(x)
... y[0] /= 2
... y[-1] /= 2
... return delta * y.sum()
...
>>> integral(0, np.pi / 2, 10000)
0.9999999979438324
>>> integral(0, 2 * np.pi, 10000)
0.0
>>> from scipy.integrate import quad
>>> quad(np.sin, 0, np.pi / 2)
(0.9999999999999999, 1.1102230246251564e-14)
>>> quad(np.sin, 0, 2 * np.pi)
(2.221501482512777e-16, 4.3998892617845996e-14)
I tried this meanwhile, too.
import numpy as np
def T_n(a, b, n, fun):
delta = (b - a)/float(n) # delta formula
x_i = lambda a,i,delta: a + i * delta # calculate x_i
return 0.5 * delta * \
(2 * sum(fun(x_i(a, np.arange(0, n + 1), delta))) \
- fun(x_i(a, 0, delta)) \
- fun(x_i(a, n, delta)))
Reconstructed the code using formulas at bottom of this page
https://matheguru.com/integralrechnung/trapezregel.html
The summing over the range(0, n+1) - which gives [0, 1, ..., n] -
is implemented using numpy. Usually, you would collect the values using a for loop in normal Python.
But numpy's vectorized behaviour can be used here.
np.arange(0, n+1) gives a np.array([0, 1, ...,n]).
If given as argument to the function (here abstracted as fun) - the function formula for x_0 to x_n
will be then calculated. and collected in a numpy-array. So fun(x_i(...)) returns a numpy-array of the function applied on x_0 to x_n. This array/list is summed up by sum().
The entire sum() is multiplied by 2, and then the function value of x_0 and x_n subtracted afterwards. (Since in the trapezoid formula only the middle summands, but not the first and the last, are multiplied by 2). This was kind of a hack.
The linked German page uses as a function fun(x) = x ^ 2 + 3
which can be nicely defined on the fly by using a lambda expression:
fun = lambda x: x ** 2 + 3
a = -2
b = 3
n = 6
You could instead use a normal function definition, too: defun fun(x): return x ** 2 + 3.
So I tested by typing the command:
T_n(a, b, n, fun)
Which correctly returned:
## Out[172]: 27.24537037037037
For your case, just allocate np.sin tofun and your values for a, b, and n into this function call.
Like:
fun = np.sin # by that eveywhere where `fun` is placed in function,
# it will behave as if `np.sin` will stand there - this is possible,
# because Python treats its functions as first class citizens
a = #your value
b = #your value
n = #your value
Finally, you can call:
T_n(a, b, n, fun)
And it will work!

Intersection between Gaussian

I'm just trying to plot two gaussians and to find the intersection point. I have the following code. It's not plotting the exact intersection though and I really cannot figure out why. It's like just barely slightly off but I worked through the derived solution if we took the log of subtracted gaussians and yeah it seems like it should be correct. Can anyone help? Thank you so much!
import numpy as np
import matplotlib.pyplot as plt
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
# found online
def solve_gasussians(m1, s1, m2, s2):
a = 1.0/(2.0*s1**2) - 1.0/(2.0*s2**2)
b = m2/(s2**2) - m1/(s1**2)
c = m1**2 /(2*s1**2) - m2**2 / (2.0*s2**2) - np.log(s2/s1)
return np.roots([a,b,c])
s1 = np.linspace(0, 10,300)
s2 = np.linspace(0, 14, 300)
solved_val = solve_gasussians(5.0, 0.5, 7.0, 1.0)
print solved_val
solved_val = solved_val[0]
plt.figure('Baseline Distributions')
plt.title('Baseline Distributions')
plt.xlabel('Response Rate')
plt.ylabel('Probability')
plt.plot(s1, plot_normal(s1, 5.0, 0.5),'r', label='s1')
plt.plot(s2, plot_normal(s2, 7.0, 1.0),'b', label='s2')
plt.plot(solved_val, plot_normal(solved_val, 7.0, 1.0), 'mo')
plt.legend()
plt.show()
You have a small bug in plot_normal function - you are missing square root in the denominator. Proper version:
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/np.sqrt(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
gives the expected result:
And two remarks.
Remember that you can have 2 roots of the equation in general (two intersection points), and this is the case with parameters you provided.
As far as I know np.roots gives you approximate result, but you cat get exact result easily, rewriting solve_gasussians function as:
def solve_gasussians(m1, s1, m2, s2):
# coefficients of quadratic equation ax^2 + bx + c = 0
a = (s1**2.0) - (s2**2.0)
b = 2 * (m1 * s2**2.0 - m2 * s1**2.0)
c = m2**2.0 * s1**2.0 - m1**2.0 * s2**2.0 - 2 * s1**2.0 * s2**2.0 * np.log(s1/s2)
x1 = (-b + np.sqrt(b**2.0 - 4.0 * a * c)) / (2.0 * a)
x2 = (-b - np.sqrt(b**2.0 - 4.0 * a * c)) / (2.0 * a)
return x1, x2
I don't know where the mistake lies in your code. But I think I found the code your borrowed from and made part of the adjustment you need.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
def solve(m1,m2,std1,std2):
a = 1/(2*std1**2) - 1/(2*std2**2)
b = m2/(std2**2) - m1/(std1**2)
c = m1**2 /(2*std1**2) - m2**2 / (2*std2**2) - np.log(std2/std1)
return np.roots([a,b,c])
m1 = 5
std1 = 0.5
m2 = 7
std2 = 1
result = solve(m1,m2,std1,std2)
x = np.linspace(-5,9,10000)
plot1=plt.plot(x,[norm.pdf(_,m1,std1) for _ in x])
plot2=plt.plot(x,[norm.pdf(_,m2,std2) for _ in x])
plot3=plt.plot(result[0],norm.pdf(result[0],m1,std1) ,'o')
plt.show()
I will offer two pieces of unsolicited advice that might make life easier for you (in the way they do for me):
When you adapt code try to make small, incremental changes and check that the code still works at each step.
Look for existing free libraries. In this case norm from scipy is a good replacement for what was used in the original code.
The mistake is here. This line:
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
Should be this:
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/np.sqrt(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
You forgot the sqrt.
It would be wiser to use a pre-existing normal pdf if that's available, such as:
import scipy.stats
def plot_normal(x, mean = 0, sigma = 1):
return scipy.stats.norm.pdf(x,loc=mean,scale=sigma)
It's also possible to solve for the intersections exactly. This answer provides a quadratic equation for the roots of the Gaussians' intersections. Using maxima to solve for x gives the following expression. Which, while complicated, does not rely on iterative methods and can be automatically generated from simpler expressions.
def solve_gaussians(m1,s1,m2,s2):
x1 = (s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)+m1*s2**2-m2*s1**2)/(s2**2-s1**2)
x2 = -(s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)-m1*s2**2+m2*s1**2)/(s2**2-s1**2)
return x1,x2
Putting it altogether gives:
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats
def plot_normal(x, mean = 0, sigma = 1):
return scipy.stats.norm.pdf(x,loc=mean,scale=sigma)
#Use the equation from [this answer](https://stats.stackexchange.com/a/12213/12116) solved for x
def solve_gaussians(m1,s1,m2,s2):
x1 = (s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)+m1*s2**2-m2*s1**2)/(s2**2-s1**2)
x2 = -(s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)-m1*s2**2+m2*s1**2)/(s2**2-s1**2)
return x1,x2
s = np.linspace(0, 14,300)
x = solve_gaussians(5.0,0.5,7.0,1.0)
plt.figure('Baseline Distributions')
plt.title('Baseline Distributions')
plt.xlabel('Response Rate')
plt.ylabel('Probability')
plt.plot(s, plot_normal(s, 5.0, 0.5),'r', label='s1')
plt.plot(s, plot_normal(s, 7.0, 1.0),'b', label='s2')
plt.plot(x[0],plot_normal(x[0],5.,0.5),'mo')
plt.plot(x[1],plot_normal(x[1],5.,0.5),'mo')
plt.legend()
plt.show()
Giving:

Categories

Resources