Problems with Cubic Hermite polynomial in Matlab - matlab

I am having trouble with this code for a piecewise cubic Hermite polynomial. In my assignment we are not allowed to use pchip. We are supposed to write a function which takes as it's input arguments an integer n and a 3 by n matrix which contains the x values, the values of a function evaluated at each x, and the values of the derivative evaluated at each x; in the first, second and third rows of the matrix respectively.
With this information we are supposed to calculate the coefficients for each Hermite polynomial on each subinterval of the interval given by the first row of the input matrix, then piece it altogether using mkpp.
Unfortunately, my code produces a very poor interpolating polynomial and I am having trouble figuring out why. For example for the function x^2 we have for instance n = 4 and M = [0,1,2,3; 0,1,4,9; 0,1,4,6] and when plugged into my function produces the following graph
I would really appreciate help with this, it's driving me nuts.
Here is my code:
function [ HP ] = HermiteInter( n,M )
%HermiteInter
% This function constructs a peicewise cubic Hermite polynomial.
% The input areguments are an integer n and M a 3 by n matrix, whose
% first row contains the values of x which a given function will be
% evaluated at. The second row contains the values of a function
% evaluated at those
% points and the third row contains the value of the derivative of
% the function evaluated at those points.
% Divided differences using these values are found by calling the
% function HermiteDD. These divided differences are then passed into the
% function mkpp to create the peicewise polynomial.
X = M(1,:);
Y = M(2,:);
Z = M(3,:);
Q = zeros(n-1,4);
for i = 1 : n-1
Q(i,:) = HermiteDD([X(i),X(i),X(i+1),X(i+1)],[Y(i),Y(i+1)],[Z(i),Z(i+1)]);
end
HP = mkpp(X,Q);
end
function [ HDD ] = HermiteDD(X,Y,Z)
%HermiteDD
% This function creates a table of divided differences for
% Hermite polynomials. The input arguments are X, the values at
% which a function was evaluated at, Y the values of the function at
% these points and Z the values of the derivative of the function at
% these points.
DD = zeros(4, 4);
DD(1, 1) = Y(1);
DD(2, 1) = Y(1);
DD(3, 1) = Y(2);
DD(4, 1) = Y(2);
DD(1, 2) = Z(1);
DD(2, 2) = (Y(1)-Y(2))/(X(1)-X(3));
DD(3, 2) = Z(2);
for j = 3 : 4
for i = 1 : (4 - j+1)
DD(i,j) = (DD(i + 1, j - 1) - DD(i, j - 1)) / (X(i + j-1) - X(i));
end
end
HDD = DD(1,:);
end

Related

Interpolation through Newton polynomials

I attempted to solve the problem, and would like a solution to compare to.
The question is:
Write a function that determines the (n-1)th order Newton polynomial and interpolates for a
set of values. The inputs of your function should be: a vector of x values, a corresponding
vector of y values, and a vector of values to interpolate. Your outputs should be the
coefficients of the polynomial (as a vector, [a1 a2 ···an]) and the corresponding function
values for the interpolation. Thoroughly comment your code to show that you
know what you are doing.
My attempt is:
function yint = Newtint(x,y,xx)
n = length(x);
if length(y)~=n, error('x and y must be same length'); end
b = zeros(n,n);
b(:,1) = y(:); % the (:) ensures that y is a column vector.
for j = 2:n
for i = 1:n-j+1
b(i,j) = (b(i+1,j-1)-b(i,j-1))/(x(i+j-1)-x(i));
end
end
xt = 1;
yint = b(1,1);
for j = 1:n-1
xt = xt*(xx-x(j));
yint = yint+b(1,j+1)*xt;
end
% input:
% x = independent variable
% y = dependent variable
% xx = value of independent variable at which
% interpolation is calculated
% output:
% yint = interpolated value of dependent variable
% compute the finite divided differences in the form of a
% difference table

Function differentiation

I have a function:
Where ||x|| is an Euclidean distance.
For given n (number of variables) I want Matlab to differentiate this function and then substitute real numbers into it.
What I truly don't understand how to do is:
1) How let Matlab create all these new n variables for later differentiation?
2) Each variable is a vector of dimension = d, i.e x=[x1, x2, ... xd], so later I want to differentiate function with respect to certain vector elements, for example with respect to x1, how can I do it?
EDIT 1: function should be differentiated at each x_i, where i=1:n
The derivative of a sum, is the sum of the derivatives of each element... so, we need to find the derivative only once (you can do this manually, if it is a simple function like in your toy-example, but we do this the general way, using the Symbolic Math Toolbox):
syms x y z % declaring 3 symolic variables
F = 1/(norm([x,y,z])); % declaring a function
f = diff(F,x) % calculate the derivative with regard to the symbolic variable x
f = -(abs(x)*sign(x))/(abs(x)^2 + abs(y)^2 + abs(z)^2)^(3/2)
you now have different options. You can use subs to evaluate the function f (simply assign numeric values to x,y, and z and call subs(f). Or, you create a (numeric) function handle by using matlabFunction (this is the way, I prefer)
fnc = matlabFunction(f); % convert to matlab function
Then, you just need to sum up the vector that you created (well, you need to sum up the sum of each of the two vector elements...)
% create arbitrary vector
n = 10;
x = rand(n+1,3);
% initialize total sum
SumFnc = 0;
% loop through elements
for i = 1:n
% calculate local sum
s = x(i,:)+x(i+1,:);
% call derivative-function + sum up
SumFnc = SumFnc + fnc(s(1),s(2),s(3));
end

vectorization Matlab - how to vectorize Gauss function ( code ) without loop

i wrote a function to calculate the integral using gaussian quadrature rule. I need to vectorize the last 2 lines, without using a loop. Any ideas?
function Q = gauss5(f,a,b,n)
% Divide the interval into n parts
y = linspace(a,b,n);
% Create function to use arrayfun
% Sum up the result from the arrayfun with function Fun on particular
% intervals, the first argument is array y without the last value, and
% the second argument is the array y without the first value
a = (y(1:end-1));
b = (y(2:end));
% coefficients - some random numbers
c = [1 2 3 4 5];
% nodes - random numbers too
x = [1 1 1 1 1];
% calculate b-a
w = b-a;
% sum up the result
Q = 0;
for i=1:n-1
Q = Q + sum((w(i)*c.*feval(f,((w(i)*x)+a(i)))));
end
end
If your f can get an array as input, you can replace this:
% sum up the result
Q = 0;
for i=1:n-1
Q = Q + sum((w(i)*c.*feval(f,((w(i)*x)+a(i)))));
end
by this:
wxa = bsxfun(#plus,bsxfun(#times,w,x(:)),a);
Qs = bsxfun(#times,w,c(:)).*feval(f,wxa);
Q = sum(Qs(:))
Or, if you have Matlab 2016b or later, just:
Qs = w.*c(:).*feval(f,w.*x(:)+a);
Q1 = sum(Qs(:));

MATLAB Create surface plot of function for all X, Y pairs

I have a custom function called dissmeasure that outputs a scalar from an input vector of frequencies. Another function called music.tone2freq converts integers numbers to frequencies.
My objective is to create a surface plot of dissmeasure for all pairs of x,y integers where X and Y equal [0:1:11].
It should look something like this (this is mesh(X,Y, ones(12,12) ) ):
Following from the mesh docs, what I have tried is doing:
[X,Y] = meshgrid(0:1:12)
Z = dissmeasure(music.tone2freq([X., Y.]))
But I get Error: Expression or statement is incorrect--possibly unbalanced (, {, or [..
Z = dissmeasure(music.tone2freq([X(:), Y(:)]))
But [X(:), Y(:)] doesn't seem to have the correct size. Also my function that accepts a vector returns one scalar for that whole input. What I need is multiple returns.
Note that both dissmeasure(music.tone2freq([X(:), Y(:)])) and dissmeasure(music.tone2freq([X, Y])) work, but the result from dissmeasure is a single scalar number, not a matrix of the result of this function for each x,y pair.
Any help? Thank you
tone2freq.m
function f = tone2freq(T)
% MUSIC.TONE2FREQ converts a musical semitone to a frequency.
% F = MUSIC.TONE2FREQ(T) converts the musical semitones in T to frequencies.
%
% Example
% f = music.tone2freq(0:2); % returns [261.63 277.19 293.67]
%
% See also music.tone2interval, music.tone2note, music.freq2tone.
% Author: E. Johnson
% Copyright 2010 The MathWorks, Inc.
fC4 = 261.625565300599; % Middle C (C4) is 261.63 Hz
f = fC4 .* 2 .^ (T / 12);
dissmeasure.m:
% calculate dissonace
% input param fvec - list of frequencies
% input param amp - list of amplitudes
% output is sum of dissonances of each pair of partials (scalar)
function d = dissmeasure(fvec, amp)
if ~exist('amp','var')
amp = ones(size(fvec));
end
Xstar = 0.24; % place with maximum dissonance
S1 = 0.0207; % to fit frequency dependend curves
S2 = 18.96; % so max. dissonance occures at 1/4 critical bandwidth
C1 = 5;
C2 = -5;
B1 = -3.51; % derived from model of Levelt & Plomp
B2 = -5.75;
N = length(fvec);
[fvec, idx_list] = sort(fvec); % sort partial frequencies ascending
amp = amp(idx_list); % rearrange amplitude values
%amp = loudness(amp);
D = 0;
for i=2:N
Fmin = fvec(1 : N-i+1); % get slice as list of Fmin
S = Xstar./(S1*Fmin+S2); % calc list of s-scalings with list of Fmin
% treat vector as tail and head ...
Fdif = fvec(i:N) - fvec(1:N-i+1); % build element wise difference
a = min(amp(i:N), amp(1:N-i+1)); % select element wise a minimum
Dnew = a .* (C1*exp(B1*S.*Fdif) + C2*exp(B2*S.*Fdif));
D = D + sum(Dnew); % sum up last D and vector elements
end
d=D;
Your function dissmeasure does not support vectorized operations, which means that for inputs of size N the function is evaluated for each element and an output of size N is returned.
Instead your function returns the summarized dissonance.
%assuming you have X and Y already converted
Z=X*0 % initialize Z of same size
for ix = 1:numel(X)
Z(ix)=dissmeasure(X(ix),Y(ix));
end

Using MATLAB to write a function that implements Newton's method in two dimensions

I am trying to write a function that implements Newton's method in two dimensions and whilst I have done this, I have to now adjust my script so that the input parameters of my function must be f(x) in a column vector, the Jacobian matrix of f(x), the initial guess x0 and the tolerance where the function f(x) and its Jacobian matrix are in separate .m files.
As an example of a script I wrote that implements Newton's method, I have:
n=0; %initialize iteration counter
eps=1; %initialize error
x=[1;1]; %set starting value
%Computation loop
while eps>1e-10&n<100
g=[x(1)^2+x(2)^3-1;x(1)^4-x(2)^4+x(1)*x(2)]; %g(x)
eps=abs(g(1))+abs(g(2)); %error
Jg=[2*x(1),3*x(2)^2;4*x(1)^3+x(2),-4*x(2)^3+x(1)]; %Jacobian
y=x-Jg\g; %iterate
x=y; %update x
n=n+1; %counter+1
end
n,x,eps %display end values
So with this script, I had implemented the function and the Jacobian matrix into the actual script and I am struggling to work out how I can actually create a script with the input parameters required.
Thanks!
If you don't mind, I'd like to restructure your code so that it is more dynamic and more user friendly to read.
Let's start with some preliminaries. If you want to make your script truly dynamic, then I would recommend that you use the Symbolic Math Toolbox. This way, you can use MATLAB to tackle derivatives of functions for you. You first need to use the syms command, followed by any variable you want. This tells MATLAB that you are now going to treat this variable as "symbolic" (i.e. not a constant). Let's start with some basics:
syms x;
y = 2*x^2 + 6*x + 3;
dy = diff(y); % Derivative with respect to x. Should give 4*x + 6;
out = subs(y, 3); % The subs command will substitute all x's in y with the value 3
% This should give 2*(3^2) + 6*3 + 3 = 39
Because this is 2D, we're going to need 2D functions... so let's define x and y as variables. The way you call the subs command will be slightly different:
syms x, y; % Two variables now
z = 2*x*y^2 + 6*y + x;
dzx = diff(z, 'x'); % Differentiate with respect to x - Should give 2*y^2 + 1
dzy = diff(z, 'y'); % Differentiate with respect to y - Should give 4*x*y + 6
out = subs(z, {x, y}, [2, 3]); % For z, with variables x,y, substitute x = 2, y = 3
% Should give 56
One more thing... we can place equations into vectors or matrices and use subs to simultaneously substitute all values of x and y into each equation.
syms x, y;
z1 = 3*x + 6*y + 3;
z2 = 3*y + 4*y + 4;
f = [z1; z2];
out = subs(f, {x,y}, [2, 3]); % Produces a 2 x 1 vector with [27; 25]
We can do the same thing for matrices, but for brevity I won't show you how to do that. I will defer to the code and you can see it then.
Now that we have that established, let's tackle your code one piece at a time to truly make this dynamic. Your function requires the initial guess x0, the function f(x) as a column vector, the Jacobian matrix as a 2 x 2 matrix and the tolerance tol.
Before you run your script, you will need to generate your parameters:
syms x y; % Make x,y symbolic
f1 = x^2 + y^3 - 1; % Make your two equations (from your example)
f2 = x^4 - y^4 + x*y;
f = [f1; f2]; % f(x) vector
% Jacobian matrix
J = [diff(f1, 'x') diff(f1, 'y'); diff(f2, 'x') diff(f2, 'y')];
% Initial vector
x0 = [1; 1];
% Tolerance:
tol = 1e-10;
Now, make your script into a function:
% To run in MATLAB, do:
% [n, xout, tol] = Jacobian2D(f, J, x0, tol);
% disp('n = '); disp(n); disp('x = '); disp(xout); disp('tol = '); disp(tol);
function [n, xout, tol] = Jacobian2D(f, J, x0, tol)
% Just to be sure...
syms x, y;
% Initialize error
ep = 1; % Note: eps is a reserved keyword in MATLAB
% Initialize counter
n = 0;
% For the beginning of the loop
% Must transpose into a row vector as this is required by subs
xout = x0';
% Computation loop
while ep > tol && n < 100
g = subs(f, {x,y}, xout); %g(x)
ep = abs(g(1)) + abs(g(2)); %error
Jg = subs(J, {x,y}, xout); %Jacobian
yout = xout - Jg\g; %iterate
xout = yout; %update x
n = n + 1; %counter+1
end
% Transpose and convert back to number representation
xout = double(xout');
I should probably tell you that when you're doing computation using the Symbolic Math Toolbox, the data type of the numbers as you're calculating them are a sym object. You probably want to convert these back into real numbers and so you can use double to cast them back. However, if you leave them in the sym format, it displays your numbers as neat fractions if that's what you're looking for. Cast to double if you want the decimal point representation.
Now when you run this function, it should give you what you're looking for. I have not tested this code, but I'm pretty sure this will work.
Happy to answer any more questions you may have. Hope this helps.
Cheers!