我正在尝试解决一个应用于桥梁的优化问题,在参数化设计的背景下。目标函数是尝试将预制护栏分割成最少数量的独特护栏。有两种护栏,普通护栏和带灯杆的护栏。我分享了一个3跨桥梁的图像。
参数如下:
灯杆屏障宽度等于w。
灯杆屏障通过参数s均匀间隔。
灯杆起点由变量a定义。
桥梁的跨度,[l1,l2,. ln]
x是所有方程都必须被整除的宽度。
这个想法是尽可能多的障碍物的宽度x和一些剩余的长度,以填补跨度保持在最低限度。我必须得到所有的距离**[a,b,c,... f]在一个数组的方程(见图),我还在特殊屏障(s-w)之间添加了光线,并使它们都能被x**整除。我还在元素 * 数组中添加了常数,以防剩余长度不能被x整除,因此我们将使用不同的障碍进行补偿(基本上,我们使用该常数进行减法)。
下面是我正在谈论的内容的草图:x1c 0d1x
from scipy.optimize import minimize
import numpy as np
s = 12500
spans = np.array([25450, 25100, 25450])
ejs = np.zeros(len(spans)-1) # expansion or separation joints . ignore for now
min_bw, max_bw = 0, 1200 #light pole barrier width
consts = abs((len(spans) * 2 - 1)) # Number of distance elements [b,c,d...]
init = np.array([1500, 1500, 6250]) #x, w, a + w/2
n = (spans - s / 2) // s # consecutive spacings of barriers for a given span. At times we have more
m = np.ones(len(spans) * 2) # the length of the equation array that has to be divisible
def objective(arr, n, s, spans, ejs):
x = arr[0]
w = arr[1]
a = arr[2] # (the offset is until the mid axis of the light pole)
m = np.ones(len(spans) * 2 + 1) # We make room for the last constraint h=(s-w)
m[0] = a - w / 2
for i in range(1, len(spans) * 2):
j = (i - 1) // 2
if i % 2 == 0:
m[i] = (s - w) - (m[i - 1] + ejs[j] + arr[2 + i])
else:
m[i] = spans[j] - (m[i - 1] + n[j] * s + w + arr[2 + i])
m[-1] = s - w # We also need the light distance between two poles to be divisible by x
#print(m)
return np.sum(m%x)
#Bounds
bounds = np.array([(1500, 2100), (1500, 2600), (3000, s)])
extras = np.repeat([(min_bw, max_bw)], consts, axis=0) # Add the bounds for each additional element
all_bounds = np.concatenate((bounds, extras), axis=0)
initial = np.pad(init, (0, consts), 'constant', constant_values=min_bw)
sol = minimize(fun=objective, x0=initial, bounds=all_bounds, method='Nelder-Mead', args=(n, s, spans, ejs))
print(np.round(sol.x))
#m = [ 5500. , 5950., 5050. ,6050. ,4950. , 6500. ,11000.] #the print m statment
字符串
我似乎不能弄清楚的问题是np.sum(m%x)
不返回0!我得到了正确的距离在m数组与绘图相比。整除方程约束不满足,但函数说它成功终止。我会很感激任何见解!非常感谢!
2条答案
按热度按时间5kgi1eie1#
尼克提出的主要问题是正确的(尽管不全面):有许多局部极小值,目标是不可微的。
但同样重要的是:你的目标根本不适合作为一个目标--它应该是一个约束;整个系统这意味着现在是整数线性规划的时候了,它比一般的
minimize
有更好的收敛性和准确性保证。(无论您选择哪种方法)。更具体地说,您正在进行约束编程,这里根本没有目标(或者你可能知道更好的目标应该是什么,比如最小化x的系数)。除了x、w、a、“距离元素”和m的变量之外,还需要一个二进制选择矩阵,其x的倍数等于m。这通过使用所谓的大M约束来解决x乘以某个倍数得到m是非线性的问题。
我已经尝试让它易于理解,在任何时候你都可以调用
some_constraint.A.toarray()
并在PyCharm的矩阵可视化器中查看结果,看看发生了什么;但你可能还需要深入研究一些关于线性规划的介绍性材料,以更好地掌握它。个字符
这个问题的规模相当小,而且稀疏性很高,因此可以快速求解。
型
x1c 0d1x的数据
j7dteeu82#
你现在有两个问题。
m % x
有一个使优化器混淆的导数。1.你要解决的问题有很多局部最小值。
让我们首先为优化器提供有关如何优化函数的更多信息。
混淆导数
函数
minimize()
是一个局部优化器,如果有另一个最小值,它必须爬上一座山才能到达,它就不能到达它。假设你想最小化函数f(x)= x % 100,其中x限制在50和150之间。
x1c 0d1x的数据
假设优化器从x = 90开始,它查看导数,然后沿着斜率向左移动,然后在x = 50时卡住,同样的事情也发生在你的问题中。
要解决这个问题,你可以创建一个函数,奖励优化器接近x = 100。我想出了这个函数,它是一个余弦波,当
variable
接近target
的倍数时,它接近于零。字符串
(Note:
overshoot_distances_amount
的用途将在后面解释。)这给出了下图:
的
这告诉优化器,如果它在x=99,它应该向x=100移动。
我用下面的方法将这个新函数集成到您的代码中。
首先,我改变了原始的目标函数,使其返回m和x,称之为
objective_inner()
。型
然后,我写了两个版本的目标函数。
型
第一个函数是你的目标的一个版本,它有很好的导数,使用正弦波。第二个函数和你原来的目标一样。
然而,修正导数本身是不够的,它仍然会陷入局部极小值。
全局优化器
有些函数不能被局部优化器优化,因为优化器会陷入局部最小值。这就是其中之一。
为了解决这个问题,我使用了basinhopping函数,它使用了一个局部优化器,但也在随机方向上采取了很大的步骤,希望找到一个更好的最小值。
我发现下面的工作很好。
型
这将执行以下操作:
overshoot_distances_amount
。这会选择一个稍微在模循环点之后的解,这使得优化器更容易找到objective_old()
的精确最小值。)这可以找到在原始目标上获得1 e-4或更低分数的解决方案。
以下是它找到的解决方案之一:
型