在 Matlab 中寻找加快符号表达式数值计算的方法

计算科学 优化 matlab 数字 符号计算
2021-11-30 06:13:28

{摘要:我有一个符号表达式 DCritnF,用两个变量 x1 和 x2 表示。我需要找到它的数值,我使用了 double 和 subs 的组合,如下所示。

FuncVal = double(subs(DCritnF,[x1,x2],[x(1),x(2)]));

挑战在于,由于 DCritnF 是一个非常复杂的表达式(由符号工具箱使用矩阵代数和一些微积分的组合生成),因此评估 FuncVal 至少需要 0.1 秒(使用 timeit 函数)。这是不可接受的,因为我正在执行函数优化,并且对上述代码行有多个函数调用,并且单个优化运行需要 4 秒到 40 秒,具体取决于所使用的算法类型。我已经使用分析器确定唯一的罪魁祸首是上面的代码行。如何加快上述功能评估?}

详细信息:我通过 For Loop 为决策变量提供不同的起点,致力于非线性和非凸函数的全局优化(只有 matlab 中的优化工具箱,没有全局优化工具箱,因此模拟多启动功能)。

我的目标函数非常复杂,基于使用http://www.amstat.org/publications/jse/v12n3等式 11 中的函数梯度的一系列矩阵计算(转置、乘法、逆和最终行列式) /goos.html目标函数(DCritn)列在本题末尾,以供参考。

这里 q1 和 q2 是参数,x1/x2 是决策变量。

对于 q1 和 q2 的任何给定值,例如 0.6 和 0.3,我尝试找到 DCritn 最小的 x1/x2 的值。为了进行优化,我为 x 提供了不同的起点,并使用不同的算法集,如内点、SQP 等,以查看哪个算法生成了最好的局部 mimima。

syms x1 x2 q1 q2;
q=[q1,q2];
x=[x1,x2];
qvalue=[0.6,0.3];

%bunch 符号计算以达到目标函数 DCritn 的符号形式(请参阅签名后的最终结果)

DCRGrad = jacobian(DCritn,x).';%Gradient of the the objective function
DCritnF = subs(DCritn,q,qvalue.');%DCritn objective function specific to qvalue
DCritnGradF = subs(DCritnGrad,q,qvalue.');%Gradient of objective function as per qvalue

xstart=[5,10];% supplying start points for the optimization

%fmincon code to optimize DCritn using different start values of x. Options for using gradient specified (fmincon code skipped here)

我有一个单独的函数文件“Objfungrad”,在下面提供,fmincon 可以利用它来计算任何给定 x 值的函数值和梯度

function [FuncVal,gradVal] = Objfungrad(x)

global DCritnF DCritnGradF;
global x1 x2;
syms x1 x2;

try
% Objective function
    FuncVal = double(subs(DCritnF,[x1,x2],[x(1),x(2)]));
catch exception
    FuncVal = NaN;
end    
% Gradient of the objective function
if nargout  > 1
      gradVal= double(subs(DCritnGradF,[x1,x2],[x(1),x(2)]));
end

根据分析工具,超过 95% 的优化时间用于评估与 FuncVal 和 gradVal 相关的代码行。

如果不是上面使用的 double 和 subs 函数,如果我使用 qvalue 作为固定参数对 FuncVal 和 gradVal 的完整符号表达式进行了硬编码,那么优化将在不到 10% 的时间内完成。不幸的是,这种硬编码是不可能的,因为参数 q 可以改变。

所以问题是,如何加快基于 double 和 subs 的符号表达式的评估?

提前感谢哈里

DCritn = ((q1 - q2)^4*(q1^2*exp(2*q1*x1)*exp(2*q1*x2)*exp(2*q2*x1)*exp(2*q2*x2) + q2^2*exp(2*q1*x1)*exp(2*q1*x2)*exp(2*q2*x1)*exp(2*q2*x2) - 2*q1*q2*exp(2*q1*x1)*exp(2*q1*x2)*exp(2*q2*x1)*exp(2*q2*x2)))/(q1^2*(q1^2*x1^2*exp(2*q1*x2)*exp(2*q2*x1) + q2^2*x1^2*exp(2*q1*x1)*exp(2*q1*x2) + q1^2*x1^2*exp(2*q2*x1)*exp(2*q2*x2) + q1^2*x2^2*exp(2*q1*x1)*exp(2*q2*x2) + q2^2*x1^2*exp(2*q1*x1)*exp(2*q2*x2) + q2^2*x2^2*exp(2*q1*x1)*exp(2*q1*x2) + q1^2*x2^2*exp(2*q2*x1)*exp(2*q2*x2) + q2^2*x2^2*exp(2*q1*x2)*exp(2*q2*x1) - 2*q1^2*x1^2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) - 2*q1^2*x2^2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) - 2*q2^2*x1^2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) - 2*q2^2*x2^2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) + q1^4*x1^2*x2^2*exp(2*q1*x1)*exp(2*q2*x2) + q1^4*x1^2*x2^2*exp(2*q1*x2)*exp(2*q2*x1) - 2*q2^2*x1*x2*exp(2*q1*x1)*exp(2*q1*x2) - 2*q1^2*x1*x2*exp(2*q2*x1)*exp(2*q2*x2) - 2*q1^3*x1*x2^2*exp(2*q1*x1)*exp(2*q2*x2) - 2*q1^3*x1^2*x2*exp(2*q1*x2)*exp(2*q2*x1) + q1^2*q2^2*x1^2*x2^2*exp(2*q1*x1)*exp(2*q2*x2) + q1^2*q2^2*x1^2*x2^2*exp(2*q1*x2)*exp(2*q2*x1) - 2*q1*q2*x1^2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) - 2*q1*q2*x1^2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) - 2*q1*q2*x2^2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) - 2*q1*q2*x2^2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) + 2*q1^2*x1*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) + 2*q2^2*x1*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) + 2*q1^2*x1*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) + 2*q2^2*x1*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) - 2*q1*q2^2*x1*x2^2*exp(2*q1*x2)*exp(2*q2*x1) - 2*q1*q2^2*x1^2*x2*exp(2*q1*x1)*exp(2*q2*x2) + 2*q1^2*q2*x1*x2^2*exp(2*q1*x1)*exp(2*q2*x2) + 2*q1^2*q2*x1*x2^2*exp(2*q1*x2)*exp(2*q2*x1) + 2*q1^2*q2*x1^2*x2*exp(2*q1*x1)*exp(2*q2*x2) + 2*q1^2*q2*x1^2*x2*exp(2*q1*x2)*exp(2*q2*x1) + 2*q1^3*x1*x2^2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) - 2*q1^3*x1^2*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) - 2*q1^3*x1*x2^2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) + 2*q1^3*x1^2*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) - 2*q1*q2*x1*x2*exp(2*q1*x1)*exp(2*q2*x2) - 2*q1*q2*x1*x2*exp(2*q1*x2)*exp(2*q2*x1) - 2*q1^3*q2*x1^2*x2^2*exp(2*q1*x1)*exp(2*q2*x2) - 2*q1^3*q2*x1^2*x2^2*exp(2*q1*x2)*exp(2*q2*x1) + 2*q1^3*x1*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 2*q1^3*x1^2*x2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 2*q1*q2*x1*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) + 2*q1*q2*x1*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) + 2*q1*q2*x1*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) + 2*q1*q2*x1*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) - 2*q1^4*x1^2*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 4*q1*q2*x1^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 4*q1*q2*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) - 2*q1^2*x1*x2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) - 2*q2^2*x1*x2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 2*q1*q2^2*x1*x2^2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) - 2*q1*q2^2*x1^2*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) - 2*q1^2*q2*x1*x2^2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) + 2*q1^2*q2*x1^2*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q1*x2) - 2*q1*q2^2*x1*x2^2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) + 2*q1*q2^2*x1^2*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) - 2*q1^2*q2*x1*x2^2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) + 2*q1^2*q2*x1*x2^2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) + 2*q1^2*q2*x1^2*x2*exp(q1*x1)*exp(q2*x1)*exp(2*q2*x2) - 2*q1^2*q2*x1^2*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q1*x1) + 2*q1^2*q2*x1*x2^2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) - 2*q1^2*q2*x1^2*x2*exp(q1*x2)*exp(q2*x2)*exp(2*q2*x1) + 4*q1^3*q2*x1^2*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) - 2*q1^2*q2^2*x1^2*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 2*q1*q2^2*x1*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) + 2*q1*q2^2*x1^2*x2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) - 4*q1^2*q2*x1*x2^2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) - 4*q1^2*q2*x1^2*x2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2) - 4*q1*q2*x1*x2*exp(q1*x1)*exp(q1*x2)*exp(q2*x1)*exp(q2*x2)))
3个回答

如果表达式像 VictorLiu 所示的那样“简单”(我意识到这是一个相对术语),那么您可以尝试手动编码。

如果手动编写等效的 MATLAB 表达式不方便(例如,如果您有其他更复杂的表达式要使用),您可以尝试使用 MATLAB 的代码生成工具。MATLAB 可以将 MuPAD 符号表达式转换为 MATLAB、Fortran 或 C 代码。如果速度真的很关键,将符号表达式转换为 C 代码,然后围绕 C 代码编写 MEX 包装器以从 MATLAB 调用它可能是一个可行的选择。

你试过matlabFunction吗?例如,

syms x y real
f = matlabFunction(x^2 + y^2, 'vars', {[x, y]})

将创建一个(非符号)函数f,该函数将二分量向量作为输入(而不是两个不同的输入参数)。该函数是自动矢量化的,这可能允许 MATLAB 优化其评估。

通过 Mathematica 的 FullSimplify 运行您的表达式会产生以下结果:

(exp(2*(q1 + q2)*(x1 + x2))*(q1 - q2)^6)/(q1^2*(exp(q2*(x1 + x2))*q1*(x1 - x2) + 
 exp(q1*(x1 + x2))*q2*(x1 - x2) + 
 exp(q1*x1 + q2*x2)*(-q2*x1 + q1*x2 + q1*(-q1 + q2)*x1*x2) + 
 exp(q2*x1 + q1*x2)*(q2*x2 + q1*x1 (-1 + q1*x2 - q2*x2)))^2)

这至少要短得多。