Overlap Integrals in Python - Storing Results in Array - python

I have a set of basis functions defined as:
def HO_wavefunction(x, n, x0, omega, m=1):
N = 1.0 / math.sqrt(2**n * math.factorial(n)) * ((m * omega)/(math.pi))**(0.25) # Normaliziation constant
y = (np.sqrt(m * omega)) * (x - x0)
return N * np.exp(-y * y / 2.0) * sp.hermite(n)(y)
#Define the basis
def enol_basis(x, n):
return HO_wavefunction(x, n, x0=Enolminx, omega=wenol)
I now want to compute the overlap integrals Sii = integral((SiSi)dx), Sjj = integral((SjSj)dx) and Sij = integral((Si*Sj)dx) of my basis functions and store them in some type of array. I tried the following:
G = 10
S = np.empty([G,G])
for n in range (G-1):
for m in range (G-1):
S[n][m]= np.trapz(enol_basis(x,n)*enol_basis(x,m),x)
print (S[n][m])
This only returns a single value instead of all the results stored in an array. If anyone could help me compute the overlap integrals as I defined them above and store the results in an array I would really appreciate it!

Solution:
G = 50
S = np.zeros([G,G])
for n in range (G):
for m in range (G):
S[n,m]= np.trapz(enol_basis(x,n)*enol_basis(x,m),x)
print (S)

Related

Differents results from create function in a different way - only length-1 arrays can be converted to Python scalars

I have defined the following functions in python:
from math import *
import numpy as np
import cmath
def BSM_CF(u, s0, T, r, sigma):
realp = -0.5*u**2*sigma**2*T
imagp = u*(s0+(r-0.5*sigma**2)*T)
zc = complex(realp, imagp)
return cmath.exp(zc)
def BSM_characteristic_function(v, x0, T, r, sigma):
cf_value = np.exp(((x0 / T + r - 0.5 * sigma ** 2) * 1j * v -
0.5 * sigma ** 2 * v ** 2) * T)
return cf_value
Parameters:
alpha = 1.5
K = 90
S0 = 100
T = 1
r = 0.05
sigma = 0.2
k = np.log(K / S0)
s0 = np.log(S0 / S0)
g = 1 # factor to increase accuracy
N = 2 ** 2
eta = 0.15
eps = (2*np.pi)/(N*eta)
b = 0.5 * N * eps - k
u = np.arange(1, N + 1, 1)
vo = eta * (u - 1)
v = vo - (alpha + 1) * 1j
BSMCF = BSM_characteristic_function(v, s0, T, r, sigma)
BSMCF_v2 = BSM_CF(0, s0, T, r, sigma)
print(BSMCF)
print(BSMCF_v2)
Both are the same functions. But, I get different results. How can I fix the function BSM_CF to get the same result from the function BSM_characteristic_function? The idea is get an array with len 4 values as in the funtion BSM_characteristic_function
Your calls are not identical. You are passing v in the first call and 0 in the second call. If I pass 0 for both, the results are identical. If I pass v, it complains because you can't call complex on a vector.
Numeric computation is Not always identical to symbolic algebra. For the first formula, you use complex computation as an alternative, which could result rounding errors in complex part. I came across such mistakes quite often as I used Mathematica, which loves to transfer a real formula to a complex one before doing the numeric computation.

Two mode Wigner function in python

I have attempted to adjust the qutip Wigner function, for it to process two mode states, specifically for the iterative method.
However the size of the array my output gives out is too big and I am unsure of why? That is when I try and calculate the Wigner logarithmic negativity using it, the integrals come out as arrays rather than singular values.
The code and description of what it is meant to do is below:
`import numpy as np
from scipy import (zeros, array, arange, exp, real, conj, pi,
copy, sqrt, meshgrid, size, polyval, fliplr, conjugate,
cos, sin)
import scipy.sparse as sp
import scipy.fftpack as ft
import scipy.linalg as la
from scipy.special import genlaguerre
from scipy.special import binom
from scipy.special import sph_harm
from qutip.qobj import Qobj, isket, isoper
from qutip.states import ket2dm
from qutip.parallel import parfor
from qutip.utilities import clebsch
from scipy.special import factorial
from qutip.cy.sparse_utils import _csr_get_diag
from qutip import *
def wigner2(psi, xvec1, yvec1, xvec2, yvec2, method='iterative', g=np.sqrt(2)):
"""Wigner function for a state vector or density matrix at points
`xvec1 + i * yvec1` `xvec2 + i * yvec2`
Parameters
state : qobj
A state vector or density matrix.
xvec1 : array_like
x-coordinates at which to calculate the Wigner function.
yvec1 : array_like
y-coordinates at which to calculate the Wigner function.
xvec2 : array_like
x-coordinates at which to calculate the Wigner function.
yvec2 : array_like
y-coordinates at which to calculate the Wigner function.
g : float
Scaling factor for a = 0.5 * g * (x + iy), default g = sqrt(2).
method : string {'iterative'}
Select method 'iterative', where 'iterative' uses
an iterative method to evaluate the Wigner functions for density
matrices :math:|m><n|. The 'iterative' method is default, and
in general recommended, but the 'laguerre' method is more efficient for
very sparse density matrices (e.g., superpositions of Fock states in a
large Hilbert space). The 'fft' method is the preferred method for
dealing with density matrices that have a large number of excitations
(>~50).
Returns
W : array
Values representing the Wigner function calculated over the specified
range [xvec1,yvec1] and [xvec2,yvec2]
"""
if not (psi.type == 'ket' or psi.type == 'oper' or psi.type == 'bra'):
raise TypeError('Input state is not a valid operator.')
if psi.type == 'ket' or psi.type == 'bra':
rho = ket2dm(psi) #always use density matrix
else:
rho = psi
if method == 'iterative':
return _wigner2_iterative(rho, xvec1, yvec1, xvec2, yvec2, g)
else:
raise TypeError(
"method must be 'iterative'")
def _wigner2_iterative(rho, xvec1, yvec1, xvec2, yvec2, g=np.sqrt(2)):
"""Using an iterative method to evaluate the wigner functions for the Fock
state :math:|mp><nq|
The Wigner function is calculated as
:math:W = \sum_{mpnq} \\rho_{mpnq} W_{mpnq} where :math:W_{mpnq} is the Wigner
function for the density matrix :math:|mp><nq|. In this implementation, for each row m*p, Wlist contains the Wigner functions
Wlist = [0, ..., W_mpmp, ..., W_mpnq]. As soon as one W_mpnq Wigner function is
calculated, the corresponding contribution is added to the total Wigner
function, weighted by the corresponding element in the density matrix :math:rho_{mpnq}."""
M1 = np.prod(ptrace(rho, 0).shape[0])
M2 = np.prod(ptrace(rho, 1).shape[0])
M = np.prod(rho.shape[0])
X1, Y1, X2, Y2 = np.meshgrid(xvec1, yvec1, xvec2, yvec2)
A1 = 0.5 * g * (X1 + 1.0j * Y1 + 0 * X2 + 0 * Y2)
A2 = 0.5 * g * (0 * X1 + 0 * Y1 + X2 + 1.0j * Y2)
Wlist1 = array([zeros(np.shape(A1), dtype=complex) for k in range(M)])
Wlist2 = array([zeros(np.shape(A2), dtype=complex) for k in range(M)])
W = real(rho[0, 0]) * real(Wlist1[0] * Wlist2[0])
for m in range(0,M1):
if m==0:
Wlist1[0] = exp(-2.0 * abs(A1) ** 2) / (pi)
else:
Wlist1[m] = ((2.0 * A1 * Wlist1[m - 1]) / sqrt(m))
for n in range(0, M2):
if n==0:
Wlist2[0] = exp(-2.0 * abs(A2) ** 2) / (pi)
else:
Wlist2[n] = ((2.0 * A2 * Wlist2[n - 1]) / sqrt(n))
if m != 0 and n != 0:
W += 2 * real(rho[0, m * M2 + n] * Wlist1[m] * Wlist2[n])
for p in range(0, M1):
temp1 = copy(Wlist1[m])
temp2 = copy(Wlist2[n])
if p==0:
Wlist1[p] = exp(-2.0 * abs(A1) ** 2) / (pi)
else:
Wlist1[p] = ((2.0 * conj(A1) * temp1 -sqrt(p) * Wlist1[p-1]) / sqrt(p))
for q in range(0, M2):
if q==0:
Wlist2[q] = exp(-2.0 * abs(A2) ** 2) / (pi)
else:
Wlist2[q] = ((2.0 * conj(A2) * temp2 - sqrt(q) * Wlist1[q - 1]) / sqrt(q))
W += 2 * real(rho[p * M2 + q, p * M2 + q] * Wlist1[p] * Wlist2[q])
if p != 0 and q !=0:
for k in range(p + 1, M1):
temp3 = (2 * A1 * Wlist1[k-1] - sqrt(k) * temp1) / sqrt(k)
temp1 = copy(Wlist1[k])
Wlist1[k] = temp3
for l in range(q +1, M2):
temp4 = (2 * A2 * Wlist2[l-1] - sqrt(l) * temp2) / sqrt(l)
temp2 = copy(Wlist2[l])
Wlist2[l] = temp4
W += 2 * real(rho[p * M2 + q, k *M2 +l] * Wlist1[k] * Wlist2[l])
return 0.5 * W * g **2'
Same problem for me, you can try this code.
def wigner2(rho,x1,p1,x2,p2,d1,d2):
# calculates the wigner function at point (alpha1,alpha2) for two modes
b1=tensor(destroy(d1),identity(d2)) #mechanical oscillator 1
b2=tensor(identity(d1),destroy(d2)) #mechanical oscillator 2
alpha1=(x1+1j*p1)/np.sqrt(2)
alpha2=(x2+1j*p2)/np.sqrt(2)
wig2=(rho*tensor(displace(d1,2*alpha1),displace(d2,2*alpha2))*(1j*np.pi*(b1.dag()*b1+b2.dag()*b2)).expm()).tr()/np.pi**2
return np.real(wig2)

Python loglog graph

In the following code I have implemented composite Simpsons Rule in python. I have tested it by integrating f = sinx over [0,pi/2] and plotting the resulting absolute error as a function of n for a suitable range of integer values for n. As shown below. Now I am trying to verify that my method is of order 4. To do this instead of plotting the error vs n I need to plot n vs n^(-4) to show that both have the same slope.
from math import pi, cos, sin
from matplotlib import pyplot as plt
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the composite Simpson's rule, using 2n subintervals """
h = (b - a) / (2*n)
s = f(a) + f(b)
for i in range(1, 2*n, 2):
s += 4 * f(a + i * h)
for i in range(2, 2*n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
diffs = {}
exact = 1 - cos(pi/2)
for n in range(1, 100):
result = simpson(lambda x: sin(x), 0.0, pi/2, n)
diffs[2*n] = abs(exact - result) # use 2*n or n here, your choice.
ordered = sorted(diffs.items())
x,y = zip(*ordered)
plt.autoscale()
plt.loglog(x,y)
plt.xlabel("Intervals")
plt.ylabel("Error")
plt.show()
this results in my error graph:
You can add another line to your plot by making another call to plt.loglog(). So just before your plt.show() you can add:
n = []
n_inv_4 = []
for i in range(1,100):
n.append(2*i)
n_inv_4.append(1.0 / ((2*i)**4))
plt.loglog(n, n_inv_4)
Note that the above calculations use (2*i) to match the (2*n) used in the simpson method.

Generating random numbers a, b, c such that a^2 + b^2 + c^2 = 1

To do some simulations in Python, I'm trying to generate numbers a,b,c such that a^2 + b^2 + c^2 = 1. I think generating some a between 0 and 1, then some b between 0 and sqrt(1 - a^2), and then c = sqrt(1 - a^2 - b^2) would work.
Floating point values are fine, the sum of squares should be close to 1. I want to keep generating them for some iterations.
Being new to Python, I'm not really sure how to do this. Negatives are allowed.
Edit: Thanks a lot for the answers!
According to this answer at stats.stackexchange.com, you should use normally distributed values to get uniformly distributed values on a sphere. That would mean, you could do:
import numpy as np
abc = np.random.normal(size=3)
a,b,c = abc/np.sqrt(sum(abc**2))
Just in case your interested in the probability densities I decided to do a comparison between the different approaches:
import numpy as np
import random
import math
def MSeifert():
a = 1
b = 1
while a**2 + b**2 > 1: # discard any a and b whose sum of squares already exceeds 1
a = random.random()
b = random.random()
c = math.sqrt(1 - a**2 - b**2) # fixed c
return a, b, c
def VBB():
x = np.random.uniform(0,1,3) # random numbers in [0, 1)
x /= np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)
return x[0], x[1], x[2]
def user3684792():
theta = random.uniform(0, 0.5*np.pi)
phi = random.uniform(0, 0.5*np.pi)
return np.sin(theta)* np.cos(phi), np.sin(theta)*np.sin(phi), np.cos(theta)
def JohanL():
abc = np.random.normal(size=3)
a,b,c = abc/np.sqrt(sum(abc**2))
return a, b, c
def SeverinPappadeux():
cos_th = 2.0*random.uniform(0, 1.0) - 1.0
sin_th = math.sqrt(1.0 - cos_th*cos_th)
phi = random.uniform(0, 2.0*math.pi)
return sin_th * math.cos(phi), sin_th * math.sin(phi), cos_th
And plotting the distributions:
%matplotlib notebook
import matplotlib.pyplot as plt
f, axes = plt.subplots(3, 4)
for func_idx, func in enumerate([MSeifert, JohanL, user3684792, VBB]):
axes[0, func_idx].set_title(str(func.__name__))
res = [func() for _ in range(50000)]
for idx in range(3):
axes[idx, func_idx].hist([i[idx] for i in res], bins='auto')
axes[0, 0].set_ylabel('a')
axes[1, 0].set_ylabel('b')
axes[2, 0].set_ylabel('c')
plt.tight_layout()
With the result:
Explanation: The rows show the distributions for a, b and c respectively while the columns show the histograms (distributions) of the different approaches.
The only approaches that give a uniformly random distribution in the range (-1, 1) are JohanLs and Severin Pappadeux's approach. All other approaches have some features like spikes or a functional behavior in the range [0, 1). Note that these two solution currently gives values between -1 and 1 while all other approaches give values between 0 and 1.
I think it is actually a cool problem, and a nice way to do this is to just use spherical polar coordinates and generate the angles at random.
import random
import numpy as np
def random_pt():
theta = random.uniform(0, 0.5*np.pi)
phi = random.uniform(0, 0.5*np.pi)
return np.sin(theta)* np.cos(phi), np.sin(theta)*np.sin(phi), np.cos(theta)
You could do it like this:
import random
import math
def three_random_numbers_adding_to_one():
a = 1
b = 1
while a**2 + b**2 > 1: # discard any a and b whose sum of squares already exceeds 1
a = random.random()
b = random.random()
c = math.sqrt(1 - a**2 - b**2) # fixed c
return a, b, c
a, b, c = three_random_numbers_adding_to_one()
print(a**2 + b**2 + c**2)
However floats have only limited precision so these won't add to exactly 1, just approximately.
You may need to check if the numbers generated with this function are "random enough". It could be that this setup biases the "randomness".
The "right" answer depends on whether you are looking for a uniform random distribution in space, or on the surface of a sphere, or something else. If you are looking for points on the surface of a sphere, you still have to worry about the cos(theta) factor which will cause points to appear "bunched up" near the poles of the sphere. Since exact nature is not clear from your question, here is a "totally random" distribution that should work:
x = np.random.uniform(0,1,3) # random numbers in [0, 1)
x /= np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)
Another advantage here is that since we are using numpy arrays, you can quickly scale to large sets of points too, by using x = np.random.uniform(0, 1, (3, n)) for any n.
Time to add another solution, heh...
This time it is truly uniform on the unit sphere point picking - check http://mathworld.wolfram.com/SpherePointPicking.html for details
import math
import random
def random_pt():
cos_th = 2.0*random.uniform(0, 1.0) - 1.0
sin_th = math.sqrt(1.0 - cos_th*cos_th)
phi = random.uniform(0, 2.0*math.pi)
return sin_th * math.cos(phi), sin_th * math.sin(phi), cos_th
for k in range(0, 100):
a, b, c = random_pt()
print("{0} {1} {2} {3}".format(a, b, c, a*a + b*b + c*c))

Python, fourier series of discrete data

I am trying to Find the Fourier series representation for n number of harmonics of a discrete time data set. The data is not originally periodic, so I performed a periodic extension on the data set and the result can be seen in the waveform below.
I have tried to replicate the solution in this question : Calculate the Fourier series with the trigonometry approach
However The results I received did not produce a proper output as can be seen in the pictures below. It seems that the computation simply outputs an offset version of the original signal.
The data set I am working with is a numpy array with 4060 elements.
How can I properly compute and graph the Fourier series decomposition of a discrete data set?
Here is the code i used, it is almost identical to that in the example referred to in the link, except changes have been made to accommodate my own signal data.
# dat is a list with the original non periodic data
# persig is essentially dat repeated over several periods
# Define "x" range.
l = len(persig)
x = np.linspace(0,1,l)
print len(x)
# Define "T", i.e functions' period.
T = len(dat)
print T
L = T / 2
# "f(x)" function definition.
def f(x):
persig = np.asarray(persig)
return persig
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 5):
a0 = a(0, L)
sum = np.zeros(np.size(x))
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos((i * np.pi * x) / L)) + (b(i, L) * np.sin((i * np.pi * x) / L)))
return (a0 / 2) + sum
plt.plot(x, f(x))
plt.plot(x, Sf(x, L))
plt.show()

Categories