Python 3: Sympy: Include list information to optimize lambdify - python

I use lambdify to compile an expression which is a function of certain parameters. Each parameter has N points. So I need to evaluate the expression N times. The following shows a simplified example on how this is done.
import numpy as np
from sympy.parsing.sympy_parser import parse_expr
from sympy.utilities.lambdify import lambdify, implemented_function
from sympy import S, Symbol
from sympy.utilities.autowrap import ufuncify
def CreateMagneticFieldsList(dataToSave,equationString,DSList):
expression = S(equationString)
numOfElements = len(dataToSave["MagneticFields"])
#initialize the magnetic field output array
magFieldsArray = np.empty(numOfElements)
magFieldsArray[:] = np.NaN
lam_f = lambdify(tuple(DSList),expression,modules='numpy')
try:
for i in range(numOfElements):
replacementList = np.zeros(len(DSList))
for j in range(len(DSList)):
replacementList[j] = dataToSave[DSList[j]][i]
try:
val = np.double(lam_f(*replacementList))
except:
val = np.nan
magFieldsArray[i] = val
except:
print("Error while evaluating the magnetic field expression")
return magFieldsArray
list={"MagneticFields":list(range(10000)), "Chx":list(range(10000))}
out=CreateMagneticFieldsList(list,"MagneticFields*5+Chx",["MagneticFields","Chx"])
print(out)
Is there a way to optimize this call further? Specifically, I mean is there a way to make lambdify include that I'm calculating for a list of points, so that the loop evalulation can be optimized?

Thanks to #asmeurer, he gave the idea on how to do it.
Since lambdify is compiled using numpy, then one could simply pass the lists as arguments! The following is a working example
#!/usr/bin/python3
import numpy as np
from sympy.parsing.sympy_parser import parse_expr
from sympy.utilities.lambdify import lambdify, implemented_function
from sympy import S, Symbol
from sympy.utilities.autowrap import ufuncify
def CreateMagneticFieldsListOpt(dataToSave,equationString,DSList):
expression = S(equationString)
numOfElements = len(dataToSave["MagneticFields"])
#initialize the magnetic field output array
magFieldsArray = np.empty(numOfElements)
magFieldsArray[:] = np.NaN
lam_f = lambdify(tuple(DSList),expression,modules='numpy')
replacementList = [None]*len(DSList)
for j in range(len(DSList)):
replacementList[j] = np.array(dataToSave[DSList[j]])
print(replacementList)
magFieldsArray = np.double(lam_f(*replacementList))
return magFieldsArray
list={"MagneticFields":[1,2,3,4,5],"ChX":[2,4,6,8,10]}
out=CreateMagneticFieldsListOpt(list,"MagneticFields*5+ChX",["MagneticFields","ChX"])
print(out)

Related

operation on Matrices with the elements in function form

I have a set of functions which are tended to be the elements of a matrix, and I have to do some + and * and / operation on them and also between each matrix element.
I am using Numpy, Sympy to do this and here is the code written on Python 2.7.
import numpy as np
import math
import cmath
import matplotlib.pyplot as pl
from cmath import*
from sympy import*
# delta, deltz and some other numbers are simple float number I changed varibles in for loop to the first value to test this section of my whole cod
f1 = Matrix([[1, 0],[ 0, 1]]) #an empty matrix from sympy
delta = 2.0*np.pi*1.6*((1.0/1530)-(1.0/(2.0*1550))) #this is a simple float number
deltz = (5*(10.0**6))/50
def apdFunc(x):
return np.exp(-2*np.log(2)*((x-(5/2))/5)**2)
def modFunc(x):
return (1+np.cos((2*np.pi/1)*x))
d1 = np.linspace(-20.0, 20.0, 5000)
apdFunc = apdFunc(d1)
modFunc = modFunc(d2)
Profile = modFunc*apdFunc
sig = (np.pi/1530)*fbgProfile + delta
kaa = (np.pi/1530)*fbgProfile
j = sqrt(-1)
gammab = np.sqrt(kaa**2.0-sig**2.0)
#Matrix elements definition
f11 = np.cosh(gammab*deltz)-1j*(sig/gammab)*np.sinh(gammab*deltz)
f22 = np.cosh(gammab*deltz)+1j*(sig/gammab)*np.sinh(gammab*deltz)
f12 = -1j*(kaa/gammab)*np.sinh(gammab*deltz)
f21 = 1j*(kaa/gammab)*np.sinh(gammab*deltz)
f1 = f1*Matrix([[f11, f12],[ f21, f22]])
PO=f1[0,0]
NO=f1[1,0]
REF=abs((NO/PO)**2)
pl.plot(d3,REF)
pl.show()
print f1[0,0]
print PO
print REF
The first problem is : gammab=np.sqrt(kaa**2.0-sig**2.0) that numpy can't accept complex number I mean negative value under sqrt and if I don't use Numpy I can't do Operation on them because kaa and sig are functions.
Second : I cant print the matrix elements (after reversing kaa**2.0-sig**2.0 and solving first problem), thus I can't plot the REF=abs((NO/PO)**2) and an error apears telling
AttributeError: 'ImmutableDenseNDimArray' object has no attribute 'as_coeff_Mul'
Any help appreciated and if you can introduce a reference to learn how to solve the problem.

Multiprocessing and scipy (dblquad)

I am trying to speed up the following code in python:
import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate
from scipy import integrate
import camb
from tqdm import tqdm
import os
#Reading a PS
dir = os.getcwd()
data = np.loadtxt(dir+"/ps1-peacock.txt")
kh = data[:,0]
p_lin = data[:,1]
p_nlin = data[:,2]
p_linear = interpolate.interp1d(kh,p_lin)
#Integrand of P22
def upper_mu(x):
return min(1.0,(kk**2 + np.exp(2*x))/(2*kk*np.exp(x)))
def lower_mu(x):
return max(-1.0,-(kk**2+np.exp(x))/(2*kk*np.exp(x)))
def mulow(x):
return max(-1.0,(kh[-1]**2.0-kk**2.0-np.exp(x)**2.0)/(-2.0*kk*np.exp(x)))
def muhigh(x):
return min(1.0,(kh[0]**2.0-kk**2.0-np.exp(x)**2.0)/(-2.0*kk*np.exp(x)))
def f22(mu,q,k):
r = np.exp(q)/k
F = (7.0*mu+(3.0-10.0*mu**2)*r)/(14.0*r*(r**2-2.0*mu*r+1.0))
psik = (k**2+np.exp(2*q)-2.0*k*mu*np.exp(q))**0.5
if (psik>kh[0] and psik<kh[-1]):
return 1.0/2.0/np.pi**2.0*np.exp(3*q)*p_linear(np.exp(q))*p_linear(psik)*F**2
else:
return 0
P22 = np.zeros_like(kh)
error = np.zeros_like(kh)
for i in tqdm(range(0,np.shape(kh)[0])):
kk = kh[i]
P22[i], error[i] = integrate.dblquad(f22,np.log(kh[0]),np.log(kh[-1]),mulow,muhigh,args=(kh[i],),epsrel=1e-3, epsabs=50)[:2]
Here follows the integral in text for reasons of clarity:
I would like to use multiprocessing to improve the performance of dblquad(). Does anyone know how can I implement it in this specific case?
Multiprocessing won't help here, you cannot split the dblquad work between python processes.
If you have several integrals to compute, then yes, you can split integrals between processes. Whether this is worth it strongly depends on the amount of work there is for each process.

Constructing a ContinuousRV from an implemented_function or rv_continuous

I would like to construct a ContinuousRV given a python-implemented probability density function (pdf). The following is a minimal working example whose last statement yields a ValueError
import numpy as np
from scipy.stats import gaussian_kde, norm
from sympy import Interval, oo, symbols
from sympy.stats import ContinuousRV
from sympy.utilities.lambdify import implemented_function
# Example Data
measures = np.concatenate([norm.rvs(loc=-2, size=64), norm.rvs(loc=3, size=32)])
# Definition of the PDF
pdf_kde = gaussian_kde(measures)
pdf_sym = implemented_function("pdf", pdf_kde)
# Create the symbolic variable
XName, x = symbols('X x')
X = ContinuousRV(XName, pdf_sym(x), set=Interval(-oo, oo))
The example fails with the following error:
.../lib/python3.8/site-packages/sympy/stats/crv_types.py in check(pdf, set)
149 x = Dummy('x')
150 val = integrate(pdf(x), (x, set))
--> 151 _value_check(val == S.One, "The pdf on the given set is incorrect.")
152
153
.../lib/python3.8/site-packages/sympy/stats/rv.py in _value_check(condition, message)
1450 truth = fuzzy_and(condition)
1451 if truth == False:
-> 1452 raise ValueError(message)
1453 return truth == True
1454
ValueError: The pdf on the given set is incorrect.
I have confirmed that the pdf is a good approximation.
from scipy import integrate
value, err = integrate.quad(pdf_kde, -np.inf, np.inf)
print(value, err)
>>> 0.9999999999999996 2.318795975521764e-09
I am currently using Python 3.8.0, Sympy 1.6, Scipy 1.4.1, and Numpy 1.18.5 if that is relevant.
The ContinuousRV method constructs an instance of ContinuousDistributionHandmade and, in doing so, invokes a check method that fails because Sympy does not automatically perform numeric computation. It is possible to construct a wrapper that yields the desired distribution and performs said check numerically.
from sympy.stats.crv import SingleContinuousDistribution
def _check_dist(pdf, set):
from sympy import Dummy, integrate, N
x = Dummy('x')
integrand = pdf(x)
integral = integrate(integrand, (x, set.start, set.end))
v = float(N(integral))
assert np.isclose(float(v), 1.0)
def EmpiricalRV(name: str, m: np.ndarray) -> SingleContinuousDistribution:
from scipy.stats import gaussian_kde
from sympy import Interval, oo
from sympy.stats import ContinuousDistributionHandmade
from sympy.stats.crv import SingleContinuousPSpace
from sympy.utilities.lambdify import implemented_function
pdf_kde = gaussian_kde(m)
pdf_sym = implemented_function(f"f_{name}", lambda y: pdf_kde(float(y)))
domain = Interval(-oo, oo)
_check_dist(pdf_sym, domain)
dist = ContinuousDistributionHandmade(pdf_sym, domain)
pspace = SingleContinuousPSpace(name, dist)
return pspace.value
The following test code demonstrates the random variable in action.
from scipy.stats import norm
data = np.concatenate([norm.rvs(loc=-2, size=64), norm.rvs(loc=3, size=32)])
from sympy import N
from sympy.stats import density, Normal, E, P, median
Y = EmpiricalRV('Y',data)
ev = E(Y)
evfloat = float(N(ev))
print("Y :", Y)
print("density(Y) :", density(Y))
print("E(Y) :", ev)
print(f"N(E(Y)) : {evfloat:.4f}")
print(f"data.mean(): {data.mean():.4f}")
>>> Y : Y
>>> density(Y) : ContinuousDistributionHandmade(f_Y, Interval(-oo, oo))
>>> E(Y) : Integral(Y*f_Y(Y), (Y, -oo, oo))
>>> N(E(Y)) : -0.3882
>>> data.mean(): -0.3882
There may be further value in subclassing ContinuousDistributionHandmade or SingleContinuousDistribution

Explicitly calculate convolution in Python

I want to calculate a convolution in Python by explicitly evaluating the integral
and comparing the result with what I get from fftconvolve. The integral would be calculated using quad:
import numpy as np
from scipy.integrate import quad
from scipy.signal import fftconvolve
import matplotlib.pyplot as plt
from sympy import symbols
def f(x,a,b):
return np.exp(-(x-a)**2/b)
def g(x,a,b):
return np.exp(-(x-np.pi*a)**2/(2.9*b))
x = symbols('x')
a = 1.2
b = 4.7
t = np.linspace(-100,100,int(1e4))
dt = t[1] - t[0]
h1 = fftconvolve(f(t,a,b),g(t,a,b),mode='same')*dt
h2,_ = quad(f(t,a,b)*g(x-t,a,b),-np.inf,np.inf,epsabs=0,epsrel=1e-6,args=(a,b))
x = np.linspace(-100,100,int(1e4))
plt.figure()
plt.plot(t,h1,label='fftconvolve')
plt.plot(x,h2,label='brute force')
plt.legend()
plt.show()
I keep getting the error AttributeError: 'Mul' object has no attribute 'exp' which refers to the line h2,_ = quad(... when it is called by quad.
What does this error mean and is this an appropriate way to use quad to evaluate the integral?

Jacobi Method & Basic Matrix Math using NUMPY

I'm getting an import error for "norm". What am I not doing correct??
I'm open to constructive feedback on improving the code, however I have to keep the parameters as they are!
Thanks!!!
Code is below:
import numpy as np
from numpy import norm, inalg, array, zeros, diag, diagflat, dot, linalg
"""Test Case Data"""
A = np.matrix([[4,-1,-1],[-2,6,1],[-1,1,7]])
b = np.matrix([[3],[9],[-6]])
x = np.matrix([[0],[0],[0]])
"""Main Function"""
def jacobi(A, b, x, Tolerance, Iterations):
V = np.diag(A)
D = np.diag(V)
R = D-A
D_I = D.I
D = np.asmatrix(D)
Counter_1 = 1
tol_gauge = 100
while Counter_1 <= Iterations:
# I considered using the "dot" function in NUMPY but I was wary of mixed results
iterative_approach_form = D_I * ((R*x)+b)
tol_gauge = np.linalg.norm(iterative_approach_form-x)
x = iterative_approach_form
if initial_tol <= Tolerance:
return("The Solution x = {},y={}, z={} ".format(x[0], x[1], x[2]))
return("The Solution was found in %s interation(s)" %(Counter_1))
else:
pass
Counter_1 +=1
return("The Solution was not found in {} iteration(s)".format(Iterations))
You need to specify which numpy module you are importing from. The following works if you want to use a function only by its name:
from numpy import linalg
from numpy.linalg import norm
from numpy import zeros, array, diag, diagflat, dot
Looking at you code however, you don't need the second import line, because in the rest of the code the numpy functions are specified according to the accepted norm. For example, norm is already present in your code as np.linalg.norm.
There are three more issues with your code: 1) initial_tol is not assigned a value; 2) tol_gauge is assigned but not used in the code; 3) the last return statement is not indented properly (perhaps only here) and the same is very likely for the block in your while loop.

Categories

Resources