using numpy to fit data to y = y = a*x**2 + b*x + c (where c=0) - numpy

noob question here, please bear with me:
I'm trying to use numpy.polynomial.polynomial.Polynomial.fit(x,y,deg).convert().coef to get the coefficients for a zero-intersecting polynomial y = ax**2 + bx + c (where c=0) fit to a set of measured data. When I use deg=2 or deg=[0,1,2] I get nicely fitting coefficients for a,b,and c. However, when I use deg = [1,2] in order to force c=0, I still get three coefficients and they don't fit at all. What am I doing wrong?
Here is a code example with real data:
import numpy as np
from numpy.polynomial.polynomial import Polynomial as p
x = np.array([0, .1, .5, 1, 2])
y_series = np.array([[2, 319, 1693, 3713, 8695],
[3, 327, 1828, 4131, 10111],
[3, 304, 1653, 3617, 8678],
[4,300,1675,3745,8922],
[3, 298,1661,3653,8694],
[5, 304,1642,3686,8670],
[3, 313,1688,3724,8657],
[5, 315,1736,3821,8963],
[3, 247,1300,2767,6376]
])
for y in y_series:
print('x: ', x,'y: ', y)
print('deg=2: ', p.fit(x, y, deg=2).convert().coef)
print('deg=[0,1,2]:', p.fit(x, y, deg=[0,1,2]).convert().coef)
print('deg=[1,2]: ', p.fit(x, y, deg=[1,2]).convert().coef)
print('')

Similar to a previous post you are having trouble with the window argument of the Polynomial class. The coefficients are actually zero in your defined window, which is the default one namely [ -1, 1 ]. If one print the coefficients before calling convert() it is actually zero. Providing the window solves the issue.
Have a look at this:
import numpy as np
from numpy.polynomial.polynomial import Polynomial
def parabola( x, a, b, c , s=0 ):
if isinstance( x, ( int, float, complex ) ):
r = np.random.normal( scale=s )
else:
r = np.random.normal( scale=s, size=len( x ) )
return a + b * x + c * x**2 + r
xl = np.linspace( -2, 3, 15 )
yl = parabola( xl, 0.01, 0.8, 0.21, s=0.1 )
print("\n p1: ")
p1 = Polynomial.fit( xl, yl, deg=[0,1,2] )
print( p1.coef )
print( p1.convert().coef )
print("\n p2: ")
p2 = Polynomial.fit( xl, yl, deg=[1,2] )
print( p2.coef )
print( p2.convert().coef )
print( p2.domain )
print( p2.window )
print("\n p3: ")
p3 = Polynomial.fit( xl, yl, deg=[1,2], window=[ min( xl ), max( xl ) ] )
print( p3.coef )

Related

Tensorflow 2: Nested gradient tape produces wrong second pathwise derivative

there is a fair number of questions on gradients out there, but I haven't been able to fix my problem. In a nutshell: Trying to run a Monte Carlo simulation and get pathwise differentials. There are a few tutorials out there, but they do run into the same problem as my own code. I have boiled it down into the toy example below.
The second order derivative called gamma is wrong however (I'll highlight it below). So something is wrong with my nested gradient tape. Having the tape watch the variable has no effect actually. There must be something I am not aware of here. Any hint much appreciated.
edit: I have cross checked this with a deterministic function and the code works just fine. Second derivatives are calculated correctly using gradient tape. So no idea why it doesn't work on a Monte Carlo.
import numpy as np
import pandas as pd
import tensorflow as tf
from pprint import pprint
DTYPE = tf.float32
SEED = 3232
S0 = tf.Variable(100, dtype=DTYPE)
strike = tf.Variable(110, dtype=DTYPE)
time_to_expiry = tf.Variable(1, dtype=DTYPE)
implied_vol = tf.Variable(0.3, dtype=DTYPE)
v = dict(S0=S0,strike=strike,time_to_expiry=time_to_expiry,implied_vol=implied_vol)
#tf.function
def brownian(S0, dt, sigma, mu, dw):
dt_sqrt = tf.math.sqrt(dt)
shock = sigma * dt_sqrt * dw
drift = (mu - (sigma ** 2) / 2)
bm = tf.math.exp(drift * dt + shock)
out = S0 * tf.math.cumprod(bm, axis=1)
return out
#tf.function
def pricer_montecarlo(S0, strike, time_to_expiry, implied_vol, dw):
sigma = implied_vol
T = time_to_expiry
r = tf.constant(0.0,dtype=DTYPE)
K = strike
dt = T / dw.shape[1]
st = brownian(S0, dt, sigma, r, dw)
payout = tf.math.maximum(st[:, -1] - K, 0)
npv = tf.exp(-r * T) * tf.reduce_mean(payout)
return npv
def calculate_montecarlo(greeks=True):
nsims = 10**7
nobs = 2
dw = tf.random.normal((nsims, nobs), seed=SEED)
out = dict()
if greeks:
with tf.GradientTape() as g2:
g2.watch(v['S0'])
with tf.GradientTape() as g1:
g1.watch(v['S0'])
npv = pricer_montecarlo(**v, dw=dw)
dv = g1.gradient(npv, v)
g2.watch(dv)
d2v = g2.gradient(dv['S0'], v)
out["dv"] = {k: v.numpy() for k, v in dv.items()}
out["d2v"] = {k: v.numpy() for k, v in d2v.items()}
else:
npv = pricer_montecarlo(**v, dw=dw).numpy()
out["npv"] = npv.numpy()
return out
out = calculate_montecarlo()
pprint(out)
from py_vollib import black_scholes
from py_vollib.black_scholes.greeks import analytical
print('npv='+str(black_scholes.black_scholes('c', 100, 110, 1, 0, 0.3)))
print('dv S0='+str(analytical.delta('c', 100, 110, 1, 0, 0.3)))
print('d2v S0='+str(analytical.gamma('c', 100, 110, 1, 0, 0.3)))
print('dv implied_vol='+str(analytical.vega('c', 100, 110, 1, 0, 0.3)))
print('dv time_to_expiry='+str(analytical.theta('c', 100, 110, 1, 0, 0.3)))
Output: 'd2v': {'S0': 0.0..., should be close to 0.013112390443974165 (plus some stochastic noise).
{'d2v': {'S0': 0.0,
'implied_vol': 0.3933603,
'strike': 0.0,
'time_to_expiry': 0.059004053},
'dv': {'S0': 0.43342653,
'implied_vol': 39.336025,
'strike': -0.32001525,
'time_to_expiry': 5.9004045},
'npv': 8.140971}
npv=8.141012048964207
dv S0=0.4334094123285094
d2v S0=0.013112390443974165
dv implied_vol=0.39337171331922494
dv time_to_expiry=-0.01616596082133801

In Tensorflow, is there a built in function to compute states over time given a transition matrix?

I have a system given by this recursive relationship: xt = At xt-1 + bt. I wish to compute xt for all t, with At, bt and x0 given. Is there are built-in function for that? If I use a loop it would be extremely slow. Thanks!
There is sort of a way. Let's say you have your A matrices in a 3D tensor with shape (T, N, N), where T is the total number of time steps and N is the size of your vector. Similarly, B values are in a 2D tensor (T, N). The first step in the computation would be:
x1 = A[0] # x0 + B[0]
Where # represents matrix product. But you can convert this into a single matrix product. Suppose we add a value 1 at the end of x0, and we call that x0p (for prime):
x0p = tf.concat([x, [1]], axis=0)
And now we build a new 3D tensor Ap with shape (T, N+1, N+1), such that for each A[i] we concatenate B[i] as a new column, and then we add a row with N zeros and a single one at the end:
AwithB = tf.concat([tf.concat([A, tf.expand_dims(B, 2)], axis=2)], axis=1)
AnewRow = tf.concat([tf.zeros((T, 1, N), A.dtype), tf.ones((T, 1, 1), A.dtype)], axis=2)
Ap = tf.concat([AwithB, AnewRow], axis=1)
As it turns out, you can now say:
x1p = Ap[0] # x0p
And therefore:
x2p = Ap[1] # x1p = Ap[1] # Ap[0] # x0p
So we just need to compute all the matrix product of all matrices in Ap across the first dimension. Unfortunately, there does not seem to be a direct operation to compute that with TensorFlow, but you can do it relatively fast with tf.scan:
Ap_prod = tf.scan(tf.matmul, Ap)[-1]
And with that you just have to do:
xtp = Ap_prod # x0p
Here is a proof of concept (the code is tweaked to support single examples and batches, either in the A and B values or in the x)
import tensorflow as tf
def compute_state(a, b, x):
s = tf.shape(a)
t = s[-3]
n = s[-1]
# Add final 1 to x
xp = tf.concat([x, tf.ones_like(x[..., :1])], axis=-1)
# Add B column to A
a_b = tf.concat([tf.concat([a, tf.expand_dims(b, axis=-1)], axis=-1)], axis=-2)
# Make new final row for A
a_row = tf.concat([tf.zeros_like(a[..., :1, :]),
tf.ones_like(a[..., :1, :1])], axis=-1)
# Add new row to A
ap = tf.concat([a_b, a_row], axis=-2)
# Compute matrix product reduction
ap_prod = tf.scan(tf.matmul, ap)[..., -1, :, :]
# Compute final result
outp = tf.linalg.matvec(ap_prod, xp)
return outp[..., :-1]
#Test
tf.random.set_seed(0)
a = tf.random.uniform((10, 5, 5), -1, 1)
b = tf.random.uniform((10, 5), -1, 1)
x = tf.random.uniform((5,), -1, 1)
y = compute_state(a, b, x)
# Also works with batches of (a, b) or x
a = tf.random.uniform((100, 10, 5, 5), -1, 1)
b = tf.random.uniform((100, 10, 5), -1, 1)
x = tf.random.uniform((100, 5), -1, 1)
y = compute_state(a, b, x)

Faster way to patchify a picture to overlapping blocks

I'm looking for a FAST (and if possible memory afficiant) way to rewrite a function I crerated as part of Visual bag of words algorithm:
def get_pic_patches(pic, l, s): # "s" stands for stride
r, c = pic.shape
i, j = [0, 0]
x_range = list(range(0, r, s ) )
y_range = list(range(0, c , s ) )
patches = []
patches_location = []
for x in x_range: # without last two since it will exceed dimensions
for y in y_range: # without last two since it will exceed dimensions
if x+ l<= r and y+l <= c:
patch = pic[x:x + l , y:y + l ]
patches_location.append([x, y]) # patch location is the upper left pixel
patches.append( patch )
return patches, patches_location
it takes a grayscale image (NOT RGB!), desired patch length and stride value,
and gives back all patches as a list of numpy array.
On other qestions, I found this:
def patchify(img, patch_shape):
img = np.ascontiguousarray(img) # won't make a copy if not needed
X, Y = img.shape
x, y = patch_shape
shape = ((X-x+1), (Y-y+1), x, y) # number of patches, patch_shape
strides = img.itemsize*np.array([Y, 1, Y, 1])
return np.lib.stride_tricks.as_strided(img, shape=shape, strides=strides)
in order to get to return a list, I used it like this:
def patchify(img, patch_shape):
img = np.ascontiguousarray(img) # won't make a copy if not needed
X, Y = img.shape
x, y = patch_shape
shape = ((X-x+1), (Y-y+1), x, y) # number of patches, patch_shape
strides = img.itemsize*np.array([Y, 1, Y, 1])
patches = np.lib.stride_tricks.as_strided(img, shape=shape, strides=strides)
a,b,c,d = patches.shape
patches = patches.reshape(((a*b),c,d))
patches = patches.tolist()
return
but this was actually much slower than my original function! another problem is that is only works with stride = 1, and I want to be able to use all sorts of stride values.

Curve fitting a function of functions in matplotlib

I have 3 functions: E (energy), P (pressure) and H (enthalpy).
The given data data.dat, contains the variables V (volume) and E (energy):
# Volume: V_C_I Energy: E_C_I
111.593876 -1.883070511360E+03
113.087568 -1.883074916825E+03
114.632273 -1.883078906679E+03
116.184030 -1.883082373429E+03
117.743646 -1.883085344696E+03
119.326853 -1.883087860954E+03
120.927806 -1.883089938181E+03
122.538335 -1.883091557526E+03
124.158641 -1.883092750745E+03
125.789192 -1.883093540824E+03
125.790261 -1.883093800747E+03
127.176327 -1.883094160364E+03
128.358654 -1.883094017730E+03
128.542807 -1.883094255789E+03
129.977279 -1.883094094751E+03
131.390610 -1.883093689121E+03
132.812287 -1.883093053342E+03
134.242765 -1.883092185844E+03
135.682211 -1.883091101112E+03
137.130792 -1.883089807766E+03
138.588565 -1.883088314435E+03
The following script, performs:
1) a curve_fit of E versus V,
2) calculates P (pressure) using the def(P) function,
3) calculates H (enthalpy) using the def(H) function. (H = E + PV).
4) Performs 3 plots with the fitting curves: E versus P, P versus V and H versus P.
When plotting the fitting curve, I have used the following:
For example, for the E versus V curve:
plt.plot(V_C_I_lin, E(V_C_I_lin, *popt_C_I)
where V_C_I_lin is a linspace of volumes between the data points.
This seems fine:
Similarly, for the P versus V curve, the analogous scheme:
plt.plot(V_C_I_lin, P(V_C_I_lin, *popt_C_I), color='grey', label='E fit Data' )
produces the desired result:
The pressure for each data point is saved in the file ./E_V_P_H__C_I.dat by the script.
However, for the H versus P curve, the analogous scheme:
plt.plot(xp_C_I, H(V_C_I_lin, *popt_C_I), color='grey', label='H fit Data' )
where xp_C_I is a linspace of pressures, does not produce the correct fit:
Why this is happening in the third case?
Code:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import sys
import os
# Intial candidates for fit
E0_init = -941.510817926696
V0_init = 63.54960592453
B0_init = 76.3746233515232
B0_prime_init = 4.05340727164527
def E(V, E0, V0, B0, B0_prime):
return E0+ (2.293710449E+17)*(1E-21)*( (9.0/16.0)*(V0*B0) * ( (((V0/V)**(2.0/3.0)-1.0)**3.0)*B0_prime + ((V0/V)**(2.0/3.0)-1)**2 * (6.0-4.0*(V0/V)**(2.0/3.0)) ))
def P(V, E0, V0, B0, B0_prime):
f0=(3.0/2.0)*B0
f1=((V0/V)**(7.0/3.0))-((V0/V)**(5.0/3.0))
f2=((V0/V)**(2.0/3.0))-1
pressure= f0*f1*(1+(3.0/4.0)*(B0_prime-4)*f2)
return pressure
def H(V, E0, V0, B0, B0_prime):
return E(V, E0, V0, B0, B0_prime) + P(V, E0, V0, B0, B0_prime) * V
# Data (Red triangles):
V_not_p_f_unit_C_I, E_not_p_f_unit_C_I = np.loadtxt('data.dat', skiprows = 1).T
# A minor conversion of the data:
nFU_C_I = 2.0
nFU_C_II = 4.0
E_C_I = E_not_p_f_unit_C_I/nFU_C_I
V_C_I = V_not_p_f_unit_C_I/nFU_C_I
######
# Fitting and obtaining the parameters:
init_vals = [E0_init, V0_init, B0_init, B0_prime_init]
popt_C_I, pcov_C_I = curve_fit(E, V_C_I, E_C_I, p0=init_vals)
# Calculation of P:
pressures_per_F_unit_C_I = P(V_C_I, *popt_C_I)
# Calculation of H: H = E + PV
H_C_I = E_C_I + pressures_per_F_unit_C_I * V_C_I
# We save E, P and H into a file:
output_array_3 = np.vstack((E_C_I, V_C_I, pressures_per_F_unit_C_I, H_C_I)).T
np.savetxt('E_V_P_H__C_I.dat', output_array_3, header="Energy / FU (a.u.) \t Volume / FU (A^3) \t Pressure / F.U. (GPa) \t Enthalpy (a.u.)", fmt="%0.13f")
EnergyCI, VolumeCI, PressureCI, EnthalpyCI = np.loadtxt('./E_V_P_H__C_I.dat', skiprows = 1).T
# Plotting E vs V:
fig = plt.figure()
V_C_I_lin = np.linspace(VolumeCI[0], VolumeCI[-1], 100)
# Linspace for plotting the fitting curve:
V_C_I_lin = np.linspace(VolumeCI[0], VolumeCI[-1], 100)
p2, = plt.plot(V_C_I_lin, E(V_C_I_lin, *popt_C_I), color='grey', label='E fit Data' )
# Plotting the scattered points:
p1 = plt.scatter(VolumeCI, EnergyCI, color='red', marker="^", label='Data', s=100)
fontP = FontProperties()
fontP.set_size('small')
plt.legend((p1, p2 ), ("Data", 'E fit Data' ), prop=fontP)
plt.xlabel('V / Formula unit (Angstrom$^{3}$)')
plt.ylabel('E / Formula unit (a.u.)')
plt.ticklabel_format(useOffset=False)
plt.savefig('E_vs_V.pdf', bbox_inches='tight')
# Plotting P vs V:
fig = plt.figure()
# Linspace for plotting the fitting curve:
xp_C_I = np.linspace(PressureCI[-1], PressureCI[0], 100)
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, P(V_C_I_lin, *popt_C_I), color='grey', label='E fit Data' )
# Plotting the scattered points:
p1 = plt.scatter(VolumeCI, PressureCI, color='red', marker="^", label='Data', s=100)
fontP = FontProperties()
fontP.set_size('small')
plt.legend((p1, p2), ("Data", "E fit Data"), prop=fontP)
plt.xlabel('V / Formula unit (Angstrom$^{3}$)')
plt.ylabel('P (GPa)')
plt.ticklabel_format(useOffset=False)
plt.savefig('P_vs_V.pdf', bbox_inches='tight')
# Plotting H vs P:
fig = plt.figure()
xp_C_I = np.linspace(PressureCI[0], PressureCI[-1], 100)
V_C_I_lin = np.linspace(VolumeCI[0], VolumeCI[-1], 100)
# Linspace for plotting the fitting curve:
V_C_I_lin = np.linspace(VolumeCI[0], VolumeCI[-1], 100)
p2, = plt.plot(xp_C_I, H(V_C_I_lin, *popt_C_I), color='grey', label='H fit Data' )
# Plotting the scattered points:
p1 = plt.scatter(pressures_per_F_unit_C_I, H_C_I, color='red', marker="^", label='Data', s=100)
fontP = FontProperties()
fontP.set_size('small')
plt.legend((p1, p2 ), ("Data", 'H fit Data' ), prop=fontP)
plt.xlabel('P / Formula unit (GPa)')
plt.ylabel('H / Formula unit (a.u.)')
plt.ticklabel_format(useOffset=False)
plt.savefig('H_vs_V.pdf', bbox_inches='tight')
plt.show()
You cannot just plot H(V) versus some completely uncorrelated pressures xp_C_I.
plt.plot(xp_C_I, H(V_C_I_lin, *popt_C_I), )
Instead you need to plot H(V) against P(V), such that exactly the same V values are used for the pressure and the enthalpy:
plt.plot(P(V_C_I_lin, *popt_C_I), H(V_C_I_lin, *popt_C_I), )

How can I find a basis for the column space of a rectangular matrix?

Given a numpy ndarray with dimensions m by n (where n>m), how can I find the linearly independent columns?
One way is to use the LU decomposition. The factor U will be of the same size as your matrix, but will be upper-triangular. In each row of U, pick the first nonzero element: these are pivot elements, which belong to linearly independent columns. A self-contained example:
import numpy as np
from scipy.linalg import lu
A = np.array([[1, 2, 3], [2, 4, 2]]) # example for testing
U = lu(A)[2]
lin_indep_columns = [np.flatnonzero(U[i, :])[0] for i in range(U.shape[0])]
Output: [0, 2], which means the 0th and 2nd columns of A form a basis for its column space.
#user6655984's answer inspired this code, where I developed a function instead of the author's last line of code (finding pivot columns of U) so that it can handle more diverse A's.
Here it is:
import numpy as np
from scipy import linalg as LA
np.set_printoptions(precision=1, suppress=True)
A = np.array([[1, 4, 1, -1],
[2, 5, 1, -2],
[3, 6, 1, -3]])
P, L, U = LA.lu(A)
print('P', P, '', 'L', L, '', 'U', U, sep='\n')
Output:
P
[[0. 1. 0.]
[0. 0. 1.]
[1. 0. 0.]]
L
[[1. 0. 0. ]
[0.3 1. 0. ]
[0.7 0.5 1. ]]
U
[[ 3. 6. 1. -3. ]
[ 0. 2. 0.7 -0. ]
[ 0. 0. -0. -0. ]]
I came up with this function:
def get_indices_for_linearly_independent_columns_of_A(U: np.ndarray) -> list:
# I should first convert all "-0."s to "0." so that nonzero() can find them.
U_copy = U.copy()
U_copy[abs(U_copy) < 1.e-7] = 0
# Because some rows in U may not have even one nonzero element,
# I have to find the index for the first one in two steps.
index_of_all_nonzero_cols_in_each_row = (
[U_copy[i, :].nonzero()[0] for i in range(U_copy.shape[0])]
)
index_of_first_nonzero_col_in_each_row = (
[indices[0] for indices in index_of_all_nonzero_cols_in_each_row
if len(indices) > 0]
)
# Because two rows or more may have the same indices
# for their first nonzero element, I should remove duplicates.
unique_indices = sorted(list(set(index_of_first_nonzero_col_in_each_row)))
return unique_indices
Finally:
col_sp_A = A[:, get_indices_for_linearly_independent_columns_of_A(U)]
print(col_sp_A)
Output:
[[1 4]
[2 5]
[3 6]]
Try this one
def LU_decomposition(A):
"""
Perform LU decompostion of a given matrix
Args:
A: the given matrix
Returns: P, L and U, s.t. PA = LU
"""
assert A.shape[0] == A.shape[1]
N = A.shape[0]
P_idx = np.arange(0, N, dtype=np.int16).reshape(-1, 1)
for i in range(N - 1):
pivot_loc = np.argmax(np.abs(A[i:, [i]])) + i
if pivot_loc != i:
A[[i, pivot_loc], :] = A[[pivot_loc, i], :]
P_idx[[i, pivot_loc], :] = P_idx[[pivot_loc, i], :]
A[i + 1:, i] /= A[i, i]
A[i + 1:, i + 1:] -= A[i + 1:, [i]] * A[[i], i + 1:]
U, L, P = np.zeros_like(A), np.identity(N), np.zeros((N, N), dtype=np.int16)
for i in range(N):
L[i, :i] = A[i, :i]
U[i, i:] = A[i, i:]
P[i, P_idx[i][0]] = 1
return P.astype(np.float64), L, U
def get_bases(A):
assert A.ndim == 2
Q = gaussian_elimination(A)
M, N = Q.shape
pivot_idxs = []
for i in range(M):
j = i
while j < N and abs(Q[i, j]) < 1e-5:
j += 1
if j < N:
pivot_idxs.append(j)
return A[:, list(set(pivot_idxs))]