优化以找到复数作为输入

6

我想知道是否有一个C/C++库或Matlab代码技术可以使用最小化求解器来确定实数和复数。下面是一段代码片段,展示了我想要做的事情。例如,假设我知道Utilde,但不知道xU变量。我想使用优化(fminsearch)来确定给定UtildexU。注意Utilde是一个复数。

x = 1.5;
U = 50 + 1i*25;
x0 = [1 20];  % starting values
Utilde = U * (1 / exp(2 * x)) * exp( 1i * 2 * x);
xout = fminsearch(@(v)optim(v, Utilde), x0);

function diff = optim(v, Utilde)
x = v(1);
U = v(2);
diff =  abs( -(Utilde/U) + (1 / exp(2 * x)) * exp( 1i * 2 * x  ) );

上面的代码无法收敛到正确的值,xout = 1.7318 88.8760。但是如果U = 50,那么xout = 1.5000 50.0000,这才是正确的值。
在Matlab或C/C++中是否有一种方式可以确保给定复数Utilde的正确收敛?也许我需要更改上面的代码?
  • 如果Matlab没有本地化实现该功能,则问题的要点之一可能是:是否存在能够处理实数和复数输入输出的多元(即Nelder-Mead或类似算法)优化库?
  • 另一个问题是函数是否收敛。我不知道是算法还是函数的问题。我需要更改Utilde = U * (1 / exp(2 * x)) * exp( 1i * 2 * x)表达式中的某些内容来使其收敛吗?

根据我的经验:使用这些内置的最小化程序通常会给你带来更多的麻烦,而不是帮助。如果你一定要这样做,我建议你坚持使用Python - 使用MATLAB可能不会更好。 - Michael Schlottke-Lakemper
当然 - 在Python中设置这个优化问题的最佳方法是什么?是否有一个可以使用复数进行优化的Python工具? - Nicholas Kinar
@NicholasKinar 我现在对于复数的算术规则有些不确定,但是如果你只想要在优化中检索最顶端的 xU 值,那么将 diff 指定为 diff = abs( Utilde - U * (1 / exp(2 * x)) * exp( 1i * 2 * x ) ) 是否更合适呢?或者从微分的角度来看,使用差的平方而不是绝对差更好呢? - Anders Gustafsson
@AndersGustafsson:感谢您的评论。嗯...我尝试了这个方法,但仍然无法使所有的xU收敛。例如,当x = 7U = 10时,仍然存在问题。也许是我做错了什么。 - Nicholas Kinar
@NicholasKinar,我进一步研究了这个问题并提供了一个答案。请看看您是否同意。 - Anders Gustafsson
3个回答

2
主要的问题在于这个优化或参数拟合问题没有唯一解。例如,从上面的预期结果和实际结果来看,对于两个(xU)对,Utilde是等效的(忽略四舍五入的差异)。
Utilde(x = 1.5, U = 50 + 25i) = Utilde(x = 1.7318, U = 88.8760)

虽然我没有深入研究,但我甚至怀疑对于任何x的值,你都可以找到一个U,使得Utilde(x,U)=Utilde(x=1.5,U=50+25i)

因此,解决方案是进一步限制参数拟合问题,以便求解器产生任何可接受的解决方案。或者,重新定义Utilde,使得任何(xU)对都有唯一的值。

更新,8月1日

在合理的起始值下,似乎将x限制为实值就足够了。使用上述定义的diff函数执行无约束非线性优化,得到以下结果:

x = 1.50462926953244
U = 50.6977768845879 + 24.7676554234729i
diff = 3.18731710515855E-06

然而,将起始猜测更改为距离所需值更远的值确实会产生不同的解,因此仅将x限制为实值并不能为问题提供唯一的解决方案。
我已经在C#中实现了此功能,使用BOBYQA优化器,但数字应与上述相同。如果您想尝试Matlab之外的方法,则也应该相对简单地将下面的C#代码转换为C++代码,使用std::complex类和您自己选择的(无约束)非线性C++优化器。您可以在此处找到一些不需要梯度计算的 C++ 兼容代码here,Numerical Recipes中也有各种实现可用。例如,您可以在此here在线访问 NR 的C版本。
以下是我的 C# 代码的相关部分,仅供参考:
class Program
{
    private static readonly Complex Coeff = new Complex(-2.0, 2.0);
    private static readonly Complex UTilde0 = GetUTilde(1.5, new Complex(50.0, 25.0));

    static void Main(string[] args)
    {
        double[] vars = new[] {1.0, 25.0, 0.0}; // xstart = 1.0, Ustart = 25.0
        BobyqaExitStatus status = Bobyqa.FindMinimum(GetObjfnValue, vars.Length, vars);
    }

    public static Complex GetUTilde(double x, Complex U)
    {
        return U * Complex.Exp(Coeff * x);
    }

    public static double GetObjfnValue(int n, double[] vars)
    {
        double x = vars[0]; 
        Complex U = new Complex(vars[1], vars[2]);
        return Complex.Abs(-UTilde0 / U + Complex.Exp(Coeff * x));
    }
}

感谢您的深入回答。我该如何重新构造Utilde,使得任何(x, U)对都有唯一的值? - Nicholas Kinar
@NicholasKinar Utilde 代表什么意思?你能提供一些相关的上下文链接吗? - Anders Gustafsson
表达式的实部可以在书的第67页找到,作为方程(4.22)和方程(4.20)。此外,“Utilde”表达式在第128页上,作为方程(7.21)。 “U”是一个Gabor变换地震输入跟踪。 - Nicholas Kinar
1
@NicholasKinar 请查看我对这个答案的鼓励性更新 :-)。 - Anders Gustafsson
1
谢谢,Anders;非常感谢。感谢您为我提供了一个出色的框架和代码来解决这个问题。我将进一步尝试指数函数参数,然后再回复您。 - Nicholas Kinar
显示剩余10条评论

2
fminsearch的文档在限制部分说明了如何处理复数:

fminsearch仅对实数进行最小化,即x必须只由实数组成,f(x)必须只返回实数。当x具有复杂变量时,它们必须被拆分为实部和虚部。

您可以使用函数realimag分别提取实部和虚部。请注意保留HTML标签。

实际上,这可能对此处没有帮助,因为您的函数针对实数输入返回复杂输出。不过,如果您的函数是实值的,那么我认为这种方法会起作用。 - zroth
这个问题是关于复数的绝对值,在这种情况下,diff变量始终是实数吗?如果是这样的话,我该如何将其拆分为实部和虚部呢? - Nicholas Kinar
@NicholasKinar 你说得对。你的函数似乎采用实数输入并具有实值。为什么你说 fminsearch 在复杂的 Utilde 上无法收敛?你是通过解析方法找到最小值的吗? - zroth
嗯,我不确定这是函数的问题还是其他问题。我会进行调查。 - Nicholas Kinar

0

看起来没有简单的方法可以做到这一点,即使xU都是实数。 Utilde的方程对于优化问题来说不是良好定义的,因此必须进行修改。

我尝试编写自己版本的Nelder-Mead优化算法,并尝试了Powell的方法。 即使我尝试修改这些方法,它们在这个问题上也似乎效果不佳。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接