How do I set up fmincon correctly for parallel computation? "Supplied objective function must return a scalar value"

15 ビュー (過去 30 日間)
I have an expensive objective function which takes in multiple parameters, while the contraints are constant. It looks something like this:
out1, out2 = obj_func(in1, in2)
in1 has 1000 different cases. For each possible value of in1, I want to minimize out1 by adjusting in2. At the same time, out2 has to be < 1 in all cases. Because obj_func takes a while to run, I want to parallelize the computation, either by calculating several gradients at the same time, or solve the problem for several in1.
Below is my simplified code:
nPoints = length(INVARS(:,1));
odInputNames = {'DISA'; 'ALT'; 'MACH'};
% Number of batches depending on given batch size:
sizeBatch = 20;
nBatch = floor( nPoints/sizeBatch ) + 1; % Last batch contains remaining points
% Define constraints
CONSTR = struct(...
'param_A', '...',...
'param_B', '...',...
'param_C', '...',...
CONSTR_REQ = [ 1800; ...
1.10; ...
1.15; ...
];
for i = 1:nBatch
% Create batchwise input vectors
for iODVar = 1:length(odInputNames)
if i < nBatch
OD.(odInputNames{iODVar}) = INVARS(sizeBatch*(i-1)+1:sizeBatch*i, iODVar);
else
OD.(odInputNames{iODVar}) = INVARS(sizeBatch*(i-1)+1:end, iODVar); % Last batch has fewer points
end
end
% Setup of FMINCON Optimizer
% ---------------------------------------------------------------------
A = [];
b = [];
Aeq = [];
beq = [];
lb = ones(size(OD.DISA)) * 80;
ub = ones(size(OD.DISA)) * 120;
options = optimoptions(@fmincon, ...
'Algorithm', 'active-set',...
'MaxIter', 200,...
'ConstraintTolerance', 1e-8,...
'OptimalityTolerance', 1e-8,...
'Display', 'off',...
'UseParallel', true);
x0 = ones(size(OD.DISA)) * 85;
% Declare constraint results to be shared between obj_fun and con_fun
constr_fields = fieldnames(CONSTR);
con_res = zeros(length(constr_fields), length(OD.DISA));
% Execute Optimizer
[PLA_max, obj_min,exitflag,~,~] = fmincon( @(x)obj_fun(x), x0, A, b, Aeq, beq, lb, ub, @(x)con_fun(), options );
% Objective:
function [obj] = obj_fun( PLA )
OD.PLA = PLA;
[OD.OUT, OD.IN] = expensive_function( DES, OD );
obj = - OD.OUT.param_obj;
for i=1:length(constr_fields)
con_res(i,:) = eval(['OD.' CONSTR.(constr_fields{i})]);
end
end
% Constraints:
function [c,ceq] = con_fun()
exp_CONSTR_REQ = repmat(CONSTR_REQ, 1, length(OD.DISA));
c = (con_res - exp_CONSTR_REQ); % Non-Equality constraint ( !<= 0 )
ceq = [];
end
It works fine when I solve one case at a time, but when I try to parallelize it I got this error. How can I fix it?
Error using fmincon (line 348)
Supplied objective function must return a scalar value.
  2 件のコメント
Torsten
Torsten 2025 年 1 月 22 日
編集済み: Torsten 2025 年 1 月 22 日
Why is lb > ub ?
lb = ones(size(OD.DISA)) * 120;
ub = ones(size(OD.DISA)) * 80;
Why does the error message report something about fminunc ? It is not referenced in your code.
Shao-Yu
Shao-Yu 2025 年 1 月 22 日
Sorry, made a mistake while simplifying the code. Both mistakes have been fixed in the post now.

サインインしてコメントする。

採用された回答

Matt J
Matt J 2025 年 1 月 22 日
編集済み: Matt J 2025 年 1 月 22 日
I want to parallelize the computation, either by calculating several gradients at the same time, or solve the problem for several in1.
The UseParallel option, which you are already invoking, does parallelize the gradient computation. If there is an analytical algorithm for computing the gradient, however, that will often be quicker.
The error message about fmincon returning a scalar should be easy for you to investigate just by calling the objective function on the initial point x0. If obj_fun(x0) gives you a non-scalar value, then you have a bug, and you must trace it.
If you are deliberately having it return a non-scalar, then you have a misconception about how fmincon works. fmincon cannot perform multiple optimizations in a single call.
  25 件のコメント
Matt J
Matt J 2025 年 1 月 24 日
編集済み: Matt J 2025 年 1 月 24 日
Seems like the only options left are making better initial guesses for PLA and approximating the derivatives.
And also Torsten's suggestion of using a parfor loop. That might be the simplest.
So you're saying I should make another function to calculate E(x_i) and E(x_i + δ)
The gradient computations must be done inside your objective functions as described here,
Obviously, you are free to out-source parts of that computation with calls to other functions, if you wish.
Should I just set δ to be something very small, or is there a better way to do it?
You will need to experiment with δ. The CheckGradients fmincon option can help you see if the derivatives you've provided agree with fmincon's own numerical approximation.
Shao-Yu
Shao-Yu 2025 年 1 月 27 日
Ok thanks a lot @Matt J amd @Torsten! I will try out your suggestions.

サインインしてコメントする。

その他の回答 (1 件)

Catalytic
Catalytic 2025 年 1 月 24 日
There are other acceleration strategies that may be superior to parallelization.
I notice in your examples that the in1,in2 pairs change by small amounts. You also appear to believe that the final solutions for PLA will be close to each other, otherwise why initialize the optimization with the same x0? Those being the case, it may be worthwile to go back to the strategy of optimizing one PLA at a time. However, you would use the optimized PLA from the N-th optimization as the x0 for the next optimization. This might cut down on the number of iterations you need to get the solution, and reduce the number of calls to expensive function.
  1 件のコメント
Shao-Yu
Shao-Yu 2025 年 1 月 24 日
The in1, in2 shown here are not the real values. In reality there are more variables and some vary quite a lot. I set x0 to the same only because the final optimized values are hard to predict (in gerneral between 80 and 105), but you're right. I should try to give PLA a better initial guess.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeSurrogate Optimization についてさらに検索

製品


リリース

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by