Let (0,0) and (Xo,Yo) be two points on a Cartesian plane. We want to determine the parabolic curve, Y = AX^2 + BX + C, which passes from these two points and has a given arc length equal to S. Obviously, S > sqrt(Xo^2 + Yo^2). As the curve must pass from (0,0), it should be C=0. Hence, the curve equation reduces to: Y = AX^2 + BX. How can I determine {A,B} knowing {Xo,Yo,S}? There are two solutions, I want the one with A>0.
I have an analytical solution (complex) that gives S for a given set of {A,B,Xo,Yo}, though here the problem is inverted... I can proceed by solving numerically a complex system of equations... but perhaps there is a numerical routine out there that does exactly this?
Any useful Python library? Other ideas?
Thanks a lot :-)
Note that the arc length (line integral) of the quadratic a*x0^2 + b*x0 is given by the integral of sqrt(1 + (2ax + b)^2) from x = 0 to x = x0. On solving the integral, the value of the integral is obtained as 0.5 * (I(u) - I(l)) / a, where u = 2ax0 + b; l = b; and I(t) = 0.5 * (t * sqrt(1 + t^2) + log(t + sqrt(1 + t^2)), the integral of sqrt(1 + t^2).
Since y0 = a * x0^2 + b * x0, b = y0/x0 - a*x0. Substituting the value of b in u and l, u = y0/x0 + a*x0, l = y0/x0 - a*x0. Substituting u and l in the solution of the line integral (arc length), we get the arc length as a function of a:
s(a) = 0.5 * (I(y0/x0 + a*x0) - I(y0/x0 - a*x0)) / a
Now that we have the arc length as a function of a, we simply need to find the value of a for which s(a) = S. This is where my favorite root-finding algorithm, the Newton-Raphson method, comes into play yet again.
The working algorithm for the Newton-Raphson method of finding roots is as follows:
For a function f(x) whose root is to be obtained, if x(i) is the ith guess for the root,
x(i+1) = x(i) - f(x(i)) / f'(x(i))
Where f'(x) is the derivative of f(x). This process is continued till the difference between two consecutive guesses is very small.
In our case, f(a) = s(a) - S and f'(a) = s'(a). By simple application of the chain rule and the quotient rule,
s'(a) = 0.5 * (a*x0 * (I'(u) + I'(l)) + I(l) - I(u)) / (a^2)
Where I'(t) = sqrt(1 + t^2).
The only problem that remains is calculating a good initial guess. Due to the nature of the graph of s(a), the function is an excellent candidate for the Newton-Raphson method, and an initial guess of y0 / x0 converges to the solution in about 5-6 iterations for a tolerance/epsilon of 1e-10.
Once the value of a is found, b is simply y0/x0 - a*x0.
Putting this into code:
def find_coeff(x0, y0, s0):
def dI(t):
return sqrt(1 + t*t)
def I(t):
rt = sqrt(1 + t*t)
return 0.5 * (t * rt + log(t + rt))
def s(a):
u = y0/x0 + a*x0
l = y0/x0 - a*x0
return 0.5 * (I(u) - I(l)) / a
def ds(a):
u = y0/x0 + a*x0
l = y0/x0 - a*x0
return 0.5 * (a*x0 * (dI(u) + dI(l)) + I(l) - I(u)) / (a*a)
N = 1000
EPSILON = 1e-10
guess = y0 / x0
for i in range(N):
dguess = (s(guess) - s0) / ds(guess)
guess -= dguess
if abs(dguess) <= EPSILON:
print("Break:", abs((s(guess) - s0)))
break
print(i+1, ":", guess)
a = guess
b = y0/x0 - a*x0
print(a, b, s(a))
Run the example on CodeSkulptor.
Note that due to the rational approximation of the arc lengths given as input to the function in the examples, the coefficients obtained may ever so slightly differ from the expected values.
Related
Background.
I'm attempting to write a python implementation of this answer over on Math SE. You may find the following background to be useful.
Problem
I have an experimental setup consisting of three (3) receivers, with known locations [xi, yi, zi], and a transmitter with unknown location [x,y,z] emitting a signal at known velocity v. This signal arrives at the receivers at known times ti. The time of emission, t, is unknown.
I wish to find the angle of arrival (i.e. the transmitter's polar coordinates theta and phi), given only this information.
Solution
It is not possible to locate the transmitter exactly with only three (3) receivers, except in a handful of unique cases (there are several great answers across Math SE explaining why this is the case). In general, at least four (and, in practice, >>4) receivers are required to uniquely determine the rectangular coordinates of the transmitter.
The direction to the transmitter, however, may be "reliably" estimated. Letting vi be the vector representing the location of receiver i, ti being the time of signal arrival at receiver i, and n be the vector representing the unit vector pointing in the (approximate) direction of the transmitter, we obtain the following equations:
<n, vj - vi> = v(ti - tj)
(where < > denotes the scalar product)
...for all pairs of indices i,j. Together with |n| = 1, the system has 2 solutions in general, symmetric by reflection in the plane through vi/vj/vk. We may then determine phi and theta by simply writing n in polar coordinates.
Implementation.
I've attempted to write a python implementation of the above solution, using scipy's fsolve.
from dataclasses import dataclass
import scipy.optimize
import random
import math
c = 299792
#dataclass
class Vertexer:
roc: list
def fun(self, var, dat):
(x,y,z) = var
eqn_0 = (x * (self.roc[0][0] - self.roc[1][0])) + (y * (self.roc[0][1] - self.roc[1][1])) + (z * (self.roc[0][2] - self.roc[1][2])) - c * (dat[1] - dat[0])
eqn_1 = (x * (self.roc[0][0] - self.roc[2][0])) + (y * (self.roc[0][1] - self.roc[2][1])) + (z * (self.roc[0][2] - self.roc[2][2])) - c * (dat[2] - dat[0])
eqn_2 = (x * (self.roc[1][0] - self.roc[2][0])) + (y * (self.roc[1][1] - self.roc[2][1])) + (z * (self.roc[1][2] - self.roc[2][2])) - c * (dat[2] - dat[1])
norm = math.sqrt(x**2 + y**2 + z**2) - 1
return [eqn_0, eqn_1, eqn_2, norm]
def find(self, dat):
result = scipy.optimize.fsolve(self.fun, (0,0,0), args=dat)
print('Solution ', result)
# Crude code to simulate a source, receivers at random locations
x0 = random.randrange(0,50); y0 = random.randrange(0,50); z0 = random.randrange(0,50)
x1 = random.randrange(0,50); x2 = random.randrange(0,50); x3 = random.randrange(0,50);
y1 = random.randrange(0,50); y2 = random.randrange(0,50); y3 = random.randrange(0,50);
z1 = random.randrange(0,50); z2 = random.randrange(0,50); z3 = random.randrange(0,50);
t1 = math.sqrt((x0-x1)**2 + (y0-y1)**2 + (z0-z1)**2)/c
t2 = math.sqrt((x0-x2)**2 + (y0-y2)**2 + (z0-z2)**2)/c
t3 = math.sqrt((x0-x3)**2 + (y0-y3)**2 + (z0-z3)**2)/c
print('Actual coordinates ', x0,y0,z0)
myVertexer = Vertexer([[x1,y1,z1], [x2,y2,z2], [x3,y3,z3]])
myVertexer.find([t1,t2,t3])
Unfortunately, I have far more experience solving such problems in C/C++ using GSL, and have limited experience working with scipy and the like. I'm getting the error:
TypeError: fsolve: there is a mismatch between the input and output shape of the 'func' argument 'fun'.Shape should be (3,) but it is (4,).
...which seems to suggest that fsolve expects a square system.
How may I solve this rectangular system? I can't seem to find anything useful in the scipy docs.
If necessary, I'm open to using other (Python) libraries.
As you already mentioned, fsolve expects a system with N variables and N equations, i.e. it finds a root of the function F: R^N -> R^N. Since you have four equations, you simply need to add a fourth variable. Note also that fsolve is a legacy function, and it's recommended to use root instead. Last but not least, note that sqrt(x^2+y^2+z^2) = 1 is equivalent to x^2+y^2+z^2=1 and that the latter is much less susceptible to rounding errors caused by the finite differences when approximating the jacobian of F.
Long story short, your class should look like this:
from scipy.optimize import root
#dataclass
class Vertexer:
roc: list
def fun(self, var, dat):
x,y,z, *_ = var
eqn_0 = (x * (self.roc[0][0] - self.roc[1][0])) + (y * (self.roc[0][1] - self.roc[1][1])) + (z * (self.roc[0][2] - self.roc[1][2])) - c * (dat[1] - dat[0])
eqn_1 = (x * (self.roc[0][0] - self.roc[2][0])) + (y * (self.roc[0][1] - self.roc[2][1])) + (z * (self.roc[0][2] - self.roc[2][2])) - c * (dat[2] - dat[0])
eqn_2 = (x * (self.roc[1][0] - self.roc[2][0])) + (y * (self.roc[1][1] - self.roc[2][1])) + (z * (self.roc[1][2] - self.roc[2][2])) - c * (dat[2] - dat[1])
norm = x**2 + y**2 + z**2 - 1
return [eqn_0, eqn_1, eqn_2, norm]
def find(self, dat):
result = root(self.fun, (0,0,0,0), args=dat)
if result.success:
print('Solution ', result.x[:3])
I made a function in Python which calculates a definite integral according to the Trapezoidal rule:
Trapezoidal rule formula
That's the code:
from math import ceil
def Trapez_rule(f, a, b, n):
'''Calculates an estimation of a definite integral of a function f(x), between the boundries a, b, by dividing the area to n equal areas'''
sum = (f(a) + f(b)) / 2
for i in range(ceil((b * n))):
sum += f(a + i / n)
sum *= (b - a) / n
return sum
The answer it gives is 10 times higher that it should have returned.
I can't find the source of the problem.
Assume:
a=10
b=20
n=5
These lines are the problem:
for i in range(ceil((b * n))):
sum += f(a + i / n)
i go from 0 to 99
when i = 99 then:
f(a + i / n) => f(10 + 99/5) => f(29)
You divide two ints 99/5 => 29 and not 29.8.
But you only want to have it in range from 10 to 20.
You use n false look at the post solution below, so this should work:
def Trapez_rule(f, a, b, n):
h = (b-a) / float(n)
sum = (f(a) + f(b)) / 2.w
for i in range(1,n-1):
sum += f(a + i * h)
sum *= h
return sum
I went ahead and fixed your code, and also renamed the function to fit with the official style guide PEP-8.
def trapezium_rule_integral(f, a, b, n):
'''Calculates an estimate of the definite integral of a function f, between
the boundaries a and b, by dividing the area to n equal areas'''
height = (b - a) / n
x = a
ys = []
while x <= b:
ys.append(f(x))
x += height
estimate = 0.5 * height * ( (ys[0] + ys[-1]) + 2 * (sum(ys[1:-1])) )
return estimate
The program needs to compute define integral with a predetermined
accuracy (eps) with the Trapezoidal Rule and my function needs to return:
1.the approximate value of the integral.
2.the number of iterations.
My code:
from math import *
def f1(x):
return (x ** 2 - 1)**(-0.5)
def f2(x):
return (cos(x)/(x + 1))
def integral(f,a,b,eps):
n = 2
x = a
h = (b - a) / n
sum = 0.5 * (f(a) + f(b))
for i in range(n):
sum = sum + f(a + i * h)
sum_2 = h * sum
k = 0
flag = 1
while flag == 1:
n = n * 2
sum = 0
k = k + 1
x = a
h = (b - a) / n
sum = 0.5 * (f(a) + f(b))
for i in range(n):
sum = sum + f(a + i * h)
sum_new = h * sum
if eps > abs(sum_new - sum_2):
t1 = sum_new
t2 = k
return t1, t2
else:
sum_2 = sum_new
x1 = float(input("First-begin: "))
x2 = float(input("First-end: "))
y1 = float(input("Second-begin: "))
y2 = float(input("Second-end: "))
int_1 = integral(f1,x1,y1,1e-6)
int_2 = integral(f2,x2,y2,1e-6)
print(int_1)
print(int_2)
It doesn't work correct. Help, please!
You implemented the math wrong. The error is in the lines
for i in range(n):
sum = sum + f(a + i * h)
range(n) always starts at 0, so in your first iteration you just add the f(a) term again.
If you replace it with
for i in range(1, n):
sum = sum + f(a + i * h)
it works.
Also, you have a ton of redundant code; you basically coded the core of the integration algorithm twice. Try to follow the DRY-principle.
The trapezoidal rule of integration simply says that an approximation to the integral $\int_a^b f(x) dx$ is (b-a) (f(a)+f(b))/2. The error is proportional to (b-a)^2, so that it is possible to have a better estimate using the composite rule, i.e., subdividing the initial interval in a number of shorter intervals.
Is it possible to use shorter intervals and still reuse the function values previously computed, so minimizing the total number of function evaluation?
Yes, it is possible if we divide each interval in two equal parts, so that at stage 0 we use 1 intervals, at stage 1 2 equal intervals and in general, at stage n, we use 2n equal intervals.
Let's start with a simple problem and see if it possible to generalize the procedure…
a, b = 0, 32
L = b-a = 32
by the trapezoidal rule the initial approximation say I0, is given by
I0 = L * (f0+f1)/2
= L * S0
with S0 = (f0+f1)/2; a pictorial representation of the real axis, the coordinates of the interval extremes and the evaluated functions follows
x0 x1
01234567890123456789012345679012
f0 f1
Next, we divide the original interval in two,
L = L/2
x0 x2 x1
01234567890123456789012345679012
f0 f2 f1
and the new approximation, stage n=1, is obtained using two times the trapezoidal rule and applying a bit of algebra
I1 = L * (f0+f2)/2 + L * (f2+f1)/2
= L * [(f0+f1)/2 + f2]
= L * [S0 + S1]
with S1 = f2
Another subdivision, stage n=2, L = L/2 and
x0 x3 x2 x4 x1
012345678901234567890123456789012
f0 f3 f2 f4 f1
I2 = L * [(f0+f3) + (f3+f2) + (f2+f4) + (f4+f1)] / 2
= L * [(f0+f1)/2 + f2 + (f3+f4)]
= L * [S0+S1+S2]
with S2 = f3 + f4.
It is not difficult, given this picture,
x0 x5 x3 x6 x2 x7 x4 x8 x1
012345678901234567890123456789012
f0 f5 f3 f6 f2 f7 f4 f8 f1
to understand that our next approximation can be computed as follows
L = L/2
S3 = f5+f6+f7+f8
I3 = L*[S0+S1+S2+S3]
Now, we have to understand how to compute a generalization of Sn,
n = 1, … — for us, the pseudocode is
L_n = (b-a)/2**n
list_x_n = list(a + L_n + 2*Ln*j for j=0, …, 2**n-1)
Sn = Sum(f(xj) for each xj in list_x_n)
For n = 3, L = (b-a)/8 = 4, we have from the formula above list_x_n = [4, 12, 20, 28], please check with the picture...
Now we are ready to code our algorithm in Python
def trapaezia(f, a, b, tol):
"returns integ(f, (a,b)), estimated error and number of evaluations"
from math import fsum # controls accumulation of rounding errors in sums
L = b - a
S = (f(a)+f(b))/2
I = L*S
n = 1
while True:
L = L/2
new_points = (a+L+j*L for j in range(0, n+n, 2))
delta_S = fsum(f(x) for x in new_points)
new_S = S + delta_S
new_I = L*new_S
# error is estimated using Richardson extrapolation (REP)
err = (new_I - I) * 4/3
if abs(err) > tol:
n = n+n
S, I = new_S, new_I
else:
# we return a better estimate using again REP
return (4*new_I-I)/3, err, n+n+1
If you are curious about Richardson extrapolation, I recommend this document that deals exactly with the application of REP to the trapezoidal rule quadrature algorithm.
If you are curious about math.fsum, the docs don't say too much but the link to the original implementation that also includes an extended explanation of all the issues involved.
I've written some Python code to do some image processing work, but it takes a huge amount of time to run. I've spent the last few hours trying to optimize it, but I think I've reached the end of my abilities.
Looking at the outputs from the profiler, the function below is taking a large proportion of the overall time of my code. Is there any way that it can be speeded up?
def make_ellipse(x, x0, y, y0, theta, a, b):
c = np.cos(theta)
s = np.sin(theta)
a2 = a**2
b2 = b**2
xnew = x - x0
ynew = y - y0
ellipse = (xnew * c + ynew * s)**2/a2 + (xnew * s - ynew * c)**2/b2 <= 1
return ellipse
To give the context, it is called with x and y as the output from np.meshgrid with a fairly large grid size, and all of the other parameters as simple integer values.
Although that function seems to be taking a lot of the time, there are probably ways that the rest of the code can be speeded up too. I've put the rest of the code at this gist.
Any ideas would be gratefully received. I've tried using numba and autojiting the main functions, but that doesn't help much.
Let's try to optimize make_ellipse in conjunction with its caller.
First, notice that a and b are the same over many calls. Since make_ellipse squares them each time, just have the caller do that instead.
Second, notice that np.cos(np.arctan(theta)) is 1 / np.sqrt(1 + theta**2) which seems slightly faster on my system. A similar trick can be used to compute the sine, either from theta or from cos(theta) (or vice versa).
Third, and less concretely, think about short-circuiting some of the final ellipse formula evaluations. For example, wherever (xnew * c + ynew * s)**2/a2 is greater than 1, the ellipse value must be False. If this happens often, you can "mask" out the second half of the (expensive) calculation of the ellipse at those locations. I haven't planned this thoroughly, but see numpy.ma for some possible leads.
It won't speed up things for all cases, but if your ellipses don't take up the whole image, you should limit your search for points inside the ellipse to its bounding rectangle. I am lazy with the math, so I googled it and reused #JohnZwinck neat cosine of an arctangent trick to come up with this function:
def ellipse_bounding_box(x0, y0, theta, a, b):
x_tan_t = -b * np.tan(theta) / a
if np.isinf(x_tan_t) :
x_cos_t = 0
x_sin_t = np.sign(x_tan_t)
else :
x_cos_t = 1 / np.sqrt(1 + x_tan_t*x_tan_t)
x_sin_t = x_tan_t * x_cos_t
x = x0 + a*x_cos_t*np.cos(theta) - b*x_sin_t*np.sin(theta)
y_tan_t = b / np.tan(theta) / a
if np.isinf(y_tan_t):
y_cos_t = 0
y_sin_t = np.sign(y_tan_t)
else:
y_cos_t = 1 / np.sqrt(1 + y_tan_t*y_tan_t)
y_sin_t = y_tan_t * y_cos_t
y = y0 + b*y_sin_t*np.cos(theta) + a*y_cos_t*np.sin(theta)
return np.sort([-x, x]), np.sort([-y, y])
You can now modify your original function to something like this:
def make_ellipse(x, x0, y, y0, theta, a, b):
c = np.cos(theta)
s = np.sin(theta)
a2 = a**2
b2 = b**2
x_box, y_box = ellipse_bounding_box(x0, y0, theta, a, b)
indices = ((x >= x_box[0]) & (x <= x_box[1]) &
(y >= y_box[0]) & (y <= y_box[1]))
xnew = x[indices] - x0
ynew = y[indices] - y0
ellipse = np.zeros_like(x, dtype=np.bool)
ellipse[indices] = ((xnew * c + ynew * s)**2/a2 +
(xnew * s - ynew * c)**2/b2 <= 1)
return ellipse
Since everything but x and y are integers, you can try to minimize the number of array computations. I imagine most of the time is spent in this statement:
ellipse = (xnew * c + ynew * s)**2/a2 + (xnew * s - ynew * c)**2/b2 <= 1
A simple rewriting like so should reduce the number of array operations:
a = float(a)
b = float(b)
ellipse = (xnew * (c/a) + ynew * (s/a))**2 + (xnew * (s/b) - ynew * (c/b))**2 <= 1
What was 12 array operations is now 10 (plus 4 scalar ops). I'm not sure if numba's jit would have tried this. It might just do all the broadcasting first, then jit the resulting operations. In this case, reordering so common operations are done at once should help.
Furthering along, you can rewrite this again as
ellipse = ((xnew + ynew * (s/c)) * (c/a))**2 + ((xnew * (s/c) - ynew) * (c/b))**2 <= 1
Or
t = numpy.tan(theta)
ellipse = ((xnew + ynew * t) * (b/a))**2 + (xnew * t - ynew)**2 <= (b/c)**2
Replacing one more array operation with a scalar, and eliminating other scalar ops to get 9 array operations and 2 scalar ops.
As always, be aware of what the range of inputs are to avoid rounding errors.
Unfortunately there's no way good way to do a running sum and bail early if either of the two addends is greater than the right hand side of the comparison. That would be an obvious speed-up, but one you'd need cython (or c/c++) to code.
You can speed it up considerably by using Cython. There is a very good documentation on how to do this.
I have the following set of equations, and I want to solve them simultaneously for X and Y. I've been advised that I could use numpy to solve these as a system of linear equations. Is that the best option, or is there a better way?
a = (((f * X) + (f2 * X3 )) / (1 + (f * X) + (f2 * X3 ))) * i
b = ((f2 * X3 ) / (1 + (f * X) + (f2 * X3))) * i
c = ((f * X) / (1 + (j * X) + (k * Y))) * i
d = ((k * Y) / (1 + (j * X) + (k * Y))) * i
f = 0.0001
i = 0.001
j = 0.0001
k = 0.001
e = 0 = X + a + b + c
g = 0.0001 = Y + d
h = i - a
As noted by Joe, this is actually a system of nonlinear equations. You are going to need more firepower than numpy alone provides.
Solution of nonlinear equations is tricky, and the typical approach is to define an objective function
F(z) = sum( e[n]^2, n=1...13 )
where z is a vector containing a value for each of your 13 variables a,b,c,d,e,f,g,h,i,X,Y and e[n] is the amount by which each of your 13 equations is violated. For example
e[3] = (d - ((k * Y) / (1 + (j * X) + (k * Y))) * i )
Once you have that objective function, then you can apply a nonlinear solver to try to find a z for which F(z)=0. That of course corresponds to a solution to your equations.
Commonly used solvers include:
The Solver in Microsoft Excel
The python library scipy.optimize
Fitting routines in the Gnu Scientific Library
Matlab's optimization toolbox
Note that all of them will work far better if you first alter your set of equations to eliminate as many variables as practical before trying to run the solver (e.g. by substituting for k wherever it is found). The reduced dimensionality makes a big difference.