I'm trying to find the area under a curve in the region both above the x-axis (from y = 0 to +inf) and below the x-axis, (y = 0 to -inf), separately.
As far as I can figure out, I can use
int = trapz(x,y)
to get the area under the curve, but not as 2 separate values.
For example, if we have x = [0,1,1,2] & y = [7,7,-4,-4]
Trapz will work out "3" as the area ((7.*1) + (-4.*1)), but I want the values of the area above the x-axis and below the x-axis as 2 separate numbers, 7 & -4.
Trapz must fit the trapezoids, calculate the area of the trapezoids and then sum over all. I want a way to fit & find the areas of the trapezoids, but then sort them in to +ve & -ve and finds the sum's of the +ve and -ve areas.
The documentation for trapz: https://au.mathworks.com/help/matlab/ref/trapz.html
You just need to be careful about how you handle the segments where a zero-crossing happens. Picchiolu's answer is much simpler to implement, but ignores this case and you will introduce errors with that approach.
The clearest way is probably to just write your own implementation of the trapezium rule and handle each segment accordingly, with particular focus on handling segments which cross y=0.
See the comments for details
% Demo data
x = [1 2 3 6 7 9 12 13 14 16];
y = sin(3*pi*(x/max(x)));
% Trapz with sign change
aPos = 0; % positive area
aNeg = 0; % negative area
% Loop over point pairs
for ii = 2:numel(x)
idx = (ii-1:ii); % two points we care about
dx = diff(x(idx)); % change in x
my = sum(y(idx))/2; % mean y
sgn = sign(y(idx));
if prod(sgn) >= 0 % Both y values are same sign or one/both equals 0
% two y points have same sign, simple trapezium area added to +/-
if max(sgn) > 0 % at least one y is positive
aPos = aPos + (dx * my);
else % 0 or -1 sign
aNeg = aNeg + (dx * my);
end
else % change in sign
pct = 1 - y(ii) / diff(y(idx)); % percentage into interval
if sgn(1) < 0
pct = 1 - pct;
end
aPos = aPos + sum( y(idx)/2 .* pct .* dx .* (y(idx)>0) );
aNeg = aNeg + sum( y(idx)/2 .* (1-pct) .* dx .* (y(idx)<0) );
end
end
% Total area
aTotal = aPos + aNeg;
Plot to visualise the integral. Note that the data points do not include the zero-crossing points, but the integral creates distinct segments in the above loop
As a sense check, using the above gives the same output for aTotal as trapz(x,y).
EDIT: Wolfie's answer handles much better the zero-crossings and it's the way to go.
Here's a possible solution (amended based on Pranav Hosangadi's remark about multiple x-axis crossings):
x = [0,1,1,2]; y = [7,7,-4,-4]
y_pos = y; y_pos(y<0) = 0; % focus on area above x-axis
res1 = trapz(x, y_pos);
y_neg = y; y_neg(y>0) = 0; % focus on area below x-axis
res2 = trapz(x, y_neg);
Related
To find the gain at the point where the root locus intersects a line of constant damping ratio, the rlocfind function can be used, but the user has to manually select a point and Matlab finds the closest point on the root locus to the selection. Is there a way to find the exact point of intersection without having to make the selection manually?
h = tf([2 5 1],[1 2 3]);
rlocus(h)
z = 0.707; sgrid(z,0)
k = rlocfind(h)
Can the value of gain be found without using rlocfind if the correct gain value is greater than 1? For example, for the following transfer function the gain value should be 23.4.
h = tf(1,poly([-2 -4 -6]))
minfun = #(k) (0.75 - tf2dampingratio(h, k))^2;
gain = fminbnd(minfun, 0, 1) % 0.1970
rlocus(h) % Root locus
ylim([-3 3])
z = 0.75; sgrid(z,0)
K = rlocfind(h)
function dampingratio = tf2dampingratio(h, k)
[num, den] = tfdata(h);
poles = roots(den{:} + k * num{:});
dampingratio = cos(pi - angle(poles(1)))
end
I don't know of a MATLAB function that can do that directly. We can, however, write a small function to do the calculation for us. We know that the root locus is the plot of how the poles of a system move when used in negative feedback with some gain (see the MATLAB docs). And the damping ratio is the cosine of the angle the pole makes with the negative real axis (illustration from Wikipedia).
h = tf([2 5 1],[1 2 3]);
minfun = #(k) (0.707 - tf2dampingratio(h, k))^2;
gain = fminbnd(minfun, 0, 1); % 0.1970
function dampingratio = tf2dampingratio(h, k)
[num, den] = tfdata(h);
poles = roots(den{:} + k * num{:});
dampingratio = cos(pi - angle(poles(1)));
end
Given the following:
Time series with x (time [s]) and y (here discharge [m³/s])
Value V1 (same units integrated y), smaller than the integral over all of x. In this case a small volume [m³].
I would like to calculate:
The y value y_V1 such that the integral between the line y = y_V1 and the curve y equals V1.
The following plot shows this, the orange region is V1, I want the circled value on the y axis:
The V1 must be placed around the peak.
I think this must be an iterative process, where also a the fitting criteria (and the degree of exactness) must be set by the user.
Until now, I haven't found a way to start; besides the pure integration.
The idea is to specify an area. The y value left and right of the peak which envelops this area should be calculated.
Edit
This is the result, if the accepted answer is applied.
You can do this by decreasing some y value until your area target is met. See the comments below for details.
% Input data
x = 0:0.01:pi;
y = sin(x);
target = 1; % Target area
yi = max( y ); % Initialise yi to be max possible y
dy = 0.001; % Step change in yi
Ai = 0; % Area each iteration
thresh = 0; % Threshold for stopping loop
while target - Ai > thresh && yi >= min(y)
yi = yi - dy;
ix = y >= yi;
% Approximate integral above the line
Ai = trapz( x(ix), y(ix) - yi );
end
% Plot
figure(1); clf; hold on
plot( x, y );
patch( x(ix), y(ix), [1,0.5,0.5], 'facealpha', 0.5 );
plot( x, ones(size(x))*yi, '--', 'linewidth', 2 )
xlim( [min(x),max(x)] )
Output:
It seems this question has been asked in a few places (including on SO). I recently came across the need for this when visualizing results of a trilateration problem.
In almost every case, the answer directs the inquiry to look at Wolfram for the math but excludes any code. The math really is a great reference, but if I'm asking a question on programming, some code might help as well. (It certainly is also appreciated when answers to a code question avoid pithy comments like "writing the code is trivial").
So how can one visualize the intersection of spheres in MATLAB? I have a simple solution below.
I wrote a small script to do just this. Feel free to make suggestions and edits. It works by checking if the surface of each sphere falls within the volume of all of the other spheres.
For sphere intersection, it's better (but slower) to use a larger number of faces in the sphere() function call. This should give denser results in the visualization. For the sphere-alone visualization, a smaller number (~50) should suffice. See the comments for how to visualize each.
close all
clear
clc
% centers : 3 x N matrix of [X;Y;Z] coordinates
% dist : 1 x N vector of sphere radii
%% Plot spheres (fewer faces)
figure, hold on % One figure to rule them all
[x,y,z] = sphere(50); % 50x50-face sphere
for i = 1 : size(centers,2)
h = surfl(dist(i) * x + centers(1,i), dist(i) * y + centers(2,i), dist(i) * z + centers(3,i));
set(h, 'FaceAlpha', 0.15)
shading interp
end
%% Plot intersection (more faces)
% Create a 1000x1000-face sphere (bigger number = better visualization)
[x,y,z] = sphere(1000);
% Allocate space
xt = zeros([size(x), size(centers,2)]);
yt = zeros([size(y), size(centers,2)]);
zt = zeros([size(z), size(centers,2)]);
xm = zeros([size(x), size(centers,2), size(centers,2)]);
ym = zeros([size(y), size(centers,2), size(centers,2)]);
zm = zeros([size(z), size(centers,2), size(centers,2)]);
% Calculate each sphere
for i = 1 : size(centers, 2)
xt(:,:,i) = dist(i) * x + centers(1,i);
yt(:,:,i) = dist(i) * y + centers(2,i);
zt(:,:,i) = dist(i) * z + centers(3,i);
end
% Determine whether the points of each sphere fall within another sphere
% Returns booleans
for i = 1 : size(centers, 2)
[xm(:,:,:,i), ym(:,:,:,i), zm(:,:,:,i)] = insphere(xt, yt, zt, centers(1,i), centers(2,i), centers(3,i), dist(i)+0.001);
end
% Exclude values of x,y,z that don't fall in every sphere
xmsum = sum(xm,4);
ymsum = sum(ym,4);
zmsum = sum(zm,4);
xt(xmsum < size(centers,2)) = 0;
yt(ymsum < size(centers,2)) = 0;
zt(zmsum < size(centers,2)) = 0;
% Plot intersection
for i = 1 : size(centers,2)
xp = xt(:,:,i);
yp = yt(:,:,i);
zp = zt(:,:,i);
zp(~(xp & yp & zp)) = NaN;
surf(xt(:,:,i), yt(:,:,i), zp, 'EdgeColor', 'none');
end
and here is the insphere function
function [x_new,y_new,z_new] = insphere(x,y,z, x0, y0, z0, r)
x_new = (x - x0).^2 + (y - y0).^2 + (z - z0).^2 <= r^2;
y_new = (x - x0).^2 + (y - y0).^2 + (z - z0).^2 <= r^2;
z_new = (x - x0).^2 + (y - y0).^2 + (z - z0).^2 <= r^2;
end
Sample visualizations
For the 6 spheres used in these examples, it took an average of 1.934 seconds to run the combined visualization on my laptop.
Intersection of 6 spheres:
Actual 6 spheres:
Below, I've combined the two so you can see the intersection in the view of the spheres.
For these examples:
centers =
-0.0065 -0.3383 -0.1738 -0.2513 -0.2268 -0.3115
1.6521 -5.7721 -1.7783 -3.5578 -2.9894 -5.1412
1.2947 -0.2749 0.6781 0.2438 0.4235 -0.1483
dist =
5.8871 2.5280 2.7109 1.6833 1.9164 2.1231
I hope this helps anyone else who may desire to visualize this effect.
I have implemented a script that does constrained optimization for solving the optimal parameters of Support Vector Machines model. I noticed that my script for some reason gives inaccurate results (although very close to the real value). For example the typical situation is that the result of a calculation should be exactly 0, but instead it is something like
-1/18014398509481984 = -5.551115123125783e-17
This situation happens when I multiply matrices with vectors. What makes this also strange is that if I do the multiplications by hand in the command window in Matlab I get exactly 0 result.
Let me give an example: If I take the vectors Aq = [-1 -1 1 1] and x = [12/65 28/65 32/65 8/65]' I get exactly 0 result from their multiplication if I do this in the command window, as you can see in the picture below:
If on the other hand I do this in my function-script I don't get the result being 0 but rather the value -1/18014398509481984.
Here is the part of my script that is responsible for this multiplication (I've added the Aq and x into the script to show the contents of Aq and x as well):
disp('DOT PRODUCT OF ACTIVE SET AND NEW POINT: ')
Aq
x
Aq*x
Here is the result of the code above when run:
As you can see the value isn't exactly 0 even though it really should be. Note that this problem doesn't occur for all possible values of Aq and x. If Aq = [-1 -1 1 1] and x = [4/13 4/13 4/13 4/13] the result is exactly 0 as you can see below:
What is causing this inaccuracy? How can I fix this?
P.S. I didn't include my whole code because it's not very well documented and few hundred lines long, but I will if requested.
Thank you!
UPDATE: new test, by using Ander Biguri's advice:
UPDATE 2: THE CODE
function [weights, alphas, iters] = solveSVM(data, labels, C, e)
% FUNCTION [weights, alphas, iters] = solveSVM(data, labels, C, e)
%
% AUTHOR: jjepsuomi
%
% VERSION: 1.0
%
% DESCRIPTION:
% - This function will attempt to solve the optimal weights for a Support
% Vector Machines (SVM) model using active set method with gradient
% projection.
%
% INPUTS:
% "data" a n-by-m data matrix. The number of rows 'n' corresponds to the
% number of data points and the number of columns 'm' corresponds to the
% number of variables.
% "labels" a 1-by-n row vector of data labels from the set {-1,1}.
% "C" Box costraint upper limit. This will constrain the values of 'alphas'
% to the range 0 <= alphas <= C. If hard-margin SVM model is required set
% C=Inf.
% "e" a real value corresponding to the convergence criterion, that is if
% solution Xi and Xi-1 are within distance 'e' from each other stop the
% learning process, i.e. IF |F(Xi)-F(Xi-1)| < e ==> stop learning process.
%
% OUTPUTS:
% "weights" a vector corresponding to the optimal decision line parameters.
% "alphas" a vector of alpha-values corresponding to the optimal solution
% of the dual optimization problem of SVM.
% "iters" number of iterations until learning stopped.
%
% EXAMPLE USAGE 1:
%
% 'Hard-margin SVM':
%
% data = [0 0;2 2;2 0;3 0];
% labels = [-1 -1 1 1];
% [weights, alphas, iters] = solveSVM(data, labels, Inf, 10^-100)
%
% EXAMPLE USAGE 2:
%
% 'Soft-margin SVM':
%
% data = [0 0;2 2;2 0;3 0];
% labels = [-1 -1 1 1];
% [weights, alphas, iters] = solveSVM(data, labels, 0.8, 10^-100)
% STEP 1: INITIALIZATION OF THE PROBLEM
format long
% Calculate linear kernel matrix
L = kron(labels', labels);
K = data*data';
% Hessian matrix
Qd = L.*K;
% The minimization function
L = #(a) (1/2)*a'*Qd*a - ones(1, length(a))*a;
% Gradient of the minimizable function
gL = #(a) a'*Qd - ones(1, length(a));
% STEP 2: THE LEARNING PROCESS, ACTIVE SET WITH GRADIENT PROJECTION
% Initial feasible solution (required by gradient projection)
x = zeros(length(labels), 1);
iters = 1;
optfound = 0;
while optfound == 0 % criterion met
% Negative of the gradient at initial solution
g = -gL(x);
% Set the active set and projection matrix
Aq = labels; % In plane y^Tx = 0
P = eye(length(x))-Aq'*inv(Aq*Aq')*Aq; % In plane projection
% Values smaller than 'eps' are changed into 0
P(find(abs(P-0) < eps)) = 0;
d = P*g'; % Projection onto plane
if ~isempty(find(x==0 | x==C)) % Constraints active?
acinds = find(x==0 | x==C);
for i = 1:length(acinds)
if (x(acinds(i)) == 0 && d(acinds(i)) < 0) || x(acinds(i)) == C && d(acinds(i)) > 0
% Make the constraint vector
constr = zeros(1,length(x));
constr(acinds(i)) = 1;
Aq = [Aq; constr];
end
end
% Update the projection matrix
P = eye(length(x))-Aq'*inv(Aq*Aq')*Aq; % In plane / box projection
% Values smaller than 'eps' are changed into 0
P(find(abs(P-0) < eps)) = 0;
d = P*g'; % Projection onto plane / border
end
%%%% DISPLAY INFORMATION, THIS PART IS NOT NECESSAY, ONLY FOR DEBUGGING
if Aq*x ~= 0
disp('ACTIVE SET CONSTRAINTS Aq :')
Aq
disp('CURRENT SOLUTION x :')
x
disp('MULTIPLICATION OF Aq and x')
Aq*x
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Values smaller than 'eps' are changed into 0
d(find(abs(d-0) < eps)) = 0;
if ~isempty(find(d~=0)) && rank(P) < length(x) % Line search for optimal lambda
lopt = ((g*d)/(d'*Qd*d));
lmax = inf;
for i = 1:length(x)
if d(i) < 0 && -x(i) ~= 0 && -x(i)/d(i) <= lmax
lmax = -x(i)/d(i);
elseif d(i) > 0 && (C-x(i))/d(i) <= lmax
lmax = (C-x(i))/d(i);
end
end
lambda = max(0, min([lopt, lmax]));
if abs(lambda) < eps
lambda = 0;
end
xo = x;
x = x + lambda*d;
iters = iters + 1;
end
% Check whether search direction is 0-vector or 'e'-criterion met.
if isempty(find(d~=0)) || abs(L(x)-L(xo)) < e
optfound = 1;
end
end
%%% STEP 3: GET THE WEIGHTS
alphas = x;
w = zeros(1, length(data(1,:)));
for i = 1:size(data,1)
w = w + labels(i)*alphas(i)*data(i,:);
end
svinds = find(alphas>0);
svind = svinds(1);
b = 1/labels(svind) - w*data(svind, :)';
%%% STEP 4: OPTIMALITY CHECK, KKT conditions. See KKT-conditions for reference.
weights = [b; w'];
datadim = length(data(1,:));
Q = [zeros(1,datadim+1); zeros(datadim, 1), eye(datadim)];
A = [ones(size(data,1), 1), data];
for i = 1:length(labels)
A(i,:) = A(i,:)*labels(i);
end
LagDuG = Q*weights - A'*alphas;
Ac = A*weights - ones(length(labels),1);
alpA = alphas.*Ac;
LagDuG(any(abs(LagDuG-0) < 10^-14)) = 0;
if ~any(alphas < 0) && all(LagDuG == zeros(datadim+1,1)) && all(abs(Ac) >= 0) && all(abs(alpA) < 10^-6)
disp('Optimal found, Karush-Kuhn-Tucker conditions satisfied.')
else
disp('Optimal not found, Karush-Kuhn-Tucker conditions not satisfied.')
end
% VISUALIZATION FOR 2D-CASE
if size(data, 2) == 2
pinds = find(labels > 0);
ninds = find(labels < 0);
plot(data(pinds, 1), data(pinds, 2), 'o', 'MarkerFaceColor', 'red', 'MarkerEdgeColor', 'black')
hold on
plot(data(ninds, 1), data(ninds, 2), 'o', 'MarkerFaceColor', 'blue', 'MarkerEdgeColor', 'black')
Xb = min(data(:,1))-1;
Xe = max(data(:,1))+1;
Yb = -(b+w(1)*Xb)/w(2);
Ye = -(b+w(1)*Xe)/w(2);
lineh = plot([Xb Xe], [Yb Ye], 'LineWidth', 2);
supvh = plot(data(find(alphas~=0), 1), data(find(alphas~=0), 2), 'g.');
legend([lineh, supvh], 'Decision boundary', 'Support vectors');
hold off
end
NOTE:
If you run the EXAMPLE 1, you should get an output starting with the following:
As you can see, the multiplication between Aq and x don't produce value 0, even though they should. This is not a bad thing in this particular example, but if I have more data points with lots of decimals in them this inaccuracy becomes bigger and bigger problem, because the calculations are not exact. This is bad for example when I'm searching for a new direction vector when I'm moving towards the optimal solution in gradient projection method. The search direction isn't exactly the correct direction, but close to it. This is why I want the exactly correct values...is this possible?
I wonder if the decimals in the data points have something to do with the accuracy of my results. See the picture below:
So the question is: Is this caused by the data or is there something wrong in the optimization procedure...
Do you use format function inside your script? It looks like you used somewhere format rat.
You can always use matlab eps function, that returns precision that is used inside matlab. The absolute value of -1/18014398509481984 is smaller that this, according to my Matlab R2014B:
format long
a = abs(-1/18014398509481984)
b = eps
a < b
This basically means that the result is zero (but matlab stopped calculations because according to eps value, the result was just fine).
Otherwise you can just use format long inside your script before the calculation.
Edit
I see inv function inside your code, try replacing it with \ operator (mldivide). The results from it will be more accurate as it uses Gaussian elimination, without forming the inverse.
The inv documentation states:
In practice, it is seldom necessary to form the explicit inverse of a
matrix. A frequent misuse of inv arises when solving the system of
linear equations Ax = b. One way to solve this is with x = inv(A)*b. A
better way, from both an execution time and numerical accuracy
standpoint, is to use the matrix division operator x = A\b. This
produces the solution using Gaussian elimination, without forming the
inverse.
With the provided code, this is how I tested:
I added a break-point on the following code:
if Aq*x ~= 0
disp('ACTIVE SET CONSTRAINTS Aq :')
Aq
disp('CURRENT SOLUTION x :')
x
disp('MULTIPLICATION OF Aq and x')
Aq*x
end
When the if branch was taken, I typed at console:
K>> format rat; disp(x);
12/65
28/65
32/65
8/65
K>> disp(x == [12/65; 28/65; 32/65; 8/65]);
0
1
0
0
K>> format('long'); disp(max(abs(x - [12/65; 28/65; 32/65; 8/65])));
1.387778780781446e-17
K>> disp(eps(8/65));
1.387778780781446e-17
This suggests that this is a displaying problem: the format rat deliberately uses small integers for expressing the value, on the expense of precision. Apparently, the true value of x(4) is the next one to 8/65 than can be possibly put in double format.
So, this begs the question: are you sure that numeric convergence depends on flipping the least significant bit in a double precision value?
I have a 3D density function q(x,y,z) that I am trying to plot in Matlab 8.3.0.532 (R2014a).
The domain of my function starts at a and ends at b, with uniform spacing ds. I want to plot the density on a ternary surface plot, where each dimension in the plot represents the proportion of x,y,z at a given point. For example, if I have a unit of density on the domain at q(1,1,1) and another unit of density on the domain at q(17,17,17), in both cases there is equal proportions of x,y,z and I will therefore have two units of density on my ternary surface plot at coordinates (1/3,1/3,1/3). I have code that works using ternsurf. The problem is that the number of proportion points grows exponentially fast with the size of the domain. At the moment I can only plot a domain of size 10 (in each dimension) with unit spacing (ds = 1). However, I need a much larger domain than this (size 100 in each dimension) and much smaller than unit spacing (ideally as small as 0.1) - this would lead to 100^3 * (1/0.1)^3 points on the grid, which Matlab just cannot handle. Does anyone have any ideas about how to somehow bin the density function by the 3D proportions to reduce the number of points?
My working code with example:
a = 0; % start of domain
b = 10; % end of domain
ds = 1; % spacing
[x, y, z] = ndgrid((a:ds:b)); % generate 3D independent variables
n = size(x);
q = zeros(n); % generate 3D dependent variable with some norm distributed density
for i = 1:n(1)
for j = 1:n(2)
for k = 1:n(2)
q(i,j,k) = exp(-(((x(i,j,k) - 10)^2 + (y(i,j,k) - 10)^2 + (z(i,j,k) - 10)^2) / 20));
end
end
end
Total = x + y + z; % calculate the total of x,y,z at every point in the domain
x = x ./ Total; % find the proportion of x at every point in the domain
y = y ./ Total; % find the proportion of y at every point in the domain
z = z ./ Total; % find the proportion of z at every point in the domain
x(isnan(x)) = 0; % set coordinate (0,0,0) to 0
y(isnan(y)) = 0; % set coordinate (0,0,0) to 0
z(isnan(z)) = 0; % set coordinate (0,0,0) to 0
xP = reshape(x,[1, numel(x)]); % create a vector of the proportions of x
yP = reshape(y,[1, numel(y)]); % create a vector of the proportions of y
zP = reshape(z,[1, numel(z)]); % create a vector of the proportions of z
q = reshape(q,[1, numel(q)]); % create a vector of the dependent variable q
ternsurf(xP, yP, q); % plot the ternary surface of q against proportions
shading(gca, 'interp');
colorbar
view(2)
I believe you meant n(3) in your innermost loop. Here are a few tips:
1) Loose the loops:
q = exp(- ((x - 10).^2 + (y - 10).^2 + (z - 10).^2) / 20);
2) Loose the reshapes:
xP = x(:); yP = y(:); zP = z(:);
3) Check Total once, instead of doing three checks on x,y,z:
Total = x + y + z; % calculate the total of x,y,z at every point in the domain
Total( abs(Total) < eps ) = 1;
x = x ./ Total; % find the proportion of x at every point in the domain
y = y ./ Total; % find the proportion of y at every point in the domain
z = z ./ Total; % find the proportion of z at every point in the domain
PS: I just recognized your name.. it's Jonathan ;)
Discretization method probably depends on use of your plot, maybe it make sense to clarify your question from this point of view.
Overall, you probably struggling with an "Out of memory" error, a couple of relevant tricks are described here http://www.mathworks.nl/help/matlab/matlab_prog/resolving-out-of-memory-errors.html?s_tid=doc_12b?refresh=true#brh72ex-52 . Of course, they work only up to certain size of arrays.
A more generic solution is too save parts of arrays on hard drive, it makes processing slower but it'll work. E.g., you can define several q functions with the scale-specific ngrids (e.g. ngridOrder0=[0:10:100], ngridOrder10=[1:1:9], ngridOrder11=[11:1:19], etc... ), and write an accessor function which will load/save the relevant grid and q function depending on the part of the plot you're looking.