我会像这样稍微重写它......
目标:MIN (1500 * x0) + (625 * x1) + (100 * x2)
约束: schedule(x0,x1,x2) >= 480 x1 > x0 x1 > x2 x0, x1, x2 是整数
界限:(5 <= x0 <=50), (5 <= x1 <=100), (1 <= x2 <=20)
在这里,我们将在函数中定义调度,以及边界和目标。
>>> import mystic as my
>>> import mystic.symbolic as ms
>>> import numpy as np
>>>
>>> def objective(x):
... x0,x1,x2 = x
... return (1500 * x0) + (625 * x1) + (100 * x2)
...
>>> bounds = [(5,50),(5,100),(1,20)]
>>>
>>> def schedule(x0,x1,x2):
... return x0 * x1 - x2 * x2
...
然后我们开始构建约束。约束函数总是x' = constraints(x)
(即它们接受一个参数向量,并返回一个参数向量)。因此,我们在这里有两个特殊情况:(1)符号方程,我们将为其构建x' = cons(x)
,以及对输出的约束schedule
——这需要一些迂回的逻辑来构建。
>>> eqns = '''
... x1 > x0
... x2 < x1
... '''
>>>
>>> ints = np.round
>>> and_ = my.constraints.and_
>>> cons = ms.generate_constraint(ms.generate_solvers(ms.simplify(eqns)), join=and_)
>>>
>>> cons([1,2,3])
[1, 2, 1.999999999999997]
>>> cons([5,5,1])
[5, 5.000000000000006, 1]
>>>
请注意,我已经重写了符号约束,每次在左侧都有一个不同的变量。这是因为不使用and_
,所构建的约束将只使用最后一个。因此,使用and_
会强制运行优化,以便两者都得到尊重。通过不在左侧重用变量,我更容易解决。最后,我检查约束是否按预期工作。
另请注意,我还没有使用整数约束。当我们将所有约束放在一起时,我们可以应用它。
为了对输出应用约束,我们首先建立一个惩罚,然后将其转换为一个约束。它效率不高,因为它再次需要内部优化。
>>> def penalty1(x):
... x0,x1,x2 = x
... return 480 - schedule(x0,x1,x2)
...
>>> @my.penalty.linear_inequality(penalty1)
... def penalty(x):
... return 0.0
...
>>> lb,ub = zip(*bounds)
>>> c = my.constraints.as_constraint(penalty, lower_bounds=lb, upper_bounds=ub, nvars=3)
>>> c([5,5,1])
[13.126545665004528, 44.97820356778645, 1.0138152329128338]
>>> schedule(*_)
589.3806217359323
>>> c([50,50,10])
[50.0, 50.0, 10.0]
>>> schedule(*_)
2400.0
>>>
然后我们把它们放在一起,再次使用and_
.
>>> constraint = and_(c, cons, ints)
>>>
>>> constraint([5,5,1])
[23.0, 30.0, 2.0]
>>> c(_)
[23.0, 30.0, 2.0]
>>> cons(_)
[23.0, 30.0, 2.0]
>>> objective(_)
53450
最后,我们解决。
>>> from mystic.solvers import diffev2
>>> from mystic.monitors import VerboseMonitor
>>> mon = VerboseMonitor(10)
>>>
>>> result = diffev2(objective,x0=bounds, bounds=bounds, constraints=constraint, npop=50, gtol=100, disp=False, full_output=True, itermon=mon)
Generation 0 has ChiSquare: 43850.000000
Generation 10 has ChiSquare: 42725.000000
Generation 20 has ChiSquare: 42725.000000
Generation 30 has ChiSquare: 42725.000000
Generation 40 has ChiSquare: 42725.000000
Generation 50 has ChiSquare: 42725.000000
Generation 60 has ChiSquare: 42725.000000
Generation 70 has ChiSquare: 42725.000000
Generation 80 has ChiSquare: 42725.000000
Generation 90 has ChiSquare: 42725.000000
Generation 100 has ChiSquare: 42725.000000
STOP("ChangeOverGeneration with {'tolerance': 0.005, 'generations': 100}")
>>> result[0]
array([13., 37., 1.])
>>> result[1]
42725.0
再次使用不同的求解器...
>>> from mystic.solvers import fmin
>>> mon = VerboseMonitor(10)
>>> result = fmin(objective, x0=[13,37,1], bounds=bounds, constraints=constraint, ftol=1e-6, disp=False, full_output=True, itermon=mon)
Generation 0 has ChiSquare: 42725.000000
Generation 10 has ChiSquare: 42725.000000
Generation 20 has ChiSquare: 42725.000000
STOP("CandidateRelativeTolerance with {'xtol': 0.0001, 'ftol': 1e-06}")
您会注意到优化器找到了比我们刚开始应用约束时更好的值,但没有进行太多搜索。在严格限制的空间中,通常会立即找到解决方案。您必须进行更多调查才能确定这是否确实是全局最小值。