Complex function of real arguments - python

I am solving a physical problem in which I have to work with the unbounded radial solution of the Schroedinger equation in the presence of a point-like charged particle Zeff.
I define the UNBRWAVE function in python as:
import numpy as np
import math as mh
from mpmath import *
a0 = 1/3.7 #edited new
Zeff = 1.0 #edited new
def UNBRWAVE(r, k, L, n, l):
return (2.0*np.pi)**(3.0/2.0)*(2.0*k*r)**L*(np.sqrt(2.0/np.pi)*np.abs(mh.gamma(L+1.0-(1j*Zeff)/(k*a0)))*np.exp(np.pi*Zeff/(2.0*k*a0)))/nh.gamma(2.0*L+1.0+1.0)*np.exp(-1j*k*r)*hyp1f1(L+1.0-(1j*Zeff)/(k*a0), 2.0*(L+1.0), 2.0*1j*k*r)
Where Zeff=1, 1j is the imaginary unit and hyp1f1 is the hypergeometric 1F1 function. The input parameters are r(a real number), k (a real number), L (an integer number), n (an integer number), l(an integer number). Whenever I call this function, like in
UNBRWAVE(10.0, 5.0, 5, 1, 0)
I get the error message
TypeError: can't convert complex to float
I am new in python so I would like to know how to code better this complex function of real arguments to get the values and perform operations with them.

Use scipy.special.gamma() instead of math.gamma(). The latter does not support complex argument and complains that it cannot convert the complex to float.
Following #devesh-kumar-singh advice would help you find the problematic part of the long expression.
The code lacks definition of a0, and nh.gamma contains a typo.

Related

Sympy not properly displaying conjugate of square root of real

I keep getting expressions like this image, despite declaring these symbols as reals.
The code to reproduce is:
import sympy as sp
delta = sp.Symbol('delta', real=True)
f = sp.sqrt(1/delta)
prod = sp.conjugate(f)*f
prod.subs(delta,delta)
I expected to get 1/delta
Also trying simplify() does not work either.
According to the official SymPy Docs for conjugate, it looks like the function is supposed to return the complex conjugate for its input. In other words, it takes the complex part of the number and flips the sign.
In your example, you are taking the square root of a variable. If delta = -1, then the resulting conjugate could be unreal and thus different than if delta was any other integer. Thus, SymPy wraps the result in a conjugate object.
If you want to tell Sympy that your variable delta is positive (and thus f must be real), then you should define it as delta = sp.Symbol('delta', real=True, positive=True).

Integer optimization/maximization in numpy

I need to estimate the size of a population, by finding the value of n which maximises scipy.misc.comb(n, a)/n**b where a and b are constants. n, a and b are all integers.
Obviously, I could just have a loop in range(SOME_HUGE_NUMBER), calculate the value for each n and break out of the loop once I reach an inflexion in the curve. But I wondered if there was an elegant way of doing this with (say) numpy/scipy, or is there some other elegant way of doing this just in pure Python (e.g. like an integer equivalent of Newton's method?)
As long as your number n is reasonably small (smaller than approx. 1500), my guess for the fastest way to do this is to actually try all possible values. You can do this quickly by using numpy:
import numpy as np
import scipy.misc as misc
nMax = 1000
a = 77
b = 100
n = np.arange(1, nMax+1, dtype=np.float64)
val = misc.comb(n, a)/n**b
print("Maximized for n={:d}".format(int(n[val.argmax()]+0.5)))
# Maximized for n=181
This is not especially elegant but rather fast for that range of n. Problem is that for n>1484 the numerator can already get too large to be stored in a float. This method will then fail, as you will run into overflows. But this is not only a problem of numpy.ndarray not working with python integers. Even with them, you would not be able to compute:
misc.comb(10000, 1000, exact=True)/10000**1001
as you want to have a float result in your division of two numbers larger than the maximum a float in python can hold (max_10_exp = 1024 on my system. See sys.float_info().). You couldn't use your range in that case, as well. If you really want to do something like that, you will have to take more care numerically.
You essentially have a nicely smooth function of n that you want to maximise. n is required to be integral but we can consider the function instead to be a function of the reals. In this case, the maximising integral value of n must be close to (next to) the maximising real value.
We could convert comb to a real function by using the gamma function and use numerical optimisation techniques to find the maximum. Another approach is to replace the factorials with Stirling's approximation. This gives a moderately complicated but tractable algebraic expression. This expression is not hard to differentiate and set to zero to find the extrema.
I did this and obtained
n * (b + (n-a) * log((n-a)/n) ) = a * b - a/2
This is not straightforward to solve algebraically but easy enough numerically (e.g. using Newton's method, as you suggest).
I may have made a mistake in the algebra, but I typed the a = 77, b = 100 example into Wolfram Alpha and got 180.58 so the approach seems to work.

Python plot of force in Lennard-Jones system gives TypeError

I am trying to plot the force on the ith particle as function of its distance from the jth particle (ie. xi-xj) in a Lennard-Jones system. The force is given by
where sigma and epsilon are two parameters, Xi is a known quantity and Xj is variable. The force directs from the ith particle to the jth particle.
The code that I have written for this is given below.
from pylab import*
from numpy import*
#~~~ ARGON VALUES ~~~~~~~~(in natural units)~~~~~~~~~~~~~~~~
epsilon=0.0122 # depth of potential well
sigma=0.335 # dist of closest approach
xi=0.00
xj=linspace(0.1,1.0,300)
f = 48.0*epsilon*( ((sigma**12.0)/((xi-xj)**13.0)) - ((sigma**6.0)/2.0/((xi-xj)**7.0)) ) * float(xj-xi)/abs(xi-xj)
plot(xj,f,label='force')
legend()
show()
But it gives me this following error.
f = 48.0*epsilon*( ((sigma**12.0)/((xi-xj)**11.0)) - ((sigma**6.0)/2.0/((xi-xj)**5.0)) ) * float(xj-xi)/abs(xi-xj)
TypeError: only length-1 arrays can be converted to Python scalars
Can someone help me solve this problem. Thanks in advance.
The error is with this part of the expression:
float(xj-xi)
Look at the answer to a related question. It appears to be conflict between Python built-in functions and Numpy functions.
If you take out the 'float' it at least returns. Does it give the correct numbers?
f = 48.0*epsilon*( ((sigma**12.0)/((xi-xj)**11.0)) - ((sigma**6.0)/2.0/((xi-xj)**5.0)) ) * (xj-xi)/abs(xi-xj)
Instead of the term float(xj-xi)/abs(xi-xj) you should use
sign(xj-xi)
If you really want to do the division, since xi and xj are already floats you could just do:
(xj-xi)/abs(xi-xj)
More generally, if you need to convert a numpy array of ints to floats you could use either of:
1.0*(xj-xi)
(xj-xi).astype(float)
Even more generally, it's helpful in debugging to not use equations that stretch across the page because with smaller terms you can identify the location of the errors more easily. It also often runs faster. For example, here you calculate xi-xj four times, when really it only needs to be done once. And it would be easier to read:
x = xi -xj
f = 48*epsilon*(s**12/x**13 - s**6/2/x**7)
f *= sign(-x)
The TypeError is due to float(xi-xj). float() cannot convert an iterable to a single scalar value. Instead, iterate over xj and convert each value in xi-xj to float. This can be easily done with
x = [float(j - xi) for j in xj)]

Associated Legendre Function

Hi I am writing Python code which returns the associated Legendre function.
Using numpy poly1d function on this part,
firstTerm = (np.poly1d([-1,0,1]))**(m/2.0) # HELP!
It yields an error since it can only be raised to integer.
Is there any other alternative where I can raise the desired function to power 1/2 and etc.?
The reason you can't raise your poly1d to half-integer power is that that would not be a polynomial, since it would contain square roots.
While in principle you could orthogonalize the functions yourself, or construct the functions from something like sympy.special.legendre, but your safest bet is symbolic math. And hey, we already have sympy.functions.special.polynomials.assoc_legendre! Since symbolic math is slow, you should probably use sympy.lambdify to turn each function into a numerical one:
import sympy as sym
x = sym.symbols('x')
n = 3
m = 1
legfun_sym = sym.functions.special.polynomials.assoc_legendre(n,m,x)
legfun_num = sym.lambdify(x,legfun_sym)
print(legfun_sym)
print(legfun_num)
x0 = 0.25
print(legfun_sym.evalf(subs={x:x0}) - legfun_num(x0))
This prints
-sqrt(-x**2 + 1)*(15*x**2/2 - 3/2)
<function <lambda> at 0x7f0a091976e0>
-1.11022302462516e-16
which seems to make sense (the first is the symbolic function at x, the second shows that lambdify indeed creates a lambda from the function, and the last one is the numerical difference of the two functions at the pseudorandom point x0 = 0.25, and is clearly zero within machine precision).

Python using scipy.optimise to find the solution to an equation

I want to solve an equation using scipy.optimise
I want to find the solution, n, for the equation
a**n + b**n = c**n
where
a=2.3
b=2.4
c=2.94
I have a list of triplets (a,b,c) I want to experiment with and I know the range of the exponent n will always be 2.0 < n < 4.0. Could I use this fact to speed up the convergence of the solution.
If your function is scalar, and accepts a scalar (your case), and if you know that:
your solution is in a given interval, and the function is continuous in the same interval (your case)
you are interested in one solution, not necessarily in all (if more than 1) solutions in that interval
You can speed up the solution using the bisection algorithm, implemented here in scipy, which requires the conditions above to guarantee convergence.
The idea behind the algorithm is quite simple, with log convergence.
See this fundamental calculus theorem on which the algorithm is based.
EDIT: I couldn't resist, here you have a MWE
import scipy.optimize as opt
def sol(a,b,c):
f = lambda n : a**n + b**n - c**n
return opt.bisect(f,2,4)
print(sol(2.3,2.4,2.94)
>3.1010655957
As requested in the comments, here's how to do it using mpmath.
We supply the a, b, c parameters as strings rather than as Python floats for maximum accuracy. Converting strings to mpf (mp floats) will be as accurate as the current precision allows. If instead we convert from Python floats then we'd be using numbers that suffer from the imprecision inherent in Python floats.
mp.dps allows us to set the precision in the form of the number of decimal digits.
The mpmath findroot function accepts an initial approximation argument. This can be a single value, or it may be an interval, given as a list or a tuple. It's ok to use Python floats in that interval.
from mpmath import mp
mp.dps = 30
a, b, c = [mp.mpf(u) for u in ('2.3', '2.4', '2.94')]
def f(x):
return a**x + b**x - c**x
x = mp.findroot(f, [2, 4])
print(x, f(x))
output
3.10106559575904097402104750305 -3.15544362088404722164691426113e-30
By default, findroot uses a simple secant solver. The docs recommend using the 'anderson' or 'ridder' solvers when supplying an interval, but for this equation all 3 solvers give identical results.

Categories