Find minimum consecutives in an optimvar
Show older comments
I try to "find" the minimum consecutives 0 or 1 in an integer optimvar.
Following a "normal logical" array, I would search for it as follows:
A = [0 0 0 1 1 0 0 1 0 1 1 1 0 0 0].';
% Find the position of the 1s
index = find(A);
% Calculate the difference between the zeros and find the indexes at wich a new block starts.
% A new Block starts at the first "index" and where the difference is bigger than 1
iStart = find([1; diff(index) >1]);
% The end of the block is always the index before the start of a new block
iEnd = [iStart(2:end)-1; size(index,1)];
% Calculate the length of the Blocks
onLength = iEnd-iStart+1;
offLength = ([index(iStart); length(A)+1] - [0; index(iEnd(1:end))] -1);
And I wanted to use this to "find" / define the minimum of a optimvar. But unfortunately this does not work, which is why I set the maximum like this:
% Define variables
prob = optimproblem;
n_max_off = 3;
N = 90;
ds_on = optimvar('ds_on', N, 'LowerBound',0, 'UpperBound', 1, 'Type','integer');
prob.Constraints.ds_max_off = optimconstr(N-n_max_off);
% The sum of the maximum +1 must be more the 1
% For more than the maximum, at least one element must be true.
for i = 1:N-n_max_off-1
prob.Constraints.ds_max_off(i) = sum(ds_on(i:i+n_max_off+1)) >= 1;
end
% Compressed code without loop
index = (0:n_max_off) + (1:N-n_max_off).';
prob.Constraints.ds_max_off(1:N-n_max_off) = sum(ds_on(index(1:N-n_max_off,:)),2) >= 1;
But to define the minimum I do not know how I can do it.
In other words:
I want to define a minimum and maximum length of zeros and ones which are my boundary conditions / constraints.
The maximum is not a problem, but to define the minimum length is my problem. So minimum and maximum length are my constraints.
My objective is someting different I left for readability.
Accepted Answer
More Answers (1)
It's going to be a nonlinear integer objective function, which means the solver it's going to choose is ga. You should save yourself the hassle and just implement the optimization with ga() directly.
15 Comments
Julian
on 17 Aug 2023
x = ga(fun,nvars,A,b,Aeq,beq,lb,ub,nonlcon,intcon)
% Assuming A = x:
function [c,ceq] = nonlcon(x)
A = x;
n_max_off = ...;
n_min_off = ...;
% Find the position of the 1s
index = find(A);
% Calculate the difference between the zeros and find the indexes at wich a new block starts.
% A new Block starts at the first "index" and where the difference is bigger than 1
iStart = find([1; diff(index) >1]);
% The end of the block is always the index before the start of a new block
iEnd = [iStart(2:end)-1; size(index,1)];
% Calculate the length of the Blocks
onLength = iEnd-iStart+1;
offLength = ([index(iStart); length(A)+1] - [0; index(iEnd(1:end))] -1);
c(1) = max(offLength) - n_max_off;
c(2) = -min(offLength) + n_min_off;
ceq = [];
end
If you want to stick to problem-based formulation of your problem, use "fcn2optimexpr".
[c,~] = fcn2optimexpr(@nonlcon,x);
prob.Constraints.ds_max_off = c <= 0;
Matt J
on 17 Aug 2023
You could also use this FEX download,
>> A = [0 0 0 1 1 0 0 1 0 1 1 1 0 0 0];
>> [~,~,runlengths]=groupLims(groupTrue(logical(A)),1)
runlengths =
2 1 3
>> min(runlengths)
ans =
1
Julian
on 17 Aug 2023
I gave you the problem-based command:
n_min_on = ...;
n_max_off = ...;
[c,~] = fcn2optimexpr(@nonlcon,x,n_min_on,n_max_off);
prob.Constraints.ds_min_on_max_off = c <= 0;
together with the function
function [c,ceq] = nonlcon(x,n_min_on,n_max_off)
A = x;
% Find the position of the 1s
index = find(A);
% Calculate the difference between the zeros and find the indexes at wich a new block starts.
% A new Block starts at the first "index" and where the difference is bigger than 1
iStart = find([1; diff(index) >1]);
% The end of the block is always the index before the start of a new block
iEnd = [iStart(2:end)-1; size(index,1)];
% Calculate the length of the Blocks
onLength = iEnd-iStart+1;
offLength = ([index(iStart); length(A)+1] - [0; index(iEnd(1:end))] -1);
c(1) = max(offLength) - n_max_off;
c(2) = -min(onLength) + n_min_on;
ceq = [];
end
x has to be defined before as a vector of integer optimvars bounded by 0 and 1.
Bruno Luong
on 17 Aug 2023
Edited: Bruno Luong
on 17 Aug 2023
As previously noted, using fcn2optimexpr you can convert any function to an expression. So in problem based optimization there is no reason for limiting to the built-in supported expression functions.
prob = optimproblem;
N = 10;
A = optimvar('A', N, 'LowerBound',0, 'UpperBound', 1, 'Type','integer');
minseqlgtexpr = fcn2optimexpr(@minseqlgt, A, 'OutputSize',[1,1],'Analysis','off','ReuseEvaluation',false);
prob.Constraints.mincst = minseqlgtexpr >= 1; % adjust the bound
maxseqlgtexpr = fcn2optimexpr(@maxseqlgt, A, 'OutputSize',[1,1],'Analysis','off','ReuseEvaluation',false);
prob.Constraints.maxcst = maxseqlgtexpr <= 6; % adjust the bound
% setup other stuffs and solve prob
function lgt = seqlgt(B)
lgt = diff(find([true; diff(B(:)~=0); true]));
end
function minlgt = minseqlgt(A)
minlgt = min(seqlgt(A));
end
function maxlgt = maxseqlgt(A)
maxlgt = max(seqlgt(A));
end
Example:
n_max_on = 5;
n_min_off = 3;
N = 10;
prob = optimproblem;
ds_on = optimvar('ds_on', N, 'LowerBound',0, 'UpperBound', 1, 'Type','integer');
[c,~] = fcn2optimexpr(@nonlcon,ds_on,n_max_on,n_min_off);
prob.Constraints.ds_max_on_min_off = c <= 0;
prob.Objective = -sum(ds_on);
opts = optimoptions("ga",Display="none");
x0.ds_on = [1 1 1 1 1 0 0 0 1 1];
show(prob)
[sol,fval,exitflag] = solve(prob,x0,Options=opts)
[c,~] = nonlcon(sol.ds_on,n_max_on,n_min_off)
sol.ds_on.'
function [c,ceq] = nonlcon(A,n_max_on,n_min_off)
% Find the position of the 1s
index = find(A);
if isempty(index)
c(1) = Inf;
c(2) = Inf;
ceq = [];
else
% Calculate the difference between the zeros and find the indexes at wich a new block starts.
% A new Block starts at the first "index" and where the difference is bigger than 1
iStart = find([1; diff(index) >1]);
% The end of the block is always the index before the start of a new block
iEnd = [iStart(2:end)-1; size(index,1)];
% Calculate the length of the Blocks
onLength = iEnd-iStart+1;
offLength = ([index(iStart); length(A)+1] - [0; index(iEnd(1:end))] -1);
c(1) = max(onLength) - n_max_on;
c(2) = -min(offLength) + n_min_off;
ceq = [];
end
end
IMO, there is no real reason to be using problem-based optimization, unless there are complicated linear constraints that you haven't yet mentioned. If everything is nonlinear and you don't have multiple optimvar vectors, there is no benefit to continuing with the problem-based framework.
Torsten
on 28 Aug 2023
From MATLAB documentation:
x = ga(fun,nvars,A,b,Aeq,beq,lb,ub,nonlcon,intcon) or x = ga(fun,nvars,A,b,Aeq,beq,lb,ub,nonlcon,intcon,options) requires that the variables listed in intcon take integer values.
Note
When there are integer constraints, ga does not accept nonlinear equality constraints, only nonlinear inequality constraints.
Torsten
on 28 Aug 2023
At least it's necessary to either remove integer variables or equality constraints from your code.
Categories
Find more on Linear Least Squares in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!