Extrapolate in log scale on y axis - matlab

x = [0,1,2,3,4,5,6,7] y = [0.07, 0.05, 0.03, 0.02, 0.01, 0.005, 0.002, 0.0007]
I want to find what x is when y= 0.000001 and I tried below but it gives me a wrong value.
10^(interp1(log10(y),x,10^-6, 'linear','extrap'))
Also, would linear extrapolation be possible if I only had two points like so,
x = [6,7] y = [0.002, 0.0007]

interp1's linear-extrap function simply extends the last (or first) segment in the piecewise linear fit made from the points. It doesn't actually create a least-squares fit. This is very evident in the image below:
How did I get this image? I fixed the following problems with your code:
You are interpolating log10(y) vs x.
So the third argument for interp1 needs to be log10(new_y). For new_y = 10^-6, you actually need to pass -6.
The call to interp1() will give you new_x. You're raising 10 to the result of interp1, which is wrong.
x = [0,1,2,3,4,5,6,7];
y = [0.07, 0.05, 0.03, 0.02, 0.01, 0.005, 0.002, 0.0007]
logy = log10(y);
plot(logy, x, '-x');
new_y = 10^-6;
new_x = interp1(logy, x, log10(new_y), 'linear', 'extrap')
plot(log10([new_y, y(end)]), [new_x, x(end)], '--r');
plot(log10(new_y), new_x, 'or');
xlabel('log10(y)'); ylabel('x');
The short answer to your second question is yes!.
Longer answer: replace x and y in my code above and see if it works(spoiler: it does).
Note: I ran this code in Octave Online because I don't have a local MATLAB installation. Shouldn't make a difference to the answer though.

Related

Calculating the curvature of a closed curve ( or polygon) in matlab

Consider the following set of points
x = [1.34, 0.92, 0.68, 0.25, -0.06, -0.34, -0.49, -0.72, -0.79, -0.94, -1.35, -0.35, 0.54, 0.68, 0.84, 1.20, 1.23, 1.32, 1.34];
y = [0.30, 0.43, 0.90, 1.40, 1.13, 1.08, 1.14, 1.23, 0.52, 0.21, -0.20, -0.73, -0.73, -0.82, -0.71, -0.76, -0.46, -0.13, 0.30];
That give a closed curve (or polygon):
figure(1)
hold on
plot(x,y,'k');
scatter(x,y,'r');
xlim([-2 2]);
ylim([-2 2]);
axis equal
I wish to calculate the curvature (as accurate as possible) at the points on the curve.
what I have until now is a simple calculation of the tangent vector (1st derivate) and then the curvature (2nd derivate):
dsx = diff(x);
dsy = diff(y);
ds = sqrt(dsx.^2+dsy.^2);
Tx = dsx./ds;
Ty = dsy./ds;
ds2 = 0.5*(ds(1:end-1)+ds(2:end));
Hx = diff(Tx)./ds2;
Hy = diff(Ty)./ds2;
But I get a very inaccurate curvature:
figure(1)
quiver(x(1:end-2),y(1:end-2),Hx,Hy,'b','autoscalefactor',1.2);
xlim([-2 2]); ylim([-2 2]);
axis equal
This is suppose to be a simple calculation, however it does not work, please advice: how can I find the curvature in the simplest approximation and have a reasonable accuracy in direction and magnitude?
The curvature computation is correct, it's the plotting that is off. Note that diff computes the difference between subsequent elements, yielding a vector with one fewer element. It estimates the derivative in between pairs of samples. If you repeat this, you'll get the second derivative at samples, but not at the first or last sample (you have 2 fewer elements now).
You did notice this, because you are plotting a curvature at all but one vertex.
So all you need to do is replicate a point after the first derivative (I'm adding the last point to the beginning, so the elements are in the same order as in the input array). The indexing statement Tx([end,1:end]) does just that.
In the code below, I'm plotting also the normals (Ty,-Tx) in black.
x = [1.34, 0.92, 0.68, 0.25, -0.06, -0.34, -0.49, -0.72, -0.79, -0.94, -1.35, -0.35, 0.54, 0.68, 0.84, 1.20, 1.23, 1.32, 1.34];
y = [0.30, 0.43, 0.90, 1.40, 1.13, 1.08, 1.14, 1.23, 0.52, 0.21, -0.20, -0.73, -0.73, -0.82, -0.71, -0.76,-0.46, -0.13, 0.30];
% First derivative
dsx = diff(x);
dsy = diff(y);
ds = sqrt(dsx.^2+dsy.^2);
Tx = dsx./ds;
Ty = dsy./ds;
% Second derivative & curvature
ds2 = 0.5*(ds([end,1:end-1])+ds);
Hx = diff(Tx([end,1:end]))./ds2;
Hy = diff(Ty([end,1:end]))./ds2;
% Plot
clf
hold on
plot(x,y,'ro-');
x = x(1:end-1);
y = y(1:end-1); % remove repeated point
quiver(x+dsx/2,y+dsy/2,Ty,-Tx,'k','autoscalefactor',0.3);
quiver(x,y,Hx,Hy,'b','autoscalefactor',1.2);
set(gca,'xlim',[-2 2],'ylim',[-1.5 2]);
axis equal
Strictly speaking, the question doesn't make sense.
A polygon has no curvature at vertices and zero curvature along the edges.
You need to be more specific about the quantity you want to evaluate, for instance by explaining the purpose.

Volume of 3d shape using numerical integration with scipy

I have written a function for computing volume of intersection of a cube and a half-space and now I'm writing tests for it.
I've tried computing the volume numerically like this:
integral = scipy.integrate.tplquad(lambda z, y, x: int(Vector(x, y, z).dot(normal) < distance),
-0.5, 0.5,
lambda x: -0.5, lambda x: 0.5,
lambda x, y: -0.5, lambda x, y: 0.5,
epsabs=1e-5,
epsrel=1e-5)
... basically I integrate over the whole cube and each point gets value 1 or 0 based on if it is inside the half space.
This gets very slow (more than several seconds per invocation) and keeps giving me warnings like
scipy.integrate.quadpack.IntegrationWarning: The integral is probably divergent, or slowly convergent
Is there a better way to calculate this volume?
Integration of characteristic function is mathematically correct, but not practical. That is because most integration schemes are designed to integrate polynomials to some degree exactly, and in consequence all "relatively smooth" functions reasonably well. Characteristic functions, however, are everything but smooth. Polynomial-style integration will get you nowhere.
A much better-suited approach is to build a discretized version of the domain first, and then simply sum up the volumes of the little tetrahedra.
Discretization in 3D can be done, for example, with pygalmesh (a project of mine interfacing CGAL). The below code discretizes the cut-off cube to
You can increase the precision by decreasing max_cell_circumradius and/or max_edge_size_at_feature_edges, but meshing will take longer then. Moreover, you could specify "feature edges" to resolve the intersection edges exactly. This would give you the exactly correct result even with the coarsest cell size.
import pygalmesh
import numpy
c = pygalmesh.Cuboid([0, 0, 0], [1, 1, 1])
h = pygalmesh.HalfSpace([1.0, 2.0, 3.0], 4.0, 10.0)
u = pygalmesh.Intersection([c, h])
mesh = pygalmesh.generate_mesh(
u, max_cell_circumradius=3.0e-2, max_edge_size_at_feature_edges=1.0e-2
)
def compute_tet_volumes(vertices, tets):
cell_coords = vertices[tets]
a = cell_coords[:, 1, :] - cell_coords[:, 0, :]
b = cell_coords[:, 2, :] - cell_coords[:, 0, :]
c = cell_coords[:, 3, :] - cell_coords[:, 0, :]
# omega = <a, b x c>
omega = numpy.einsum("ij,ij->i", a, numpy.cross(b, c))
# https://en.wikipedia.org/wiki/Tetrahedron#Volume
return abs(omega) / 6.0
vol = numpy.sum(compute_tet_volumes(mesh.points, mesh.get_cells_type("tetra")))
print(f"{vol:.8e}")
8.04956436e-01
Integration
Integration of a discontinuous function is problematic, especially in multiple dimension. Some preliminary work, reducing the problem to an integral of a continuous function, is needed. Here I work out the height (top-bottom) as a function of x and y, and use dblquad for that: it returns in 36.2 ms.
I express the plane equations as a*x + b*y + c*z = distance. Some care is needed with the sign of c, as the plane could be a part of the top or of the bottom.
from scipy.integrate import dblquad
distance = 0.1
a, b, c = 3, -4, 2 # normal
zmin, zmax = -0.5, 0.5 # cube bounds
# preprocessing: make sure that c > 0
# by rearranging coordinates, and flipping the signs of all if needed
height = lambda y, x: min(zmax, max(zmin, (distance-a*x-b*y)/c)) - zmin
integral = dblquad(height, -0.5, 0.5,
lambda x: -0.5, lambda x: 0.5,
epsabs=1e-5, epsrel=1e-5)
Monte Carlo methods
Picking sample points at random (Monte Carlo method) avoids the issues with discontinuity: the accuracy is about the same for discontinuous as for continuous functions, the error decreases at the rate 1/sqrt(N) where N is the number of sample points.
The polytope package uses it internally. With it, a computation could go as
import numpy as np
import polytope as pc
a, b, c = 3, 4, -5 # normal vector
distance = 0.1
A = np.concatenate((np.eye(3), -np.eye(3), [[a, b, c]]), axis=0)
b = np.array(6*[0.5] + [distance])
p = pc.Polytope(A, b)
print(p.volume)
Here A and b encode the halfspaces as Ax<=b: the first fix rows are for faces of the cube, the last is for the plane.
To have more control over precision, either implement Monte-Carlo method yourself (easy) or use mcint package (about as easy).
Polytope volume: a task for linear algebra, not for integrators
You want to compute the volume of a polytope, a convex body formed by intersecting halfspaces. This ought to have an algebraic solution. SciPy has HalfspaceIntersection class for these but so far (1.0.0) does not implement finding the volume of such an object. If you could find the vertices of the polytope, then the ConvexHull class could be used to compute the volume. But as is, it seems that SciPy spatial module is no help. Maybe in a future version of SciPy...
If we assume that boundary of the half-space is given by $\{(x, y, z) \mid ax + by + cz + d = 0 \}$ with $c \not= 0$, and that the half-space of interest is that below the plane (in the $z$-direction), then your integral is given by
scipy.integrate.tplquad(lambda z, y, x: 1,
-0.5, 0.5,
lambda x: -0.5, lambda x: 0.5,
lambda x, y: -0.5, lambda x, y: max(-0.5, min(0.5, -(b*y+a*x+d)/c)))
Since at least one of $a$, $b$, and $c$ must be non-zero, the case $c = 0$ can be handled by changing coordinates.

Why is MATLAB calculation of standard deviation different than a hand calculation?

Matlab:
>> std( [3 2 2 3] )
ans =
0.5774
Layman's interpretation of standard deviation per Google:
Mean of {3,2,2,3} = 2.5
Deviation from mean for each value = {0.5, 0.5, 0.5, 0.5}
Square of deviation from mean = {0.25, 0.25, 0.25, 0.25}
Mean of the squares = 0.25
Square root of 0.25 = 0.5
Therefore Standard Deviation of {3,2,2,3} = 0.5
What did I muck up? I was foolishly expecting those two numbers to agree.
Matlab std computes the corrected standard deviation. From help std:
std normalizes Y by (N-1), where N is the sample size. This is the
sqrt of an unbiased estimator of the variance of the population from
which X is drawn, as long as X consists of independent, identically
distributed samples.
So you have
Square of deviation from mean = {0.25, 0.25, 0.25, 0.25}
Then you don't compute the actual root mean of the deviations, but sqrt(sum([0.25 0.25 0.25 0.25])/3). Generally, sum(square of deviation)/(N-1) for a vector of length N.
Update: as Leonid Beschastny pointed out, you can get matlab to calculate the uncorrected standard deviation. Again, from help std:
Y = std(X,1) normalizes by N and produces the square root of the second
moment of the sample about its mean. std(X,0) is the same as std(X).

Limit cycle in Matlab for 2nd order autonomous system

I wish to create a limit cycle in Matlab. A limit cycle looks something like this:
I have no idea how to do it though, I've never done anything like this in Matlab.
The equations to describe the limit cycle are the following:
x_1d=x_2
x_2d=-x_1+x_2-2*(x_1+2*x_2)x_2^2
It is to be centered around the equilibrium which is (0,0)
Can any of you help me?
If you use the partial derivatives of your function to make a vector field, you can then use streamlines to visualize the cycle that you are describing.
For example, the function f = x^2+y^2
Gives me partial derivatives dx = 2x, dy=2y For the visualization, I sample from the partial derivatives over a grid.
[x,y] = meshgrid(0:0.1:1,0:0.1:1);
dx = 2*x;
dy = 2*y;
To visualize the vector field, I use quiver;
figure;
quiver(x, y, dx, dy);
Using streamline, I can visualize the path a particle injected into the vector field would take. In my example, I inject the particle at (0.1, 0.1)
streamline(x,y, dx, dy, 0.1, 0.1);
This produces the following visualization
In your case, you can omit the quiver step to remove the hedgehog arrows at every grid point.
Here's another example that shows the particle converging to an orbit.
Edit: Your function specifically.
So as knedlsepp points out, the function you are interested in is a bit ambiguously stated. In Matlab, * represents the matrix product while .* represents the element-wise multiplication between matrices. Similarly, '^2' represents MM for a matrix M, while .^2 represents taking the element-wise power.
So,
[x_1,x_2] = meshgrid(-4:0.1:4,-4:0.1:4);
dx_1 = x_2;
dx_2 = -x_1+x_2-2*(x_1+2*x_2)*(x_2)^2;
figure; streamline(x_1,x_2, dx_1, dx_2, 0:0.1:4, 0:0.1:4);
Looks like
This function will not show convergence because it doesn't converge.
knedlsepp suggests that the function you are actually interested in is
dx_1 = -1 * x_2;
dx_2 = -1 * -x_1+x_2-2*(x_1+2*x_2).*(x_2).^2;
His post has a nice description of the rest.
This post shows the code to produce the integral lines of your vector field defined by:
dx/dt = y
dy/dt = -x+y-2*(x+2*y)*y^2.
It is important to properly vectorize this function. (i.e. Introducing dots at all the important places)
dxdt = #(x,y) y;
dydt = #(x,y) -x+y-2*(x+2*y).*y.^2;
[X,Y] = meshgrid(linspace(-4,4,100));
[sx,sy] = meshgrid(linspace(-3,3,20));
streamline(stream2(X, Y, ... % Points
dxdt(X,Y), dydt(X,Y),... % Derivatives
sx, sy)); % Starting points
axis equal tight
To get a picture more similar to yours, change the grid size and starting points:
[X,Y] = meshgrid(linspace(-1,1,100));
[sx,sy] = meshgrid(linspace(0,0.75,20),0.2);

"Frequency" shift in discrete FFT in MATLAB

(Disclaimer: I thought about posting this on math.statsexchange, but found similar questions there that were moved to SO, so here I am)
The context:
I'm using fft/ifft to determine probability distributions for sums of random variables.
So e.g. I'm having two uniform probability distributions - in the simplest case two uniform distributions on the interval [0,1].
So to get the probability distribution for the sum of two random variables sampled from these two distributions, one can calculate the product of the fourier-transformed of each probabilty density.
Doing the inverse fft on this product, you get back the probability density for the sum.
An example:
function usumdist_example()
x = linspace(-1, 2, 1e5);
dx = diff(x(1:2));
NFFT = 2^nextpow2(numel(x));
% take two uniform distributions on [0,0.5]
intervals = [0, 0.5;
0, 0.5];
figure();
hold all;
for i=1:size(intervals,1)
% construct the prob. dens. function
P_x = x >= intervals(i,1) & x <= intervals(i,2);
plot(x, P_x);
% for each pdf, get the characteristic function fft(pdf,NFFT)
% and form the product of all char. functions in Y
if i==1
Y = fft(P_x,NFFT) / NFFT;
else
Y = Y .* fft(P_x,NFFT) / NFFT;
end
end
y = ifft(Y, NFFT);
x_plot = x(1) + (0:dx:(NFFT-1)*dx);
plot(x_plot, y / max(y), '.');
end
My issue is, the shape of the resulting prob. dens. function is perfect.
However, the x-axis does not fit to the x I create in the beginning, but is shifted.
In the example, the peak is at 1.5, while it should be 0.5.
The shift changes if I e.g. add a third random variable or if I modify the range of x.
But I can't get figure how.
I'm afraid it might have to do with the fact that I'm having negative x values, while fourier transforms usually work in a time/frequency domain, where frequencies < 0 don't make sense.
I'm aware I could find e.g. the peak and shift it to its proper place, but seems nasty and error prone...
Glad about any ideas!
The problem is that your x origin is -1, not 0. You expect the center of the triangular pdf to be at .5, because that's twice the value of the center of the uniform pdf. However, the correct reasoning is: the center of the uniform pdf is 1.25 above your minimum x, and you get the center of the triangle at 2*1.25 = 2.5 above the minimum x (that is, at 1.5).
In other words: although your original x axis is (-1, 2), the convolution (or the FFT) behave as if it were (0, 3). In fact, the FFT knows nothing about your x axis; it only uses the y samples. Since your uniform is zero for the first samples, that zero interval of width 1 is amplified to twice its width when you do the convolution (or the FFT). I suggest drawing the convolution on paper to see this (draw original signal, reflected signal about y axis, displace the latter and see when both begin to overlap). So you need a correction in the x_plot line to compensate for this increased width of the zero interval: use
x_plot = 2*x(1) + (0:dx:(NFFT-1)*dx);
and then plot(x_plot, y / max(y), '.') will give the correct graph: