Solver stops before obtaining a solution in the MATLAB optimization toolbox - matlab

I use fmincon function of MATLAB optimization toolbox for a minimization problem. The basic part of the code is as follows:
clc;
clear;
load('coord_points.mat');
load('coord_centroids.mat');
numberOfPoints=200;
numberOfCentroids=40;
Q=5;
tau=0.1;
d = zeros(numberOfPoints,numberOfCentroids);
for j=1:numberOfCentroids
centroid = coord_centroids(j,:);
for i=1:numberOfPoints
point = coord_points(i,:);
d(i,j)=norm(centroid-point);
end
end
sectorization = optimproblem;
x = optimvar('x',numberOfPoints,numberOfCentroids,'LowerBound',0,'UpperBound',1);
y = optimvar('y',1,'LowerBound',0,'UpperBound',1);
qjExpression=optimexpr(numberOfCentroids);
for j=1:numberOfCentroids
qjExpression(j) = sum(x(:,j));
end
qExpression = mean(qjExpression);
objExpression = y;
%%%
sectorization.Objective = objExpression;
cons1 = optimconstr(numberOfPoints,1);
for i=1:numberOfPoints
cons1(i) = sum(x(i,:)) == 1;
end
cons2 = optimconstr(numberOfCentroids,1);
for j=1:numberOfCentroids
cons2(j) = sum(x(:,j)) >= 1;
end
cons3 = optimconstr(numberOfCentroids,1);
for j=1:numberOfCentroids
cons3(j) = sum(x(:,j)) <= Q*(1+tau);
end
cons4 = optimconstr(1);
varianceOfQj = optimexpr(1);
for j=1:numberOfCentroids
varianceOfQj = varianceOfQj + (qjExpression(j)-qExpression)^2;
end
varianceOfQj=varianceOfQj/numberOfCentroids;
cons4 = varianceOfQj - y <= 0;
sectorization.Constraints.cons1=cons1;
sectorization.Constraints.cons2=cons2;
sectorization.Constraints.cons3=cons3;
sectorization.Constraints.cons4=cons4;
%load('sol.mat');
sol.x=zeros(numberOfPoints,numberOfCentroids);
sol.y=1;
problem = prob2struct(sectorization,sol);
options = optimoptions(#fmincon,'MaxFunctionEvaluations',5000,'Algorithm','active-set','Display','iter')
[x,fval] = fmincon(problem);
But I get the following result:
Solver stopped prematurely.
fmincon stopped because it exceeded the function evaluation limit, options.MaxFunctionEvaluations = 3.000000e+03.
However, as seen in the code, in the options section, max function evaluations is made equal to 5000.
Even, while the program is running, at first outputs as follows are appeared:
*options =
fmincon options:
Options used by current Algorithm ('active-set'):
(Other available algorithms: 'interior-point', 'sqp', 'sqp-legacy', 'trust-region-reflective')
Set properties:
Algorithm: 'active-set'
Display: 'iter'
MaxFunctionEvaluations: 5000
Default properties:
CheckGradients: 0
ConstraintTolerance: 1.0000e-06
FiniteDifferenceStepSize: 'sqrt(eps)'
FiniteDifferenceType: 'forward'
FunctionTolerance: 1.0000e-06
MaxIterations: 400
OptimalityTolerance: 1.0000e-06
OutputFcn: []
PlotFcn: []
SpecifyConstraintGradient: 0
SpecifyObjectiveGradient: 0
StepTolerance: 1.0000e-06
TypicalX: 'ones(numberOfVariables,1)'
UseParallel: 0
Show options not used by current Algorithm ('active-set')*
What can I do to make the Solver work properly? Why does it stop even though I have changed the setting for the maximum evaluation?
Please give your feedback.

Related

Why do these linear inequality constraints work in Matlab but not Octave?

I have the following script performing a nonlinear optimization (NLP), which works in Matlab and hits MaxFunctionEvaluations after about 5 minutes on my machine:
% Generate sample consumption data (4 weeks)
x = 0:pi/8:21*pi-1e-1; %figure; plot(x, 120+5*sin(0.2*x).*exp(-2e-2*x) + 10*exp(-x))
y = 120 + 5*sin(0.2*x).*exp(-2e-2*x) + 10*exp(-x);
consumptionPerWeek = (y + [0; 11; -30; 4.5]).'; % in 168x4 format
consumptionPerHour = reshape(consumptionPerWeek, [], 1);
hoursPerWeek = 168;
hoursTotal = numel(consumptionPerHour);
daysTotal = hoursTotal/24;
weeksTotal = ceil(daysTotal/7);
%% Perform some simple calculations
q_M_mean = mean(consumptionPerHour);
dvsScalingPerWeek = mean(consumptionPerWeek)/q_M_mean;
%% Assumptions about reactor, hard-coded
V_liq = 5701.0; % m^3, main reactor; from other script
initialValue = 4.9298; % kg/m^3; from other script
substrates_FM_year = [676.5362; 451.0241];
total_DVS_year = [179.9586; 20.8867];
mean_DVS_conc = 178.1238; %kg/m^3
% Product yields (m^3 per ton DVS)
Y_M = 420;
Y_N = 389;
%% Test DVS model
DVS_hour = sum(total_DVS_year)/hoursTotal; % t/h
k_1 = 0.25; % 1/d
parameters = [k_1; Y_M; Y_N; V_liq];
%% Build reference and initial values for optimization
% Distribute feed according to demand (-24%/+26% around mean)
feedInitialMatrix = DVS_hour*ones(hoursPerWeek, 1)*dvsScalingPerWeek;
% Calculate states with reference feed (improved initials)
feedInitialVector = reshape(feedInitialMatrix, [], 1);
feedInitialVector = feedInitialVector(1:hoursTotal);
resultsRef = reactorModel1(feedInitialVector, initialValue, parameters, ...
mean_DVS_conc);
V_M_PS = 0 + cumsum(resultsRef(:,2)/24 - consumptionPerHour);
neededMStorage0 = max(V_M_PS) - min(V_M_PS);
%% Setup optimization problem (NLP): feed optimization with virtual product storage
% Objective function 1: Standard deviation of theoretical product storage volume
objFun1 = #(feedVector) objFunScalar(feedVector, initialValue, parameters, ...
mean_DVS_conc, consumptionPerHour);
% Bounds (lb <= x <= ub), i.e., decision variables can only range between 0 and 0.9*dailyDvsAmount
upperfeedLimitSlot = 0.90; % Limit DVS feed amount per *slot*
upperfeedLimitDay = 1.80; % Limit DVS feed amount per *day*
upperfeedLimitWeek = 1.37; % Limit DVS feed amount per *week*
lowerBound_nlp = zeros(1, hoursTotal);
upperBound_nlp = upperfeedLimitSlot*24*DVS_hour.*ones(1, hoursTotal);
% Equality Constraint 1: feed amount mean = constant
A_eq1_nlp = ones(1, hoursTotal);
b_eq1_nlp = DVS_hour*hoursTotal;
% Inequality Constraint 1: Limit max. daily amount
A_nlp1 = zeros(daysTotal, hoursTotal);
for dI = 1:daysTotal
A_nlp1(dI, (24*dI)-(24-1):(24*dI)) = 1;
end
b_nlp1 = upperfeedLimitDay*24*DVS_hour*ones(daysTotal, 1);
% Inequality Constraint 2: Limit max. weekly amount
A_nlp2 = zeros(weeksTotal, hoursTotal);
for wIi = 1:weeksTotal
A_nlp2(wIi, (168*wIi)-(168-1):(168*wIi)) = 1;
end
b_nlp2 = upperfeedLimitWeek*168*DVS_hour*ones(weeksTotal, 1);
% Summarize all inequality constraints
A_nlp = [A_nlp1; A_nlp2]; %sparse([A_nlp1; A_nlp2]);
b_nlp = [b_nlp1; b_nlp2]; %sparse([b_nlp1; b_nlp2]);
try
% Solver: fmincon (Matlab Optimization Toolbox) --> SQP-algorithm = best
optionen_GB = optimoptions('fmincon', 'Display', 'iter', 'FunctionTolerance', 1e-5, ...
'StepTolerance', 1e-4, 'MaxIterations', 2*hoursTotal, ...
'MaxFunctionEvaluations', 100*hoursTotal, 'HonorBounds', true, 'Algorithm', 'sqp');
catch
optionen_GB = optimset('Display', 'iter', 'TolFun', 1e-5, 'TolX', 1e-4, ...
'MaxIter', 2*hoursTotal, 'MaxFunEvals', 100*hoursTotal, 'Algorithm', 'sqp');
end
%% Solve gradient-based NLP
tic; [feedOpt, fval] = fmincon(#(feedVector) objFun1(feedVector), ...
feedInitialVector, A_nlp, b_nlp, A_eq1_nlp, b_eq1_nlp, lowerBound_nlp, upperBound_nlp, ...
[], optionen_GB); toc
%% Rerun model and calculate virtual storage volume with optimized input
resultsOpt = reactorModel1(feedOpt, initialValue, parameters, mean_DVS_conc);
q_M_Opt = resultsOpt(:,2)/24;
V_M_PS_opt = 0 + cumsum(q_M_Opt - consumptionPerHour);
neededMStorageOpt = max(V_M_PS_opt) - min(V_M_PS_opt);
sprintf('Needed product storage before optimization: %.2f m^3, \nafterwards: %.2f m^3. Reduction = %.1f %%', ...
neededMStorage0, neededMStorageOpt, (1 - neededMStorageOpt/neededMStorage0)*100)
%% Objective as separate function
function prodStorageStd = objFunScalar(dvs_feed, initialValues, parameters, mean_DVS_conc, ...
MConsumptionPerHour)
resultsAlgb = reactorModel1(dvs_feed(:, 1), initialValues, parameters, mean_DVS_conc);
q_M_prod = resultsAlgb(:,2)/24;
V_M_PS1 = 0 + cumsum(q_M_prod - MConsumptionPerHour);
prodStorageStd = std(V_M_PS1);
end
The external function reads like this:
function resultsArray = reactorModel1(D_feed, initialValue, parameters, D_in)
% Simulate production per hour with algebraic reactor model
% Feed is solved via a for-loop
hoursTotal = length(D_feed);
k_1 = parameters(1);
Y_M = parameters(2);
Y_N = parameters(3);
V_liq = parameters(4);
resultsArray = zeros(hoursTotal, 3);
t = 1/24;
liquid_feed = D_feed/(D_in*1e-3); % m^3/h
initialValue4Model0 = (initialValue*(V_liq - liquid_feed(1))*1e-3 ...
+ D_feed(1))*1e3/V_liq; % kg/m^3
resultsArray(1, 1) = initialValue4Model0*exp(-k_1*t);
% Simple for-loop with feed as vector per hour
for pHour = 2:hoursTotal
initialValue4Model = (resultsArray(pHour-1, 1)*(V_liq - liquid_feed(pHour))*1e-3 ...
+ D_feed(pHour))*1e3/V_liq; % kg/m^3
resultsArray(pHour, 1) = initialValue4Model*exp(-k_1*t);
end
resultsArray(:, 2) = V_liq*Y_M*k_1*resultsArray(:, 1)*1e-3; % m^3/d
resultsArray(:, 3) = V_liq*Y_N*k_1*resultsArray(:, 1)*1e-3; % m^3/d
end
When I execute the very same script in Octave (ver 5.1.0 with optim 1.6.0), I get:
error: linear inequality constraints: wrong dimensions
When in fact, the following line (executed from the command prompt)
sum(A_nlp*feedInitialVector <= b_nlp)
gives 32 on both Octave and Matlab, thus showing that dimensions are correct.
Is this a bug? Or is Octave treating linear (in)equality constraints somehow different than Matlab?
(Also, if you have tips on how to speed up this script, they would come in handy.)
I've debugged this a bit for you to get you started.
First enable debugging on error:
debug_on_error(1)
Then find the installation folder of optim, and have a look at file /private/__linear_constraint_dimensions__.m within.
*(I found this by doing a grep operation for the exact error you were getting, and found the relevant file. There is another one outside the private folder, you may want to look at that too.)
If you look at the lines trigerring the errors, you will notice, e.g. that an error is triggered if rm != o.np, where [rm, cm] = size(f.imc)
Now run your script and let it enter debug mode on error. You will see that:
debug> [rm, cm] = size(f.imc)
rm = 32
cm = 672
debug> o.np
ans = 672
debug> rm != o.np
ans = 1 % I.e. boolean test succeeds and triggers error
I have no idea what these are, presumably r and c reflect rows and columns, but in any case, you will see that it appears you are trying to match rows with columns and vice versa.
In other words, it looks like you may have passed your inputs in a transposed fashion at some point.
In any case, if this isn't exactly what's happening, this should be a decent starting point for you to figure the exact bug out.
I don't know why matlab "works". Maybe there's a bug in your code and matlab works despite it (for better or worse).
Or there might be a bug in optim transposing inputs by accident (or, at least, in a manner that is incompatible to matlab).
If you feel after your debugging adventures that it's a bug in the optim package, feel free to file a bug report :)

What's wrong with my Logistic Regression parameters in MATLAB glmnet?

I am using glmnet in MATLAB 2019a on my Macbook to do logistic regression.
Algorithm:
log(pi/(1-pi))=b0+X*b
pi=P(Y=2|X_i)=1-P(Y=1|X_i)
Code:
Y = [2;1;2;1;2;1;2;1;1;2];
X = [0.1451 0.1176 0.0872 0.0544 0.0197 -0.0164 -0.0533 -0.0907;
0.5096 0.7240 0.9038 1.0515 1.1694 1.2599 1.3253 1.3681;
-0.0593 -0.1683 -0.2738 -0.3754 -0.4730 -0.5660 -0.6543 -0.7376;
-1.0128 -0.9539 -0.9004 -0.8522 -0.8089 -0.7701 -0.7355 -0.7047;
0.7533 0.5640 0.4054 0.2752 0.1709 0.0900 0.0302 -0.0109;
0.2014 0.2595 0.3070 0.3444 0.3724 0.3918 0.4032 0.4074;
0.9174 0.8706 0.8260 0.7834 0.7423 0.7025 0.6636 0.6253;
0.7643 0.6115 0.4789 0.3653 0.2693 0.1897 0.1252 0.0744;
-0.3299 -0.5078 -0.6507 -0.7615 -0.8430 -0.8981 -0.9294 -0.9399;
-0.2141 -0.1472 -0.0818 -0.0179 0.0443 0.1045 0.1626 0.2183];
lambda=0.5;
family='binomial';
options.weights = [];
options.alpha = 1;
options.nlambda = 100;
options.lambda_min = 0;
options.lambda = lambda;
options.standardize = false;%true
options.thresh = 1E-4;
options.dfmax = 0;
options.pmax = 0;
options.exclude = [];
options.penalty_factor = [];
options.maxit = 100;
options.HessianExact = false;
options.type = 'naive';
fit = glmnet(X,Y,family,options);
Outcome:
a0 0
label [1;2]
beta [0;0;0;0;0;0;0;0]
dev 0
nulldev 13.8629
df 0
lambda 0.5000
npasses 1
jerr 0
dim [8,1]
class 'lognet'
No matter how I changed my input and output, the function always returns coefficients with all 0. I picked these options carefully so I am really confused how it comes to this outcome.
Is it because the data I picked are special or the parameters are wrong?
I have just found where the problem is. The data here is small, so lambda=0.5 implements an overly strong penalty to it.
Changing lambda to less than 0.01 solves the problem. Plus, options.pmax = 0 should be deleted, otherwise it asks for all coefficients to be 0.

The Matlab event function doesn't stop the integration

I have a ode system that I solve with Matlab. I want to find the steady state of the system and to do so, I use the event function as described here.
But some times, the solver doesn't stop even if the criterium is achieved.
For example, if you solve the following system with x0 = 10 the solver will stop before 2000 but with x0 = 0.0001 it won't.
The event function (eventfun_t.m)
function [x,isterm,dir] = eventfun_t(t,y)
dy = test_systeme(t,y);
x = norm(dy) - 1e-3;
isterm = 1;
dir = 0; %or -1, doesn't matter
end
The system (test_systeme.m)
function dx = test_systeme(t,x)
v = x./(x+1);
dx = -v;
end
Solve the system
x0 = 10;
eventfonction = #(t,y) eventfun_t(t,y);
optionsode=odeset('Events',eventfonction);
[t x]=ode15s(#(t,x) test_systeme(t,x),[0 2000],x0,optionsode);
I suppose it's because with x0 = 0.0001 norm(dy) is already lower than 1e-3 but in that case, how can I stop the solver without checking by myself ?
The events function checks for sign changes in value. So if value(t=0)<0 and value(0 < t < t_end)<0, then it will never trigger.
A way I've gotten around this is to use conditional statements:
value = 1;
if norm(dy)<1e-3&&t~=0
value = -1;
end
The t~=0 statement allows value to change sign after the first step if it's already less than the theshold.

Why does `minmax` take longer than a consecutive `min` and `max`?

Well basically the question says it all, my intuition tells me that a call to minmax should take less time than calling a min and then a max.
Is there some optimization I prevent Matlab carrying out in the following code?
minmax:
function minmax_vals = minmaxtest()
buffSize = 1000000;
A = rand(128,buffSize);
windowSize = 100;
minmax_vals = zeros(128,buffSize/windowSize*2);
for i=1:(buffSize/windowSize)
minmax_vals(:,(2*i-1):(2*i)) = minmax(A(:,((i-1)*windowSize+1):(i*windowSize)));
end
end
separate min-max:
function minmax_vals = minmaxtest()
buffSize = 1000000;
A = rand(128,buffSize);
windowSize = 100;
minmax_vals = zeros(128,buffSize/windowSize*2);
for i=1:(buffSize/windowSize)
minmax_vals(:,(2*i-1)) = min(A(:,((i-1)*windowSize+1):(i*windowSize)),[],2);
minmax_vals(:,(2*i)) = max(A(:,((i-1)*windowSize+1):(i*windowSize)),[],2);
end
end
Summary
You can see the overhead because minmax isn't completely obfuscated. Simply type
edit minmax
And you will see the function!
It appears that there is a data-type conversion to nntype.data('format',x,'Data');, which will not be the case for min or max and could be costly. This is for use with MATLAB's neural networking (nn) tools as minmax belongs to that toolbox.
In short, min and max are lower-level, compiled functions (hence they are fully obfuscated), which don't require functionality from the nn toolbox.
Benchmark
Here is a slightly more isolated benchmark, without your windowing and using timeit instead of the profiler. I've also included timings for just the data conversion used in minmax! The test gets the min and max of each row in a large matrix, see here the output plot and code below...
It appears that there is a linear relationship between number of rows and time taken (as expected for a linear operator), but the coefficient is much greater for the combined minmax relationship, with the separate operations being approximately 10x quicker. Also you can clearly see that data conversion takes more time that the min then max version alone!
function benchie()
K = zeros(10, 3);
for k = 1:10
n = 2^k;
A = rand(n, 200);
Arow = zeros(1,200);
m = zeros(n,2);
f1 = #()minmaxtest(A,m);
K(k,1) = timeit(f1);
f2 = #()minthenmaxtest(A,m);
K(k,2) = timeit(f2);
f3 = #()dataconversiontest(A, Arow);
K(k,3) = timeit(f3);
end
figure; hold on; plot(2.^(1:10), K(:,1)); plot(2.^(1:10), K(:,2)); plot(2.^(1:10), K(:,3));
end
function minmaxtest(A,m)
for ii = 1:size(A,1)
m(ii, 1:2) = minmax(A(ii,:));
end
end
function dataconversiontest(A, Arow)
for ii = 1:size(A,1)
Arow = nntype.data('format', A(ii,:), 'Data');;
end
end
function minthenmaxtest(A,m)
for ii = 1:size(A,1)
m(ii, 1) = min(A(ii,:));
m(ii, 2) = max(A(ii,:));
end
end

Error with matrix indices in heat flow simulation

When I run this code that I've written to simulate a heat flow model in MATLAB i get an error that says 'Subscript indices must either be real positive integers or logicals.' I think this is probably something to do with my linspace command generating a different type of variable not integers and so it's not working properly but I'm not sure how to amend my script to correct for this.
Cp = 400;
p = 8960;
k = 400;
a = k/(p*Cp);
dt = 0.01;
dx = sqrt(5*a*dt); %% 5 as 1/5 is smaller than 1/4 for stability
T = zeros(20000,10000);
for x = linspace(1,10000,10000);
T(x,:) = 1000;
end
for x = linspace(10001,20000,10000);
T(x,:) = 25;
end
for t = linspace(1,10000,10000);
for x = linspace(1,20000,20000);
T(x,t+1) = T(x,t)+a*dt*((T(x-1,t)-2*T(x,t)+ T(x+1,t))/(dx*dx));
end
end
The line that blows up is:
T(x,t+1) = T(x,t)+a*dt*((T(x-1,t)-2*T(x,t)+ T(x+1,t))/(dx*dx));
Specifically T(x-1,t) triggers the error because x starts as 1, hence x - 1 = 0 and 0 is not a valid index.
On a more general Matlab coding note, I would write x = 1:10000 instead of x = linspace(1,10000,10000), but this is not causing the error. Note that I'm only addressing the Matlab error message. I have no idea whether your overall code works.