Constraining other parameters usng fmincon MATLAB? - matlab

When using fmincon I constrain the values of two input parameters to be be in a given range, I then build a design matrix based on these values and use rdivide to calculate the beta parameters (i.e. the weights that provide the desired values for a given design matrix). I want to be able to constrain these beta values too. This is not part of the optimisation per se, but are values calculated from the parameters being optimised. Is there anyway to achieve this using fmincon?
clc
mats = [1/12 3/12 6/12 9/12 1 15/12 18/12 21/12 24/12 30/12 3 4 5 6 7 8 9 10];
mats2=mats;
for ii = 1:size(rates_DL,1)
for jj=1:10
lam1=unifrnd(0,2.5);
lam2=unifrnd(2.5,5.5);
y2=rates_DL(ii,:);
yc_m=rates_DL(ii,:);
f=#(l)NSS_fmin(l,mats,mats2,y2,yc_m);
L0=[lam1; lam2];
lbn=[0 0];
ubn=[30 30];
options=optimset('Algorithm','interior-point','Hessian','lbfgs','MaxFunEvals',10000,'TolFun',1e-16,'MaxIter',10000);
[cn(:,ii,jj),residual(:,ii,jj),exitflag(:,ii,jj),output(:,ii,jj)]=fmincon(#(l)NSS_fmin(l,mats,mats2,y2,yc_m),L0,[],[],[],[],lbn,ubn,[],options);
end
end
This is how I have the problem set up now with the constraints on lam1 and lam2 only.
function [residuals]=NSS_fmin(lambda,mats,mats2,y2,yc_m)
residuals=zeros(size(yc_m));
mats2=mats2';
nObs = size(y2,2);
G = [ones(nObs,1),...
(1-exp(-mats2/lambda(1)))./(mats2/lambda(1)),...
((1-exp(-mats2/lambda(1)))./(mats2/lambda(1)) - exp(-mats2/lambda(1))),...
((1-exp(-mats2/lambda(2)))./(mats2/lambda(2)) - exp(-mats2/lambda(2)))];
%betas = G\data.y2.';
betas = G\y2';
beta = vertcat(betas,lambda);
gam1 = mats/beta(5);
gam2 = mats/beta(6);
aux1 = 1-exp(-gam1);
aux2 = 1-exp(-gam2);
YC = beta(1) + ...
beta(2)*(aux1./gam1) + ...
beta(3)*(aux1./gam1+aux1-1) + ...
beta(4)*(aux2./gam2+aux2-1);
residuals=YC-yc_m;
residuals=sum(residuals.^2);
end
As a work around I could test the values of the betas produced and if a violation occurred multiply residuals by Inf. However ideally I Would like to find a solution where I use the existing functionality within fmincon.

Related

mle memory error with custom negative log-likelihood function

I am trying to use 'mle' with a custom negative log-likelihood function, but I get the following error:
Requested 1200000x1200000 (10728.8GB) array exceeds maximum array size preference (15.6GB). This might cause MATLAB to become unresponsive.
The data I am using is a 1x1200000 binary array (which I had to convert to double), and the function has 10 arguments: one for the data, 3 known paramenters, and 6 to be optimized. I tried setting 'OptimFun' to both 'fminsearch' and 'fmincon'. Also, optimizing the parameters using 'fminsearch' and 'fminunc' instead of 'mle' works fine.
The problem happens in the 'checkFunErrs' functions, inside the 'mlecustom.m' file (call at line 173, actuall error at line 705).
With 'fminunc' I could calculate the optimal parameters, but it does not give me confidence intervals. Is there a way to circumvent this? Or am I doing something wrong?
Thanks for the help.
T_1 = 50000;
T_2 = 100000;
npast = 10000;
start = [0 0 0 0 0 0];
func = #(x, data, cens, freq)loglike(data, [x(1) x(2) x(3) x(4) x(5) x(6)],...
T_1, T_2, npast);
params = mle(data, 'nloglf', func, 'Start', start, 'OptimFun', 'fmincon');
% Computes the negative log likehood
function out = loglike(data, params, T_1, T_2, npast)
size = length(data);
if npast == 0
past = 0;
else
past = zeros(1, size);
past(npast+1:end) = movmean(data(npast:end-1),[npast-1, 0]); % Average number of events in the previous n years
end
lambda = params(1) + ...
(params(2)*cos(2*pi*(1:size)/T_1)) + ...
(params(3)*sin(2*pi*(1:size)/T_1)) + ...
(params(4)*cos(2*pi*(1:size)/T_2)) + ...
(params(5)*sin(2*pi*(1:size)/T_2)) + ...
params(6)*past;
out = sum(log(1+exp(lambda))-data.*lambda);
end
Your issue is line 228 (as of MATLAB R2017b) of the in-built mle function, which happens just before the custom function is called:
data = data(:);
The input variable data is converted to a column array without warning. This is typically done to ensure that all further calculations are robust to the orientation of the input vector.
However, this is causing you issues, because your custom function assumes data is a row vector, specifically this line:
out = sum(log(1+exp(lambda))-data.*lambda);
Due to implicit expansion, when the row vector lambda and the column vector data interact, you get a huge square matrix per your error message.
Adding these two lines to make it explicit that both are column vectors resolves the issue, avoids implicit expansion, and applies the calculation element-wise as you intended.
lambda = lambda(:);
data = data(:);
So your function becomes
function out = loglike(data, params, T_1, T_2, npast)
N = length(data);
if npast == 0
past = 0;
else
past = zeros(1,N);
past(npast+1:end) = movmean(data(npast:end-1),[npast-1, 0]); % Average number of events in the previous n years
end
lambda = params(1) + ...
(params(2)*cos(2*pi*(1:N)/T_1)) + ...
(params(3)*sin(2*pi*(1:N)/T_1)) + ...
(params(4)*cos(2*pi*(1:N)/T_2)) + ...
(params(5)*sin(2*pi*(1:N)/T_2)) + ...
params(6)*past;
lambda = lambda(:);
data = data(:);
out = sum(log(1+exp(lambda))-data.*lambda);
end
An alternative would be to re-write your function so that it uses column vectors, but you create new row vectors with the (1:N) steps and the concatenation within the movmean. The suggested approach is arguably "lazier", but also robust to row or column inputs.
Note also I've changed your variable name from size to N, since size is an in-built function which you should avoid shadowing.

Using MATLAB plots to find linear equation constants

Finding m and c for an equation y = mx + c, with the help of math and plots.
y is data_model_1, x is time.
Avoid other MATLAB functions like fitlm as it defeats the purpose.
I am having trouble finding the constants m and c. I am trying to find both m and c by limiting them to a range (based on smart guess) and I need to deduce the m and c values based on the mean error range. The point where mean error range is closest to 0 should be my m and c values.
load(file)
figure
plot(time,data_model_1,'bo')
hold on
for a = 0.11:0.01:0.13
c = -13:0.1:-10
data_a = a * time + c ;
plot(time,data_a,'r');
end
figure
hold on
for a = 0.11:0.01:0.13
c = -13:0.1:-10
data_a = a * time + c ;
mean_range = mean(abs(data_a - data_model_1));
plot(a,mean_range,'b.')
end
A quick & dirty approach
You can quickly get m and c using fminsearch(). In the first example below, the error function is the sum of squared error (SSE). The second example uses the sum of absolute error. The key here is ensuring the error function is convex.
Note that c = Beta(1) and m = Beta(2).
Reproducible example (MATLAB code[1]):
% Generate some example data
N = 50;
X = 2 + 13*random(makedist('Beta',.7,.8),N,1);
Y = 5 + 1.5.*X + randn(N,1);
% Example 1
SSEh =#(Beta) sum((Y - (Beta(1) + (Beta(2).*X))).^2);
Beta0 = [0.5 0.5]; % Initial Guess
[Beta SSE] = fminsearch(SSEh,Beta0)
% Example 2
SAEh =#(Beta) sum(abs(Y-(Beta(1) + Beta(2).*X)));
[Beta SumAbsErr] = fminsearch(SAEh,Beta0)
This is a quick & dirty approach that can work for many applications.
#Wolfie's comment directs you to the analytical approach to solve a system of linear equations with the \ operator or mldivide(). This is the more correct approach (though it will get a similar answer). One caveat is this approach gets the SSE answer.
[1] Tested with MATLAB R2018a

How to create a second order linear model in MATLAB if I have 5 predictors.?

Basically, I have a dataset with 5 predictors and one target variable. I need to fit a second order linear model in MATLAB. So do I need to create a total of 20 predictor variables and then use fitlm or is there any other approach so that I donot need to create 20 variables?
According to the documentation you can fit a second order model using fitlm by specifying the modelspec argument as 'quadratic'. Here's an example with simulated data.
% generate some random correlated data
mu = [0 0 0 0 0 0];
sigma = [1.6737 1.0183 1.0279 -1.8104 -2.4717 -2.2875; ...
1.0183 2.9619 -0.2512 -1.9997 2.4059 -1.7610; ...
1.0279 -0.2512 2.7031 -0.2611 -3.9707 -0.6580; ...
-1.8104 -1.9997 -0.2611 5.8947 -2.9645 4.1843; ...
-2.4717 2.4059 -3.9707 -2.9645 15.3447 1.6498; ...
-2.2875 -1.7610 -0.6580 4.1843 1.6498 6.0116];
data_train = mvnrnd(mu,sigma,10000);
data_test = mvnrnd(mu,sigma,1000);
% fit second order polynomial
predictors_train = data_train(:,1:5);
target_train = data_train(:,6);
model = fitlm(predictors_train, target_train, 'quadratic');
% test using data from same distribution
predictors_test = data_test(:,1:5);
target_test = data_test(:,6);
target_est = predict(model, predictors_test);
% report root mean-square error
rmse = sqrt(mean((target_est - target_test).^2))

MATLAB: ODE45 time dependent paramters in R14SP3 and interpolation issue

In how-do-i-solve-an-ode-with-time-dependent-parameters-in-matlab
Q: Consider the following ODE with time-dependent parameters:
y'(t) + f(t)y(t) = g(t)
and the given initial condition:
y(0) = 1
This is an example of an ODE with time-dependent terms. Suppose the time-dependent terms are defined only through the set of data points given in two vectors. Which of the MATLAB ODE solvers should I use, and how do I set up this problem?
A: This enhancement has been incorporated in Release 14 Service Pack 3 (R14SP3). For previous product releases, read below for any possible workarounds
My question one is: I use Release 14 Service Pack 3 (R14SP3) indeed. But the answer does not mention how to deal with it. How can I implement time dependent parameters in ODE45?
My question two is: Because I do not know how to implement time dependent parameters in ODE45 in (R14SP3). I plan to use the old method of interpolation in the answer of that page. But why do we need interpolation as we already know the exact form of time-dependent parameters.
My question three is: Jan Simon in the comment points out its flaw of interpolation. I have implemented the method suggested by Jan Simon using "integration per loop". But Jan Simon's method treat the variable as constant inside ODE45. So How to do integration inside the ode45 "h" intervals as well? I know how to do integration in my defined interval, but the ode45 will treat the variable as constant when running its own intervals. For example, doing integration per loop for each timestep:
function Integrationperloop
tResult = [];
xResult = [];
tStep = [0 1 2 3 4 5 6 7 8 9 10 11];
x0 = [1 2 3];
for index = 2:numel(tStep)
% Integrate:
beta = 1 + exp(-0.20*tStep(index - 1))
af = #(t,x) f(t, x, beta);
t = tStep(index-1:index);
[t, x] = ode45(af, t, x0);
% Collect the results:
tResult = cat(1, tResult, t);
xResult = cat(1, xResult, x);
% Final value of x is initial value for next step:
x0 = x(end, :);
end
%This is the derivative
function dx = f(t,x, beta)
dx = [-0.5*x(1)-beta*x(1)*x(3); ...
beta*x(1)*x(3) - x(2); ...
x(2) - x(3)];
However in the code above ode45 will treat the variable as constant when running its own intervals. In the link how-do-i-solve-an-ode-with-time-dependent-parameters-in-matlab, MathWorks Support Team suggest using the enhancement in R14S13 or using interpolation. If using interpolation, when ODE45 is running the time-dependent variable changes using interpolation.

Dice simulation with matlab

I am new on this forum. First of all, I find it very interesting to have such a website were everyone can get help in different domains. Thank you very much.
So I have a problem: I was supposed to resolve the following problem:
Simulate with rand ntrials of rolling a dice.
if rand() in [0, 1/6] then 1 was thrown;
if rand() in (1/6, 2/6] then 2 was thrown
...
if rand() in (5/6, 1] then 6 was thrown.
Generate with hist an histogramm of the results of ntrials.
This is what I did:
ntrials = 100;
X = abs(rand(1,ntrials)*6) + 1;
hist(floo(X))
Now there is a second exercise that I must do:
two dice are thrown and S is the sum of the 2 dice
Compute the probability that S respectively accept one of the value 2,3,4,5.....12.
Write a Matlab function twoTimesDice that the theoritical result through a simulation of the throw of 2 dice like in the first exercise.
That is what I tryed:
function twoTimesDice
x1 = abs(rand(1,11))*6 + 1;
s1 = floor(x1); % probably result of the first dice
x2 = abs(rand(1,11))*6 +1;
s2 = floor(x2) % probably result of de second dice
S = s1 +s2;
hist(S);
end
Can you tell me please if I did it well?
Generating a dice roll between 1 and 6 can be done by randi().
So first, use randi() instead of floor() and abs():
X = randi(6,1,ntrials)
which will give you an array of length ntrials with random integers ranging from 1 to 6. (you need the 1 there or it will return a square matrix of size ntrials by ntrials). randi documentation
In the function my personal preference would be to request the number of trials as input.
Your function then becomes:
function twoTimesDice(ntrials)
s1 = randi(6,1,ntrials); % result of the first dice
s2 = randi(6,1,ntrials); % result of the second dice
S = s1 +s2;
hist(S);
end
For a normalised histogram, you can replace hist(S) by:
numOfBins = 11;
[histFreq, histXout] = hist(S, numOfBins);
figure;
bar(histXout, histFreq/sum(histFreq)*100);
xlabel('Value');ylabel('Percentage');
(As described in this question)
For the first part, I would use floor instead of abs,
X = floor(rand(1, ntrials)*6) + 1;
as it returns the values you are looking for, or as Daniel commented, use
randi(6)
which returns an integer.
Then you can just run
hist(X,6)
For the second part, I believe they are asking for two dice rolls, each being 1-6, and not one 2-12.
x = floor(rand(1)*6) + 1;
The distribution will look different. Roll those twice, add the result, that is your twoTimesDice function.
Roll that ntrials times, then do a histogram of that (as you already do).
I am not sure how random rand() really is though.