Why it does not calculate properly?
Correct solution is c=25.672 and b2=10.24.
Here solver returns the input values.
Thanks for help!
from numpy import *
from scipy.optimize import *
#UNITS:
psi = 6895.
ft=0.3048
inch=0.0254
psisqin=psi*sqrt(inch)
#DATA:
K_Ict=1500.*psisqin
K_Icb=1700.*psisqin
sigma_2=6700.*psi
sigma_1=6000.*psi
sigma_3=7200.*psi
hp=105.*ft
P = 6500*psi
def f(p):
b2,c= p
F1 = sqrt(pi*c)*(K_Icb-K_Ict)/2 - ( (sigma_2-sigma_1)*sqrt(c**2-b2**2) - (sigma_3-sigma_1)*sqrt(c**2-(hp-b2)**2) )
F2 = sqrt(pi)*(K_Icb+K_Ict)/(2*sqrt(c)) - ( (sigma_2-sigma_1)*arcsin(b2/c) + (sigma_3-sigma_1)*arcsin((hp-b2)/c) - (sigma_2+sigma_3-2*P)*pi/2 )
return (F1,F2)
b2, c = fsolve(f,(16.002,30))
print b2, c
Not entirely sure why but I think it's because you are leaving one of the roots out of the range, so it just returns the boundaries you gave. The true reason depends on whatever method is using to find the roots. The documentation states:
fsolve is a wrapper around MINPACK’s hybrd and hybrj algorithms.
These algorithms are a bit beyond my expertise but you'll find documentation about them easily. This link is one example.
In any case if you change the boundaries to contain both your roots you should obtain the correct results (within a tolerance):
from numpy import *
from scipy.optimize import *
#UNITS:
psi = 6895.
ft=0.3048
inch=0.0254
psisqin=psi*sqrt(inch)
#DATA:
K_Ict=1500*psisqin
K_Icb=1700*psisqin
sigma_2=6700*psi
sigma_1=6000*psi
sigma_3=7200*psi
hp=105.*ft
P = 6500*psi
def f(p):
b2,c= p
F1 = sqrt(pi*c)*(K_Icb-K_Ict)/2
F1 = sqrt(pi*c)*(K_Icb-K_Ict)/2 - ( (sigma_2-sigma_1)*sqrt(c**2-b2**2) - (sigma_3-sigma_1)*sqrt(c**2-(hp-b2)**2) )
F2 = sqrt(pi)*(K_Icb+K_Ict)/(2*sqrt(c)) - ( (sigma_2-sigma_1)*arcsin(b2/c) + (sigma_3-sigma_1)*arcsin((hp-b2)/c) - (sigma_2+sigma_3-2*P)*pi/2 )
return (F1,F2)
b2, c = fsolve(f,(9.002,30))
print(b2, c)
This results in:
10.2613616029 25.63857432
Related
I want to calculate the angle between vectors. I thought the sum of 2 vectors should be in the middle of the 2. But calculating the angle with my method gives different results. I guess it has to be with rounding but the result is too different. I tried 2 different approaches. Can you explain me, why? Or am I wrong with my math understanding?
from numpy import (array, dot, arccos, clip, sum)
from numpy.linalg import norm
import spectral
import numpy as np
def calculateAngle(u, v):
c = dot(u, v) / norm(u) / norm(v) # -> cosine of the angle
angle = arccos(clip(c, -1, 1)) # if you really want the angle
return c, angle
def calc_with_numpy():
print("Method 2:")
v = (u1_norm + u2_norm)
c1, angle1 = calculateAngle(u1, v)
c2, angle2 = calculateAngle(u2, v)
print("angle1:", angle1)
print("angle2:", angle2)
def calc_with_spectral():
print("Method 1:")
v = (u1_norm + u2_norm)
img=np.array([v]).reshape((1,1,v.size))
means = np.array([u1, u2])
angles = spectral.spectral_angles(img, means)
print("angle1:", angles[0,0,0])
print("angle2:", angles[0, 0, 1])
u1 = array([1.0,2.0], dtype="float64")
u1_norm = u1 / sum(u1)
u2 = array([3.0,2.0], dtype="float64")
u2_norm = u2 / sum(u2)
calc_with_spectral()
calc_with_numpy()
My results:
Method 1:
angle1: 0.25518239062081866
angle2: 0.2639637236257044
Method 2:
angle1: 0.2551823906208191
angle2: 0.2639637236257044
You are wrong here
u1_norm = u1 / sum(u1)
u2_norm = u2 / sum(u2)
To get normalized (unit length) vector, you need to divide it's components by vector length, not by component sum (like you perform right job inside calculateAngle)
u1_norm = u1 / np.linalg.norm(u1)
You've normalised wrong. Instead, do
u1_norm = u1 / np.sqrt(np.sum(u1**2))
u2_norm = u2 / np.sqrt(np.sum(u2**2))
I now get
>>> calc_with_numpy()
angle1: 0.2595730571232615
angle2: 0.2595730571232615
>>> norm(u1) == np.sqrt(np.sum(u1**2))
True
>>> norm(u2) == np.sqrt(np.sum(u2**2))
True
I don't know what spectral is, my python distribution doesn't have it as a module.
just extchange the following code
u1_norm = u1 / sum(u1)
u2_norm = u2 / sum(u2)
by
u1_norm = u1 / len(u1)
u2_norm = u2 / len(u2)
This function can be used to compute the angle (in degrees) with the x axis of a 2d vector [x, y]:
from math import atan2, pi
def angle(vec):
return atan2(*reversed(vec)) * 180 / pi
Now if you want to compute the angle between two vectors, you can use this function:
def angle_between_vecs(vec1, vec2):
return abs(angle(vec1) - angle(vec2))
You may also want to compute the sum of two vectors:
def sum_vecs(vec1, vec2):
return [vec1[0] + vec2[0], vec1[1]+ vec2[1]]
Now notice that angle(sum_vecs(vec1, vec2)) is not necessarily equal to angle_between_vecs(vec1, vec2). Look at this graphical example that follows (vec3 is the sum of vec1 and vec2). As you can see, the sum is not exactly cutting the angle in two parts.
This code can be clearly optimised by using for example NumPy, but this is just an example to show you that your assumption that the angle of the sum should be between the two vectors is wrong!
Here is the question and what I got so far, but I do not know how I should go for the next to get C1.
Solve the IVP (1/x + 2y^2x)dx + (2yx^2 - cos(y))dy = 0, y(1) = pi. Give an implicit solution
from sympy import *
x = symbols('x')
y = Function('y')
deq = diff(y(x),x) + (1/x + 2*y(x)**2*x)/(2*y(x)*x**2 - cos(y(x)))
ysoln = dsolve(deq, y(x))
The following should work:
from sympy import *
x = symbols('x')
y = Function('y')
deq = diff(y(x),x)*(2*y(x)*x**2 - cos(y(x))) + (1/x + 2*y(x)**2*x)
# this leads to an error
# ysoln = dsolve(deq, y(x), ics={y(0): pi})
# so we do it our own way
ysoln = dsolve(deq, y(x))
C1 = solve(ysoln.subs(x, 1).subs(y(1), pi), 'C1')[0]
ysoln = ysoln.subs('C1', C1)
print(ysoln)
# Eq(x**2*y(x)**2 + log(x) - sin(y(x)), pi**2)
My version couldn't solve the equation in the form that you have so I had to restructure deq a bit. It probably just a little problem with all the division.
Note that this is likely will not work for every ODE. It worked now because one can solve for the unique solution of C1 given the initial conditions. Also, in the future, SymPy might not use C1 as the name of the arbitrary constant and functions such as .subs('C1', C1) will not work in that case.
As an interactive session though, the above method will work just fine.
I want to evaluate the value of phi(+oo)
where phi(xi) is the solution of ODE
Eq(Derivative(phi(xi), (xi, 2)), (-K + xi**2)*phi(xi))
and K is a known real variable.
By dsolve, I got the solution:
Eq(phi(xi), -K*xi**5*r(3)/20 + C2*(K**2*xi**4/24 - K*xi**2/2 + xi**4/12 + 1) + C1*xi*(xi**4/20 + 1) + O(xi**6))
with an unknown function r() in the first term on the right-hand side.
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import sympy
from sympy import I, pi, oo
sympy.init_printing()
def apply_ics(sol, ics, x, known_params):
"""
Apply the initial conditions (ics), given as a dictionary on
the form ics = {y(0): y0, y(x).diff(x).subs(x, 0): yp0, ...},
to the solution of the ODE with independent variable x.
The undetermined integration constants C1, C2, ... are extracted
from the free symbols of the ODE solution, excluding symbols in
the known_params list.
"""
free_params = sol.free_symbols - set(known_params)
eqs = [(sol.lhs.diff(x, n) - sol.rhs.diff(x, n)).subs(x, 0).subs(ics)
for n in range(len(ics))]
sol_params = sympy.solve(eqs, free_params)
return sol.subs(sol_params)
K = sympy.Symbol('K', positive = True)
xi = sympy.Symbol('xi',real = True)
phi = sympy.Function('phi')
ode = sympy.Eq( phi(xi).diff(xi, 2), (xi**2-K)*phi(xi))
ode_sol = sympy.dsolve(ode)
ics = { phi(0):1, phi(xi).diff(xi).subs(xi,0): 0}
phi_xi_sol = apply_ics(ode_sol, ics, xi, [K])
Where ode_sol is the solution, phi_xi_sol is the solution after initial conditions are applied.
Since r() is undefined in NumPy I can't evaluate the results by
for g in [0.9, 0.95, 1, 1.05, 1.2]:
phi_xi = sympy.lambdify(xi, phi_xi_sol.rhs.subs({K:g}), 'numpy')
Does anyone know what this function r() mean and how should I deal with it?
As visible in the form of the result, the solver falls back to a power series solution (instead of searching the solution in terms of parabolic cylinder functions as WolframAlpha does).
So let's set phi(xi)=sum a[k]*xi^k leading to the coefficient equations (using a[k]=0 for k<0)
(k+2)(k+1)a[k+2] = -K*a[k] + a[k-2]
a[0] = C2
a[1] = C1
a[2] = -K/2*C2
a[3] = -K/6*C1
a[4] = (K^2/2 + 1)/12*C2
a[5] = (K^2/6 + 1)/20*C1
Inserting that the the power series solution should have been
C2*(1-K/2*xi**2+(K**2/24+1/12)*xi**4) + C1*xi*(1-K/6*xi**2+(K/120+1/20)*xi**4) + O(xi**6)
Comparing with the sympy solution, all terms containing both C1 and K are missing, especially the missing degree 3 term is not explainable. It seems that the solution process was prematurely ended, or some equation transformation was not correctly reversed.
Please note that the ODE solver routines in sympy are experimental and rudimentary. Also, the power series solution gives only valid information for small values of xi, there is no way to derive any exact value for the limit at +oo.
The sol_params is a list containing a single dictionary. Passing that dictionary instead of the list gives the solution phi_xi_sol without the r(3):
Eq(rho(s), (-K*s**2/2 + s**2*xi**2/2 + 1)*(-6*rho(s) + 6*C2*s - C2*K*s**3 + O(s**5))/
(3*(K*s**2 - 2)) + C2*(-K*s**3/6 + s**3*xi**2/6 + s) + O(s**5))
I am looking to reproduce results from a research article.
I am at a point, where I have to find the maximum value of the following equation (w), and corresponding independent variable value (k). k is my only variable.
from sympy import *
import numpy as np
import math
rho_l = 1352;
rho_g= 1.225;
sigma = 0.029;
nu = 0.02;
Q = rho_g/ rho_l;
u = 99.67;
h = 1.6e-5; # Half sheet thickness
k = Symbol('k', real=True)
t = tanh(k*h);
w1 = -2 * nu * k**2 * t ;
w2 = 4* nu**2 * k**4 * t**2;
w3 = - Q**2 * u**2 * k**2;
w4 = -(t + Q)
w5 = (- Q* u**2 * k**2 + (sigma*k**3/ rho_l));
w6 = -w4;
w = ( w1 + sqrt(w2 + w3 + w4*w5))/w6;
I was able to solve this using Sympy - diff & solve functions, only when I give t = 1 or a any constant.
Do anyone have suggestions on finding the maximum value of this function? Numerically also works - however, I am not sure about the initial guess value. Good thing is I only have one independent variable.
Edit:
As per the answers given here regarding gradient descent, and also plotting and seeing the maximum value. I literally copied the code lines, that include plotting and I got a different plot.
Any thoughts on why this is happening? I am using Python 3.7
There are a bunch of ways to do this. scipy in particular has a bunch of optimization algorithms. I'm going to use gradient descent (or, perhaps more appropriately, gradient ascent) and autograd because it might be fun.
First, let's import autograd and turn your function into a callable function.
import autograd.numpy as anp
from autograd import grad
import matplotlib.pyplot as plt
def w(k):
rho_l = 1352;
rho_g= 1.225;
sigma = 0.029;
nu = 0.02;
Q = rho_g/ rho_l;
u = 99.67;
h = 1.6e-5; # Half sheet thickness
t = anp.tanh(k*h);
w1 = -2 * nu * k**2 * t ;
w2 = 4* nu**2 * k**4 * t**2;
w3 = - Q**2 * u**2 * k**2;
w4 = -(t + Q)
w5 = (- Q* u**2 * k**2 + (sigma*k**3/ rho_l));
w6 = -w4;
w = ( w1 + anp.sqrt(w2 + w3 + w4*w5))/w6;
return w
Now, we can use autograd to compute the gradient of w with respect to k. You can add some logic to ensure that the procedure will terminate once we meet some tolerance threshold.
dwdk = grad(w)
#Do gradient descent
k = 10.0 #An initial guess
learning_rate = 1
for i in range(1000):
k+=learning_rate*dwdk(k)
And now, let's plot the result to ensure we found the maximum
K = np.arange(0,1000)
plt.plot(K,w(K))
plt.scatter(k, w(k), color = 'red')
plt.show()
My thought is that the function returning values for w (your blue line) might be a truncated estimate (with a polynomial maybe)? Is the formula for w off by a factor of 10 or so?
Here is another method. It is an implementation of the Metropolis algorithm, a so-called Markov Chain Monte Carlo method. Using the definition of w, it is possible to construct a Markov Chain of w(k) called wlist. The tail of this chain should be the maximum of w, and we can recover k that got it by storing the k values in a list called kvalues.
import math
import random
klist = [1.0]
wlist = [w(1.0)] # initialize the chain
# you can tune the value passed to `range`
for _ in range(5000):
k = random.gauss(klist[-1], 0.2*klist[-1]) # q
if k <= 0.0: # assuming max has positive `k` arg
continue
w_hat = w(k)
if w_hat > wlist[-1]:
klist.append(k)
wlist.append(w_hat)
else:
u = random.random()
try:
alpha = math.exp(-w_hat) / math.exp(-wlist[-1])
except ZeroDivisionError:
alpha = 1.0
if u >= alpha:
klist.append(k)
wlist.append(w_hat)
else:
klist.append(klist[-1])
wlist.append(wlist[-1])
wlist[-10:], klist[-10:]
Which should return approximately (my seed is not set) something like this:
([8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532,
8679.594992731532],
[416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436,
416.22335719432436])
I do not expect that there is an analytical solution to this problem. There is a theory of pfaffian functions for which it is possible to provide certificate that there are no roots for given range. See https://en.m.wikipedia.org/wiki/Pfaffian_function.
However this heavy artilery.
If you are not sure about initial guess try to compute the function for a large number of random points (e.g.million) and select the best one as starting point. This approach works really well for low dimensional, differentiable problems
This code only works for solving the differential equation v_equation if v(t) isn't squared. When I squared it it returned the error PolynomialDivisionFailed. Is there another way of doing this with Sympy or should I find a different python package for doing these sorts of calculations.
from sympy import *
from matplotlib import pyplot as plt
import numpy as np
m = float(raw_input('Mass:\n> '))
g = 9.8
k = float(raw_input('Drag Coefficient:\n> '))
f1 = g * m
t = Symbol('t')
v = Function('v')
v_equation = dsolve(f1 - k * (v(t) ** 2) - m * Derivative(v(t)), 0)
C1 = Symbol('C1')
C1_ic = solve(v_equation.rhs.subs({t:0}),C1)[0]
v_equation = v_equation.subs({C1:C1_ic})
func = lambdify(t, v_equation.rhs,'numpy')
From my experience with symbolic math packages, I would not recommend performing (symbolic) calculations using floating point constants. It is better to define equations using symbolic constants, perform calculations as far as possible, and then substitute with numerical values.
With this approach, Sympy can provide a solution for this D.E.
First, define symbolic constants. To aid calculations, note that we can provide additional information about these constants (e.g., real, positive, e.t.c)
import sympy as sp
t = sp.symbols('t', real = True)
g, k, m = sp.symbols('g, k, m', real = True, positive = True)
v = sp.Function('v')
The symbolic solution for the DE can be obtained as follows
f1 = g * m
eq = f1 - k * (v(t) ** 2) - m * sp.Derivative(v(t))
sol = sp.dsolve(eq,v(t)).simplify()
The solution sol will be a function of k, m, g, and a constant C1. In general, there will be two, complex C1 values corresponding to the initial condition. However, both values of C1 result in the same (real-valued) solution when substituted in sol.
Note that if you don't need a symbolic solution, a numerical ODE solver, such as Scipy's odeint, may be used. The code would be the following (for an initial condition 0):
from scipy.integrate import odeint
def fun(v, t, m, k, g):
return (g*m - k*v**2)/m
tn = np.linspace(0, 10, 101)
soln = odeint(fun, 0, tn, args=(1000, 0.2, 9.8))
soln is an array of samples v(t) corresponding to the tn elements