Two mode Wigner function in python - 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)

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.

Fitting curve with conditions

I'm trying to simulate an exoplanet transit and to determine its orbital characteristics with curve fitting. However, the intersection area between two circles needs to distinguish two cases: if the center of the smallest circle is in the biggest or not. This is a problem for scipy with the function curve_fit, calling an array in my function cacl_aire. The function transit simulates the smallest disc's evolution with time.
Here's my code:
import numpy as np
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit
import xlrd
dt = 0.1
Vx = 0.08
Vy = 0
X0 = -5
Y0 = 0
R = 2
r = 0.7
X = X0
Y = Y0
doc = xlrd.open_workbook("transit data.xlsx")
feuille_1 = doc.sheet_by_index(0)
mag = [feuille_1.cell_value(rowx=k, colx=4) for k in range(115)]
T = [feuille_1.cell_value(rowx=k, colx=3) for k in range(115)]
def calc_aire(r, x, y):
D2 = x * x + y * y
if D2 >= (r + R)**2:
return 0
d = (r**2 - R**2 + D2) / (2 * (D2**0.5))
d2 = D2**0.5 - d
if abs(d) >= r:
return min([r * r * np.pi, R * R * np.pi])
H = (r * r - d * d)**0.5
As = np.arccos(d / r) * r * r - d * H
As2 = R * R * np.arccos(d2 / R) - d2 * H
return As + As2
def transit(t, r, X0, Y0, Vx, Vy):
return -calc_aire(r, X0 + Vx * t, Y0 + Vy * t)
best_vals = curve_fit(transit, T, mag)[0]
print('best_vals: {}'.format(best_vals))
plt.figure()
plt.plot(T, mag)
plt.draw()
I have the following error :
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() with the line 28 :
if D2 >= (r + R)**2:
Here is my database:
https://drive.google.com/file/d/1SP12rrHGjjpHfKBQ0l3nVMJDIRCPlkuf/view?usp=sharing
I don't see any trick to solve my problem.

How can I plot a 3D graph of a multivariate integral function, and find its global minima

I have a cost function f(r, Q), which is obtained in the code below. The cost function f(r, Q) is a function of two variables r and Q. I want to plot the values of the cost function for all values of r and Q in the range given below and also find the global minimum value of f(r, Q).
The range of r and Q are respectively :
0 < r < 5000
5000 < Q < 15000
The plot should be in r, Q and f(r,Q) axis.
Code for the cost function:
from numpy import sqrt, pi, exp
from scipy import optimize
from scipy.integrate import quad
import numpy as np
mean, std = 295, 250
l = 7
m = 30
p = 15
w = 7
K = 100
c = 5
h = 0.001 # per unit per day
# defining Cumulative distribution function
def cdf(x):
cdf_eqn = lambda t: (1 / (std * sqrt(2 * pi))) * exp(-(((t - mean) ** 2) / (2 * std ** 2)))
cdf = quad(cdf_eqn, -np.inf, x)[0]
return cdf
# defining Probability density function
def pdf(x):
return (1 / (std * sqrt(2 * pi))) * exp(-(((x - mean) ** 2) / (2 * std ** 2)))
# getting the equation in place
def G(r, Q):
return K + c * Q \
+ w * (quad(cdf, 0, Q)[0] + quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]) \
+ p * (mean * l - r + quad(cdf, 0, r)[0])
def CL(r, Q):
return (Q - r + mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / mean
def I(r, Q):
return h * (Q + r - mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / 2
def f(params):
r, Q = params
TC = G(r, Q)/CL(r, Q) + I(r, Q)
return TC
How to plot this function f(r,Q) in a 3D plot and also get the global minima or minimas and values of r and Q at that particular point.
Additionally, I already tried using scipy.optimize.minimize to minimise the cost function f(r, Q) but the problem I am facing is that, it outputs the results - almost same as the initial guess given in the parameters for optimize.minimize. Here is the code for minimizing the function:
initial_guess = [2500., 10000.]
result = optimize.minimize(f, initial_guess, bounds=[(1, 5000), (5000, 15000)], tol=1e-3)
print(result)
Output:
fun: 2712.7698818644253
hess_inv: <2x2 LbfgsInvHessProduct with dtype=float64>
jac: array([-0.01195986, -0.01273293])
message: b'CONVERGENCE: REL_REDUCTION_OF_F_<=_FACTR*EPSMCH'
nfev: 6
nit: 1
status: 0
success: True
x: array([ 2500.01209628, 10000.0127784 ])
The output x: array([ 2500.01209628, 10000.0127784 ]) - Which I doubt is the real answer and also it is almost same as the initial guess provided. Am I doing anything wrong in minimizing or is there any other way to do it? So I want to plot the cost function and look around for myself.
It could be great if I can have an interactive plot to play around with
My answer is concerned only with plotting but in the end I'll comment on the issue of minimax.
For what you need a 3D surface plot is, imho, overkill, I'll show you instead show the use of contourf and contour to have a good idea of what is going on with your function.
First, the code — key points:
your code, as is, cannot be executed in a vector context, so I wrote an explicit loop to compute the values,
due to Matplotib design, the x axis of matrix data is associated on columns, this has to be accounted for,
the results of the countour and contourf must be saved because they are needed for the labels and the color bar, respectively,
no labels or legends because I don't know what you are doing.
That said, here it is the code
import matplotlib.pyplot as plt
import numpy as np
from numpy import sqrt, pi, exp
from scipy.integrate import quad
mean, std = 295, 250
l, m, p = 7, 30, 15
w, K, c = 7, 100, 5
h = 0.001 # per unit per day
# defining Cumulative distribution function
def cdf(x):
cdf_eqn = lambda t: (1 / (std * sqrt(2 * pi))) * exp(-(((t - mean) ** 2) / (2 * std ** 2)))
cdf = quad(cdf_eqn, -np.inf, x)[0]
return cdf
# defining Probability density function
def pdf(x):
return (1 / (std * sqrt(2 * pi))) * exp(-(((x - mean) ** 2) / (2 * std ** 2)))
# getting the equation in place
def G(r, Q):
return K + c * Q \
+ w * (quad(cdf, 0, Q)[0] + quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]) \
+ p * (mean * l - r + quad(cdf, 0, r)[0])
def CL(r, Q):
return (Q - r + mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / mean
def I(r, Q):
return h * (Q + r - mean * l - quad(cdf, 0, Q)[0]
- quad(lambda x: cdf(r + Q - x) * cdf(x), 0, r)[0]
+ quad(cdf, 0, r)[0]) / 2
# pulling it all together
def f(r, Q):
TC = G(r, Q)/CL(r, Q) + I(r, Q)
return TC
nr, nQ = 6, 11
r = np.linspace(0, 5000, nr)
Q = np.linspace(5000, 15000, nQ)
z = np.zeros((nr, nQ)) # r ←→ y, Q ←→ x
for i, ir in enumerate(r):
for j, jQ in enumerate(Q):
z[i, j] = f(ir, jQ)
print('%2d: '%i, ','.join('%8.3f'%v for v in z[i]))
fig, ax = plt.subplots()
cf = plt.contourf(Q, r, z)
cc = plt.contour( Q, r, z, colors='k')
plt.clabel(cc)
plt.colorbar(cf, orientation='horizontal')
ax.set_aspect(1)
plt.show()
and here the results of its execution
$ python cost.py
0: 4093.654,3661.777,3363.220,3120.073,2939.119,2794.255,2675.692,2576.880,2493.283,2426.111,2359.601
1: 4072.865,3621.468,3315.193,3068.710,2887.306,2743.229,2626.065,2528.934,2447.123,2381.802,2316.991
2: 4073.852,3622.443,3316.163,3069.679,2888.275,2744.198,2627.035,2529.905,2448.095,2382.775,2317.965
3: 4015.328,3514.874,3191.722,2939.397,2758.876,2618.292,2505.746,2413.632,2336.870,2276.570,2216.304
4: 3881.198,3290.628,2947.273,2694.213,2522.845,2394.095,2293.867,2213.651,2148.026,2098.173,2047.140
5: 3616.675,2919.726,2581.890,2352.015,2208.814,2106.289,2029.319,1969.438,1921.555,1887.398,1849.850
$
I can add that global minimum and global maximum are in the corners, while there are two sub-horizontal lines of local minima (lower line) and local maxima (upper line) in the approximate regions r ≈ 1000 and r ≈ 2000.

finite difference methods in python

I am trying to calculate g(x_(i+2)) from the value g(x_(i+1)) and g(x_i), i is an integer, assuming I(x) and s(x) are Gaussian function. If we know x_i = 100, then the summation from 0 to 100, I don't know how to handle g(x_i) with the subscript in python, knowing the first and second value, we can find the third value, after n cycle, we can find the nth value.
Equation:
code:
import numpy as np
from matplotlib import pyplot as p
from math import pi
def f_s(x, mu_s, sig_s):
ss = -np.power(x - mu_s, 2) / (2 * np.power(sig_s, 2))
return np.exp(ss) / (np.power(2 * pi, 2) * sig_s)
def f_i(x, mu_i, sig_i):
ii = -np.power(x - mu_i, 2) / (2 * np.power(sig_i, 2))
return np.exp(ii) / (np.power(2 * pi, 2) * sig_i)
# problems occur in this part
def g(x, m, mu_s, sig_s, mu_i, sig_i):
for i in range(1, m): # specify the number x, x_1, x_2, x_3 ......X_m
h = (x[i + 1] - x[i]) / e
for n in range(0, x[i]): # calculate summation
sum_f = (f_i(x[i], mu_i, sig_i) - f_s(x[i] - n, mu_s, sig_s) * g_x[n]) * np.conj(f_s(n +
x[i], mu_s, sig_s))
g_x[1] = 1 # initial value
g_x[2] = 5
g_x[i + 2] = h * sum_f + 2 * g_x[i + 1] - g_x[i]
return g_x[i + 2]
x = np.linspace(-10, 10, 10000)
e = 1
d = 0.01
m = 1000
mu_s = 2
sig_s = 1
mu_i = 1
sig_i = 1
p.plot(x, g(x, m, mu_s, sig_s, mu_i, sig_i))
p.legend()
p.show()
result:
I(x) and s(x)

Poisson (npr) Size Alteration Returns ValueError (wrt arbitrary paths and array creation)

If I sample a non-central chi-square distribution using a Poisson distribution, I am unable to alter the size and can only input the mean, "nc / 2" (I must set size = 1 or it also returns the same error):
n = np.random.poisson(nc / 2, 1) # generates a random variable from the poisson distribution with
# mean: non-centrality parameter / 2
x[t] = c * mp.nsum(lambda i: np.random.standard_normal() ** 2, [0, v + 2 * n])
If I attempt to increase the size to the number of simulations being run
n = np.random.poisson(nc / 2, simulations)
where simulations = 10000, I receive:
"ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
Running the code with 1 simulation produces one desired result, and every run produces another random path.
Graph created under 10,000 simulations with size = one
However, it is a necessity to have the graph composed of paths determined by each iteration of the simulation. Under a different condition, the non-central chi-square distribution is determined by the code:
x[t] = c * ((np.random.standard_normal(simulations) + nc ** 0.5) ** 2 + mp.nsum(
lambda i: np.random.standard_normal(simulations) ** 2, [0, v - 1]))
which does produce the desired result
Graph produced by the line of code above
How can I obtain a different path for x[t] despite not being able to change the size of the Poisson distribution (i.e. not have the same path for each of the 10,000 simulations)
If required:
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import stats
import mpmath as mp
T = 1
beta = 1.5
x0 = 0.05
q = 0
mu = x0 - q
alpha = - (2 - beta) * mu
sigma0 = 0.1
sigma = (2 - beta) * sigma0
b = - (1 - beta) / (2 * mu) * sigma ** 2
simulations = 10000
M = 50
dt = T / M
def srd_sampled_nxc2():
x = np.zeros((M + 1, simulations))
x[0] = x0
for t in range(1, M + 1):
v = 4 * b * alpha / sigma ** 2
c = (sigma ** 2 * (1 - np.exp(-alpha * dt))) / (4 * alpha)
nc = np.exp(-alpha * dt) / c * x[t - 1] # The non-centrality parameter lambda
if v > 1:
x[t] = c * ((np.random.standard_normal(simulations) + nc ** 0.5) ** 2 + mp.nsum(
lambda i: np.random.standard_normal(simulations) ** 2, [0, v - 1]))
else:
n = np.random.poisson(nc / 2, 1)
x[t] = c * mp.nsum(lambda i: np.random.standard_normal() ** 2, [0, v + 2 * n])
return x
x1 = srd_sampled_nxc2()
plt.figure(figsize=(10, 6))
plt.plot(x1[:, :10], lw=1)
plt.xlabel('time')
plt.ylabel('index')
plt.show()
I've realized that the variable beta greater than 1 creates a negative v and a very large nc. There was nothing to fill the array with due to the fact that no distribution could be created as v couldn't go positive. I am under the impression that b must be made positive and thus solving the negative v and allowing the program to run.

Categories