代码之家  ›  专栏  ›  技术社区  ›  JE_Muc

具有多个边界、约束和连续字段的scipy.optimize

  •  1
  • JE_Muc  · 技术社区  · 6 年前

    我想优化一个 CHP plant 通过请求的电源配置文件。因此,我定义了一个功率曲线,热电厂应尽可能遵循该曲线。 必须应用多个边界和约束来表示热电厂的实际运行。例如,这包括CHP可以开启或关闭,并且在开启时,其功率调制只能设置为特定百分比范围。

    import scipy.optimize as opt
    import numpy as np
    
    x = np.arange(200)  # dummy x vector
    poly_profile = np.array(  # 7th degree polynome fit of profile
        [-2.14104340e-11,  1.85108903e-08, -6.66697810e-06,  1.29239710e-03,
         -1.45110876e-01,  9.40324129e+00, -3.24548750e+02,  4.60006330e+03])
    poly_fun = np.poly1d(poly_profile)  # make poly fun
    profile = poly_fun(x[65:196])
    x0 = np.zeros_like(profile)  # all zeros as starting values
    
    def optifun(x, profile):  # define minimization fun
        return - np.sum(profile * x)
    
    bnds_hi = opt.Bounds(0.3, 1)  # upper bounds
    bnds_lo = opt.Bounds(0, 0)  # lower bounds
    
    res = opt.minimize(
        optifun, x0, args=(profile), bounds=bnds_hi,
        constraints={'type': 'eq', 'fun': lambda x:  np.sum(x*40) - 2000},
        method='SLSQP')
    plt.plot(res.x)
    plt.plot(profile)
    

    所以我想用这些界限:

    • (x == 0) or (0.3 <= x <= 1) x
      这意味着x,CHP总功率的调制度,可以是0(关闭)或 >0.3 <= 1 . 但我可以指定下限 或者 上界。仅指定上限使其无法“关闭CHP”,而将下限设置为 bnds_lo = opt.Bounds(0, 1)
      将使热电联产装置在非实际运行点运行(在0%和30%功率调制之间)。
      bounds=[bnds_lo, bnds_hi] ?
      我猜这是一个混合整数线性规划问题,但是COBYLA或SLSQP难道不能处理这个问题吗?如果没有:是否有解决方法?

    • np.sum(x*40) - 450
      将热输出限制为某些热存储容量。这里40是热输出功率,450是剩余存储容量。这很容易实现。
    • 限制热电厂的启动次数。作为一个例子,我们假设

      bnds_lo = opt.Bounds(0, 1)  # lower bounds
      res = opt.minimize(
          optifun, x0, args=(profile), bounds=bnds_lo,
          constraints={'type': 'eq', 'fun': lambda x:  np.sum(x*40) - 1000},
          method='SLSQP')
      

      这导致热电联产装置运行3个阶段。有没有办法限制这个?我在考虑添加一个特定的约束函数来计算前导0之后的正微分,但是我不能做类似的工作(例如,因为大多数 (0, 1) . 但其他问题也可能是原因)。。。

    • 设定热电联产装置的最小连续运行时间。这意味着至少应该有5个连续的 x != 0 . 我想尝试一些类似于上一点的方法(限制启动次数),但也没能找到有用的方法。这是迄今为止最不重要的问题。

    为了解决这些问题,我还尝试使用 scipy.optimize.LinearConstraings NonlinearConstraings method='trust-constr'

    我有什么办法能让这件事成功吗?特别是指定多个边界非常重要。

    提前谢谢!

    真诚地, 斯科蒂

    1 回复  |  直到 6 年前
        1
  •  2
  •   denis    6 年前

    profile * x0 在你的代码里
    “ValueError:操作数无法与形状(131,)(200,)”一起广播。”。

    x_t onoff_t * xon_t
    onoff_t =0或1
    0.3 <= xon_t <= 1 每次 t 在里面 0 .. T ?
    T = 5 有2^5种可能 onoff

    sum 0:T w_t * onoff_t * xon_t 具有固定权重函数 w_t 是微不足道的:
    w_t <= 0 : onoff_t = 0 ,关
    w_t > 0 : onoff_t = 1 ,开,和 xon_t = 1

    如果 进一步限制为只切换两次,0。。。1... 0..., 那么可能的序列数就足够小,可以全部尝试, 沿线:

    def pulse_generator( T=200, minwidth=5 ):
        """ -> arrays of T floats, 0... 1... 0... """
        for t0 in xrange( 1, T ):
            for t1 in xrange( t0 + minwidth, T ):
                pulse = np.zeros( T )
                pulse[t0:t1] = 1
                yield pulse
    
    for pulse in pulse_generator( T ):
        print "pulse:", pulse
        optimize myfunction( pulse * xon ), 0.3 <= xon <= 1
    

    (一个给定的时间有多少这样的脉冲 T ? 见维基百科 Stars and bars -- 太棒了。)


    bang-bang control 非常 一个程序(mer)可以花很多时间抖动,在噪声中。
    1. 把时间0:T分成10段,运行所有2^10=1024个开关序列
    2. 将边缘移动半步,T/20。

    谷歌“离散优化”多重网格。。。和 Grid search