В этом примере показано, как избежать двойного вызова функции при вычислении значений как для цели, так и для ограничений с использованием подхода на основе решателя. Чтобы избежать двойного вызова функции с использованием подхода, основанного на проблемах, см. раздел Цель и ограничения, имеющие общую функцию в последовательном или параллельном, основанном на проблемах.
Как правило, такая функция используется при моделировании. Решатели, такие как fmincon вычислить целевые и нелинейные функции ограничения по отдельности. Этот анализ является расточительным при использовании одного и того же расчета для обоих результатов.
Чтобы избежать потери времени, используйте вложенную функцию для оценки функций цели и ограничения только при необходимости, сохраняя значения трудоемких вычислений. Такой подход позволяет избежать использования глобальных переменных при сохранении промежуточных результатов и совместном использовании их функций цели и ограничения.
Примечание
Из-за пути ga (Global Optimization Toolbox) вызывает нелинейные функции ограничения, метод в этом примере обычно не уменьшает число вызовов целевых функций или функций ограничения.
Шаг 1. Запишите функцию, которая вычисляет цель и ограничения.
Шаг 2. Встраивайте функцию во вложенную функцию, которая сохраняет последние значения.
Например, предположим computeall является дорогостоящей (трудоемкой) функцией, вызываемой как целевой функцией, так и функциями нелинейных ограничений. Предположим, что вы хотите использовать fmincon как ваш оптимизатор.
Записать функцию, которая вычисляет часть функции Розенброка f1 и включает нелинейное ограничение c1 который удерживает решение на диске радиуса 1 вокруг начала координат. Функция Розенброка
(1 − x1) 2,
который имеет уникальное минимальное значение 0 в (1,1). См. раздел Решение ограниченной нелинейной проблемы на основе решателя.
Этот пример не имеет ограничений нелинейного равенства, поэтому ceq1 = []. Добавить pause(1) оператор для моделирования дорогостоящего вычисления.
function [f1,c1,ceq1] = computeall(x) ceq1 = []; c1 = x(1)^2 + x(2)^2 - 1; f1 = 100*(x(2) - x(1)^2)^2 + (1-x(1))^2; pause(1) % Simulate expensive computation end
Сохранить computeall.m в виде файла по пути MATLAB ®.
Предположим, что целевая функция
y = 100 (x2 - x12) 2 + (1 - x1 ) 2
+ 20* (x3 – x42) 2 + 5* (1 – x4) 2.
computeall возвращает первую часть целевой функции. Внедрить вызов в computeall во вложенной функции:
function [x,f,eflag,outpt] = runobjconstr(x0,opts) if nargin == 1 % No options supplied opts = []; end xLast = []; % Last place computeall was called myf = []; % Use for objective at xLast myc = []; % Use for nonlinear inequality constraint myceq = []; % Use for nonlinear equality constraint fun = @objfun; % The objective function, nested below cfun = @constr; % The constraint function, nested below % Call fmincon [x,f,eflag,outpt] = fmincon(fun,x0,[],[],[],[],[],[],cfun,opts); function y = objfun(x) if ~isequal(x,xLast) % Check if computation is necessary [myf,myc,myceq] = computeall(x); xLast = x; end % Now compute objective function y = myf + 20*(x(3) - x(4)^2)^2 + 5*(1 - x(4))^2; end function [c,ceq] = constr(x) if ~isequal(x,xLast) % Check if computation is necessary [myf,myc,myceq] = computeall(x); xLast = x; end % Now compute constraint function c = myc; % In this case, the computation is trivial ceq = myceq; end end
Сохранение вложенной функции в виде файла с именем runobjconstr.m на пути MATLAB.
Запустите функцию, синхронизируя вызов с tic и toc.
opts = optimoptions(@fmincon,'Algorithm','interior-point','Display','off'); x0 = [-1,1,1,2]; tic [x,fval,exitflag,output] = runobjconstr(x0,opts); toc
Elapsed time is 259.364090 seconds.
Сравните время запуска решателя с вложенной функцией и без нее. Для прогона без вложенной функции сохраните myrosen2.m в качестве файла целевой функции и constr.m в качестве ограничения.
function y = myrosen2(x) f1 = computeall(x); % Get first part of objective y = f1 + 20*(x(3) - x(4)^2)^2 + 5*(1 - x(4))^2; end function [c,ceq] = constr(x) [~,c,ceq] = computeall(x); end
Управляемый fmincon, синхронизация вызова с tic и toc.
tic
[x,fval,exitflag,output] = fmincon(@myrosen2,x0,...
[],[],[],[],[],[],@constr,opts);
tocElapsed time is 518.364770 seconds.
Решатель занимает вдвое больше времени, чем раньше, поскольку он оценивает цель и ограничение отдельно.
Если у вас есть лицензия Parallel Computing Toolbox™, вы можете сэкономить еще больше времени, установив UseParallel опция для true.
parpool
Starting parallel pool (parpool) using the 'local' profile ...
Connected to the parallel pool (number of workers: 6).
ans =
ProcessPool with properties:
Connected: true
NumWorkers: 6
Cluster: local
AttachedFiles: {}
AutoAddClientPath: true
IdleTimeout: 30 minutes (30 minutes remaining)
SpmdEnabled: trueopts = optimoptions(opts,'UseParallel',true);
tic
[x,fval,exitflag,output] = runobjconstr(x0,opts);
tocElapsed time is 121.151203 seconds.
В этом случае разрешение параллельных вычислений сокращает вычислительное время вдвое по сравнению с последовательным выполнением с вложенной функцией.
Сравнение прогонов с параллельными вычислениями с вложенной функцией и без нее:
tic
[x,fval,exitflag,output] = fmincon(@myrosen2,x0,...
[],[],[],[],[],[],@constr,opts);
tocElapsed time is 235.914597 seconds.
В этом примере вычисления параллельно, но не вложенно занимают примерно то же время, что и вычисления вложенные, но не параллельные. Вычисления как вложенные, так и параллельные занимают половину времени одного использования.