【问题标题】:How to extend the number of Symfit parameters arbitrarily in Python如何在 Python 中任意扩展 Symfit 参数的数量
【发布时间】:2019-08-30 01:05:30
【问题描述】:

我有以下由变量 n 参数化的优化代码。

from symfit import parameters, Eq, Ge, Fit
import numpy as np
n = 3
xdata = np.sort(np.random.choice(range(1, 4*n), n)) # Make fake data
print(xdata)
p1, p2, p3 = parameters('p1, p2, p3')
model = p1*p2*p3
constraints = [
    Eq(xdata[0]*p1+(xdata[1]-xdata[0])*p2+(xdata[2]-xdata[1])*p3, 1),
    Ge(p1, p2),
    Ge(p2, p3),
    Ge(p3, 0)
    ]

fit = Fit(- model, constraints=constraints)
fit_result = fit.execute()
print(fit_result)

我想将它用于更大的 n 值,但我不知道如何更改线条

 p1, p2, p3 = parameters('p1, p2, p3')
 model = p1*p2*p3

constraints 来应对任意大的n

代码使用symfit 库。该链接显示了如何使用 parameters 的示例以及文档链接。

如何做到这一点?

【问题讨论】:

  • p1, p2, p3 = parameters('p1, p2, p3') 这条线对我来说很模糊...可以做一个不包括那些库的minimal reproducible example 吗? (它可以帮助你也可以帮助我)
  • @UlysseBN 问题是这条线是我问题的关键。我将添加相关文档的链接。

标签: python symfit


【解决方案1】:

Numpy 与symfit 库的交互真的很好。使用它时,您尝试概括的所有操作都相当简单。


设置

n = 3
_data = np.sort(np.random.choice(np.arange(1, 4 * n), n))

  1. 字符串格式

    您可以使用简单的迭代器和str.join 动态创建参数的tuple,然后您可以将其传递给parameters 构造函数以获取参数的tuple

params = parameters(', '.join(f'p{i}' for i in range(1, n+1)))
                ^^
# p1, p2, p3 = parameters('p1, p2, p3')
  1. np.prod

    这个操作非常简单。 np.prod 计算:

    数组元素在给定轴上的乘积

    当应用于symfit 参数中的tuple 时,会产生您想要的p1*p2*...pn

model = np.prod(params)
        ^^
# model = p1*p2*p3
  1. np.concatenate + np.diff

    可能是概括起来最复杂的一行,但仍然不太难理解。您想将数据数组中连续元素的差异乘以您的参数,并对结果求和。由于第一个元素与前一个元素没有区别,您可以使用np.concatenate 重新添加。

u = np.concatenate((_data[:1], np.diff(_data)))
c1 = Eq(np.sum(params * u), 1)
              ^^
# Eq(xdata[0]*p1+(xdata[1]-xdata[0])*p2+(xdata[2]-xdata[1])*p3, 1)
  1. np.column_stack

    您希望将参数滚动查看为约束:p1-p2p2-p3、...pn, 0。这只是用原始的tuple 参数堆叠一个带有零填充的一次性元组,然后使用列表推导解压缩到您的Ge 构造函数中。

ges = [Ge(*group) for group in np.column_stack((params, params[1:] + (0,)))]
  1. 适合!

    我没有在这里改变任何东西!

constraints = [c1, *ges]
fit = Fit(- model, constraints=constraints)
fit_result = fit.execute()

【讨论】:

  • 很好地使用了 numpy 操作,这绝对比包括我在内的其他基本解决方案更好。不过,我希望你能多解释一下你在做什么以及它为什么有效。
  • @Avish 更新了更多信息。希望大多数运营商都熟悉 OP,因为他们已经在使用 numpy
  • @Anush 字符串格式肯定不起作用,您需要使用str.format。除此之外,我只是测试它,我并没有真正使用 symfit,并且正在使用 3.6 来测试这段代码
  • 非常感谢。你的解决方案肯定很复杂!
【解决方案2】:

参数字符串需要从n动态计算出来

paramstr = ', '.join(['p{}'.format(i) for i in range(1, n)])
# when n=1, paramstr = 'p1, p2, p3'

使用paramstr 作为parameters 函数的参数。

paramvals = parameters(paramstr)

model 可以通过在其产品上减少 paramvals 来重构。


from functools import reduce

model = reduce(lambda x, y: x * y, paramvals, 1)

现在开始有趣的部分! constraints 可以重构为:

eqs = xdata[0] * paramvals[0] + sum(
    (xdata[i] - xdata[i-1]) * paramvals[i]
    for i in range(1, n)
)

ges = [
    Ge(paramvals[i-1], paramvals[i])
    for i in range(1, n)
]

ges.append(
    Ge(paramvals[-1], 0)
)

constraints = [
    Eq(eqs, 1),
    *ges
]

【讨论】:

    【解决方案3】:

    我对 Symfit 一无所知,但如果你只是想将上面的代码泛化为任意 N,那么:

    • 您可以为任意 N 任意生成一个类似于"p1, p2, p3" 的字符串,并将其解析为参数列表:
    params_string = ", ".join("p{}".format(i + 1) for i in range(n))
    params = parameters(params_string)
    

    解析字符串以获取参数列表的概念对我来说听起来很糟糕,而且我确信有更好的方法来以编程方式声明一堆参数,但这将尽可能接近您的原始代码正在做。

    编辑:查看 Symfit 文档,parameters(s) 似乎只是一个捷径,您实际上可以这样做:

    params = [Parameter("p{}".format(i + 1)) for i in range(n)]
    

    这不需要您构建自己的所有参数名称的连接字符串,以便 Symfit 可以将它们拆分回单独的参数名称。这还允许您为参数定义其他属性,例如它们的初始值或它们的最小/最大界限。

    • 您可以概括Eq 约束:
    coeffs = [xdata[0]] + [(xdata[i+1] - xdata[i]) for i in range(n-1)]
    eq_constraint = Eq(sum(param * coeff for param, coeff in zip(params, coeffs), 1)
    

    或者,作为另一个答案,使用 numpy 操作:

    coeffs = np.concat(xdata[:1], np.diff(xdata))
    eq_constraint = Eq(np.sum(params * coeffs), 1)
    
    • 您可以概括Ge 约束:
    ge_constraints = [Ge(params[i + 1], params[i]) for i in range(n - 1)] + [Ge(params[-1], 0]
    
    constraints = [eq_constraint] + ge_constraints
    

    这可以再次使用 numpy 操作来完成,但我将把它留给@user3483203 的回答。

    • 您可以使用reduce 将所有参数相乘:
    model = reduce(lambda x, y: x * y, params, 1)
    

    或者使用numpy.prod:

    model = np.prod(params)
    

    这应该足以将上述推广到任意 N。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 2020-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      相关资源
      最近更新 更多