0. Зачем использовать Юлию для оптимизации?
Эта статья мояЮлия Китайское сообществоВстреча пользователей 2018 г.О совместном содержании оптимизационного моделирования и решения с Юлией.
В этом руководстве в основном представлен JuMP.jl, AML (язык алгебраического моделирования) с открытым исходным кодом на языке Julia, аналогичный AMPL, YALMIP, CVX, Poymo, GAMS и т. д. Оптимизированное моделирование + скорость передачи памяти решателя JuMP сравнивается с другими коммерческими / открытыми исходными кодами AML в таблице ниже. Convex.jl не задействован, как CVX в Matlab, еще один AML в Julia, разработанный Boyd et al.
Мы обнаружили, что JuMP не уступает коммерческому AML в оптимизации скорости моделирования и значительно быстрее, чем другие AML с открытым исходным кодом.
Еще одним большим преимуществом JuMP является бесшовная интеграция различных коммерческих/открытых решателей: Gurobi, CPLEX, SCIP, Knitro, Mosek и т. д.
1. Предварительное оптимизационное моделирование (самое основное линейное программирование)
(Эта часть в основном предназначена для читателей, у которых нет знаний в теории оптимизации. Примеры взяты из -Цинь Ханьчжан: Как понять дополнительный резерв в исследовании операций. Как следует использовать это свойство?)
Скажем, вы плотник, продающий деревянные столы и стулья ручной работы. Теперь вы хотите создать производственный план, чтобы максимизировать прибыль за месяц.
Обратите внимание, что для изготовления стола или стула требуется определенное количество дерева и времени, а в качестве небольшой ремесленной мастерской время, которое вы можете использовать для производства каждый месяц, ограничено, и вы можете ввести количество древесины также ограничено.
Для простоты предположим, что фиксированная прибыль составляет 10 долларов за стол и 3 доллара за стул. Для изготовления стола требуется 5 единиц дерева и 3 единицы времени, а для изготовления стула — 2 единицы дерева и 1 единица времени. И все столы и стулья, которые мы производим, можно продать. Предположим, у нас есть всего 200 единиц древесины и 90 единиц времени на месяц.
Очевидно, что эту задачу производственного планирования можно смоделировать с помощью линейного программирования:
Согласно теории двойственности, мы также знаем, что эквивалентная двойственная задача:
# 利用JuMP求解原问题
function CarpenterPrimal(c,A,b)
# 定义Model对象, OutputFlag = 0指不输出log
Primal = Model(solver = GurobiSolver(OutputFlag = 0))
# 定义变量,注意这里使用了宏(macro),宏的调用也是Julia&JuMP高效编译/元编程(metaprogramming)的重要技巧
@variable(Primal, x[1:2]>=0)
# 定义不等式约束
constr = @constraint(Primal, A*x.<=b)
# 定义目标函数
@objective(Primal, Max, dot(c,x))
# 求解
solve(Primal)
# 返回最优目标函数值,最优解(原问题),最优解(对偶问题)
return getobjectivevalue(Primal), getvalue(x), getdual(constr)
end
При вызове функции CarpenterPrimal оптимальным решением будет изготовление 30 столов и ни одного стула:. Давайте посмотрим на другую информацию, стоящую за этим решением. В то же время теневые цены, соответствующие древесине и времени
равны 0 и 10/3.
Отметим, что анализ чувствительности также можно выполнить с помощью JuMP. Рассмотрим следующие четыре варианта (P) задач: (Конечно, все они могут быть решены вручную и проверены с помощью CarpenterPrimal)
(1) Измените правый конечный элемент (P1) в (P) на 250, то есть общее количество древесины увеличится. Остальные остаются без изменений. Какое оптимальное решение?
(2) Изменить правый конечный элемент (P2) в (P) на 120, то есть увеличить время производства. Остальные остаются без изменений. Какое оптимальное решение? Связь между увеличенной прибылью (по сравнению с первоначальной (P)) и теневой ценой (P2)?
(3) Если предположить, что удельная прибыль от стола постоянна, насколько увеличится удельная прибыль от стула, когда оптимальное решение (P) начнет производить стулья (без производства столов)?
(4) Предполагая, что общее количество времени по-прежнему равно 90, насколько уменьшится общее количество древесины, когда скрытая цена древесины строго больше 0 (вместо этого скрытая цена времени становится равной 0)?
Если вы не знакомы с этими основными понятиями, обратитесь к моему ответу, упомянутому в начале этого раздела.
2. Пример генерации столбца в линейном программировании: крупномасштабная аддитивная выпуклая регрессия
данные данныеРассмотрим следующую задачу линейного программирования:
Обратите внимание, что если мы добавим дополнительный регулярный член к целевой функции(
),
Тогда мы можем получить разреженную аддитивную модель. См.: Равикумар, Прадип и др. «Разреженные аддитивные модели», Журнал Королевского статистического общества: Серия B (Статистическая методология) 71.5 (2009): 1009-1030.
Приведенная выше модель является линейной программой, поскольку она эквивалентна (обозначаетсяза
Последовательность, соответствующая исходному индексу после сортировки по возрастанию)
# 生成样本
n = 5000
d = 2
function_list = [x->0.5*abs.(x).^2,x->2*abs.(x),x->5*x.^2,x->0.5*exp.(x),x->0.3*x,x->0.7*x,x->2*x.^2,x->0.1*exp.(x),x->x.^2,x->exp.(x)]
srand(123)
X = rand(n,d) - 0.5
Y = 0.02*randn(n,1)
for i = 1:d
Y = Y + function_list[mod(i-1,10)+1](X[:,i])
end
# Full-sized的线性规划模型
function Full_ConvReg(Max_T)
M_conv_full = Model(solver=GurobiSolver(TimeLimit=Max_T, OutputFlag=0))
@variable(M_conv_full, f_hat[1:n,1:d])
@variable(M_conv_full, Res_pos[1:n]>=0)
@variable(M_conv_full, Res_neg[1:n]>=0)
for k = 1:d
xx = X[:,k]
ids = sortperm(vec(xx),rev=false)
xx = xx[ids]
for i in 2:(n-1)
d1 = (xx[i+1]-xx[i]); d2=(xx[i]-xx[i-1]);
@constraint(M_conv_full, (f_hat[ids[i],k]-f_hat[ids[i-1],k])*d1<=(f_hat[ids[i+1],k]-f_hat[ids[i],k])*d2);
end
end
for i in 1:n
@constraint(M_conv_full, Y[i] - sum(f_hat[i,k] for k=1:d) == Res_pos[i] - Res_neg[i] )
end
@objective(M_conv_full, :Min, sum(Res_pos[i] + Res_neg[i] for i = 1:n) )
solve(M_conv_full)
return getvalue(f_hat)
end
Далее мы расскажем, что такое генерация столбцов, и покажем, как использовать JuMP и Gurobi для выполнения генерации столбцов для решения вышеуказанных проблем. Этот метод основан на симплексном методе. То есть мы по умолчанию используем все переменные равные 0, а не базовые переменные в начале, и на каждом шаге итерации мы используем значение двойной переменной предыдущего раунда для расчета приведенной стоимости (нужно только вычислить умножение матрица и вектор за один шаг), и положить Переменная с наибольшей отрицательной приведенной стоимостью добавляется (становится базовой переменной). Также обратите внимание, что, поскольку матрица ограничений исходной задачи фактически имеет блочную характеристику (согласноПо сути, мы можем взять формулировку Данцига-Вульфа, то есть использовать метод циклического обновления для расчета приведенной стоимости отдельно для каждого блока в каждой итерации (быстрее, чем вычислять их вместе).
раз). Когда все приведенные затраты неотрицательны, оптимальное решение находится: алгоритм завершает работу.
Для учащихся, не знакомых с Данцигом-Вульфом или генерацией столбцов, см. некоторые классические материалы, такие как:Пирс О. Калифорнийский университет в Лувене. Не голоден / Энтони. Боюсь…
Конечно, принцип генерации ограничений в пятом примере аналогичен принципу генерации столбца (переменной), и этот пример может быть легче читать учащимся с нулевыми базовыми знаниями.
# Column Generation化的线性规划模型
function CG_ConvReg(Max_T)
# build the LO: no Δ included
M_conv = Model(solver=GurobiSolver(TimeLimit = Max_T,OutputFlag = 0,Method = 0))
@variable(M_conv, Res_pos[1:n]>=0)
@variable(M_conv, Res_neg[1:n]>=0)
@constraintref constr[1:n]
MaxIter = 1000
XX = zeros(n,d)
L = zeros(n,n+2,d)
ids = convert(Array{Int64,2},zeros(n,d))
ids_rec = convert(Array{Int64,2},zeros(n,d))
for k = 1:d
# sort the sequences
ids[:,k] = sortperm(vec(X[:,k]),rev=false)
ids_rec[:,k] = sortperm(ids[:,k],rev=false)
XX[:,k] = X[ids[:,k],k]
# needed for column generation
L[:,1,k] = ones(n,1)
L[:,2,k] = -ones(n,1)
L[2,3,k] = XX[2] - XX[1]
L[2,4,k] = - XX[2] + XX[1]
for i = 3:n
L[i,3,k] = XX[i,k] - XX[1,k]
L[i,4,k] = - XX[i,k] + XX[1,k]
L[i,5:i+2,k] = [XX[i,k]-XX[j+1,k] for j=1:i-2]'
end
L[:,:,k] = L[ids_rec[:,k],:,k]
end
@constraint(M_conv, constr[i=1:n], Res_pos[i] - Res_neg[i] == Y[i])
@objective(M_conv, :Min, sum(Res_pos[i] + Res_neg[i] for i = 1:n) )
solve(M_conv)
dual_var = getdual(constr)
NewColumns = [Variable[] for i=1:d]
IdxList = [[] for i=1:d]
# Implement column generation
iter = 1
flag = vec(ones(d,1))
while sum(flag) > 1e-3 && iter < MaxIter
#for t = 1:30
for k = 1:d
if flag[k] == 1
Δ_r = -L[:,:,k]'*dual_var
else
continue
end
if findmin(Δ_r)[1] < -1e-3
idx = findmin(Δ_r)[2]
constr_list = constr[1:n]
coeff_list = L[:,idx,k]
@variable(M_conv, Δ_temp >= 0, objective = 0., inconstraints = constr_list, coefficients = coeff_list )
#print("CG iteration ", iter,": enter Δ ",idx," for covariate ",k, "\n")
push!(NewColumns[k],Δ_temp)
push!(IdxList[k],idx)
solve(M_conv)
iter = iter + 1
dual_var = getdual(constr);
else
#print("Skip covariate ", k,"\n")
flag[k] = 0
end
end
end
#print("CG ends after ", iter-1, " itertaions.\n")
# Retrieve the ϕ values
delta = zeros(n+2,d)
zz = zeros(n,d)
phi = zeros(n,d)
for k = 1:d
delta[IdxList[k],k]=getvalue(NewColumns[k])
if isempty(find(IdxList[k].==2))
zz[1,k] = delta[1,k]
else
zz[1,k] = -delta[2,k]
end
if isempty(find(IdxList[k].==4))
zz[2,k] = delta[3,k]
else
zz[2,k] = -delta[4,k]
end
phi[1,k] = zz[1,k]
phi[2,k] = zz[1,k]+zz[2,k]*(XX[2,k]-XX[1,k])
for i = 3:n
zz[i,k] = zz[i-1,k] + delta[i+2,k]
phi[i,k] = zz[i,k]*(XX[i,k]-XX[i-1,k])+phi[i-1,k]
end
end
return phi
end
phi = CG_ConvReg(200);
# 画出第一维上的解
XX = zeros(n,d)
ids = convert(Array{Int64,2},zeros(n,d))
ids_rec = convert(Array{Int64,2},zeros(n,d))
for k = 1:d
# sort the sequences
ids[:,k] = sortperm(vec(X[:,k]),rev=false)
ids_rec[:,k] = sortperm(ids[:,k],rev=false)
XX[:,k] = X[ids[:,k],k]
end
plot(XX[:,1],phi[:,1])
Далее мы сравним скорость решения этих двух формулировок.
@benchmark CG_ConvReg(200)
@benchmark Full_ConvReg(600)
Удивительно, но алгоритм, основанный на древнем симплекс-методе и генерации столбцов, намного быстрее, чем недетерминированный параллельный метод Gurobi по умолчанию для решения LP! Это доказывает, что иногда более разумное моделирование дает более быстрые алгоритмы для конкретных задач оптимизации.
3. Надежное линейное программирование
Мы рассматриваем задачу потока минимальной стоимости, где стоимость на каждом ребре имеет неопределенность. То есть мы рассматриваем ориентированный граф, рассмотрим задачу оптимизации:
# Nominal Problem
function NominalProblem(n,μ,δ)
NetworkModel = Model(solver=GurobiSolver(OutputFlag=0))
capacity = (ones(n,n)-eye(n,n))*0.5
capacity[1,n] = 0
capacity[n,1] = 0
@variable(NetworkModel, flow[i=1:n,j=1:n]>=0)
@constraint(NetworkModel, flow .<= capacity)
@constraint(NetworkModel, sum(flow[1,i] for i=1:n)==1)
@constraint(NetworkModel, sum(flow[i,n] for i=1:n)==1)
for j = 2:n-1
@constraint(NetworkModel, sum(flow[i,j] for i=1:n) == sum(flow[j,k] for k=1:n) )
end
@objective(NetworkModel,Min, sum(flow[i,j]*μ[i,j] for i=1:n for j=1:n) );
solve(NetworkModel)
return getvalue(flow)
end
Мы рассматриваем следующие три набора неопределенностей:
На самом деле, легко понять, что робастная задача о потоке минимальных затрат (i)(ii) по-прежнему может быть записана в виде линейной программы, а робастная задача о потоке минимальных затрат в (iii) может быть записана как задача второго порядка. задача оптимизации конуса. Однако здесь мы показываем, что использование пакета JuMPeR может сэкономить этап получения надежного аналога.
# Robust Problem
function RobustProblem(n,μ,δ,Γ,norm_type)
NetworkModel_robust = RobustModel(solver=GurobiSolver(OutputFlag=0))
capacity = (ones(n,n)-eye(n,n))*0.5
capacity[1,n] = 0
capacity[n,1] = 0
@variable(NetworkModel_robust, flow[i=1:n,j=1:n]>=0)
@uncertain(NetworkModel_robust, cost[i=1:n,j=1:n])
@uncertain(NetworkModel_robust, r[i=1:n,j=1:n])
@variable(NetworkModel_robust, obj)
for i = 1:n
for j = 1:n
@constraint(NetworkModel_robust, cost[i,j] == μ[i,j]+r[i,j]*δ[i,j])
end
end
@constraint(NetworkModel_robust, norm(r,norm_type) <= Γ)
@constraint(NetworkModel_robust, flow .<= capacity)
@constraint(NetworkModel_robust, sum(flow[1,i] for i=1:n) == 1)
@constraint(NetworkModel_robust, sum(flow[i,n] for i=1:n) == 1)
for j = 2:n-1
@constraint(NetworkModel_robust, sum(flow[i,j] for i=1:n) == sum(flow[j,k] for k=1:n))
end
@constraint(NetworkModel_robust, sum(cost[i,j]*flow[i,j] for i=1:n for j=1:n) <= obj )
@objective(NetworkModel_robust, Min, obj)
solve(NetworkModel_robust)
return getvalue(flow)
end
Давайте проведем здесь несколько симуляций, чтобы увидеть производительность надежного решения. Предполагая, что G полностью ассоциативна, а пропускная способность всех ребер равна 0,5, мы случайным образом генерируем
, а затем случайным образом генерируем
Посмотрим на реальную стоимость. Мы берем решение номинальной задачи в качестве эталона и выводим разницу между стоимостью робастного решения и разностью.
function Evaluation()
n_type = [10,25,50,75,100]
Γ_type = [1e-4,1e-1,1e1,1e4]
report_data = zeros(13,5)
for n_idx = 1:5
n = n_type[n_idx]
count = 2
μ = 10*rand(n,n)
δ = zeros(n,n)
for i = 1:n
for j = 1:n
δ[i,j] = μ[i,j]*rand()
end
end
cost_sim = zeros(n,n)
for i = 1:n
for j = 1:n
cost_sim[i,j] = μ[i,j] + δ[i,j] * (rand()-0.5) * 2
end
end
sol_0 = NominalProblem(n,μ,δ)
report_data[1,n_idx] = sum(sum(cost_sim.*sol_0))
for Γ in Γ_type
print("n=",n," Γ=",Γ)
sol_inf = RobustProblem(n,μ,δ,Γ,Inf)
sol_1 = RobustProblem(n,μ,δ,Γ,1)
sol_2 = RobustProblem(n,μ,δ,Γ,2)
report_data[count,n_idx] = sum(sum(cost_sim.*(sol_inf-sol_0)))
report_data[count+1,n_idx] = sum(sum(cost_sim.*(sol_1-sol_0) ))
report_data[count+2,n_idx] = sum(sum(cost_sim.*(sol_2-sol_0) ))
count = count + 3
end
end
return report_data
end
output = Evaluation();
# 输出Γ=10时三种uncertainty set给出的difference
temp_p = plot([10,25,50,75,100],output[8,:],xlabel = "n", label = "Uinf")
plot!(temp_p,[10,25,50,75,100],output[9,:], label = "U1")
plot!(temp_p,[10,25,50,75,100],output[10,:], label = "U2")
Тогда мы видим, что коробочное ограничение дает более надежное решение, а задача робастной оптимизации при построении неопределенности L1/2 является более мягкой.
JuMPeR также может моделировать многоэтапные задачи надежной оптимизации, особенно для политик с аффинной настройкой (которые намного свободнее, чем формулировка набора неопределенностей). Здесь он только упоминается, а не детализируется, желающие могут изучить его самостоятельно, что на самом деле очень полезно.
4. Приблизительное решение Стабильное число: положительное полуопределенное программирование/целочисленное программирование
неориентированный графСтабильное (независимое) множество V — это подмножество V, в котором все узлы не связаны друг с другом. Максимальная мощность стабильного множества определяется как α(G), также известный как число стабильности графа.Предполагая |V|=n.Естественно, его целочисленное программирование 0/1 можно записать как:
использоватьи
Эквивалентно, мы можем получить релаксацию положительной полуопределенной программы:
Пример источника: Пена, Хавьер, Хуан Вера и Луис Ф. Сулуага, «Вычисление числа устойчивости графа с помощью линейного и полуопределенного программирования», SIAM Journal on Optimization 18.1 (2007): 87-105.
function StableBin(A)
n = size(A,1)
StableB = Model(solver = GurobiSolver(OutputFlag = 0))
@variable(StableB, x[1:n], Bin)
for i = 1:n
for j in find(A[i,:].== 1)
@constraint(StableB, x[i] + x[j] <= 1)
end
end
@objective(StableB, Max, sum(x))
solve(StableB)
return getobjectivevalue(StableB)
end
function StableSDP(A)
n = size(A,1)
StableDD = Model(solver = MosekSolver(MSK_IPAR_LOG = 0))
@variable(StableDD, X[1:n,1:n])
@SDconstraint(StableDD, X>=0)
@constraint(StableDD, X.>=0)
@constraint(StableDD, sum(sum(A.*X)) == 0 )
@constraint(StableDD, sum(X[i,i] for i=1:n) == 1)
@objective(StableDD, Max, sum(sum(X)))
solve(StableDD)
return getobjectivevalue(StableDD)
end
Возьмите это G в качестве примера.
A14 = [0 1 1 0 0 0 0 0 0 0 0 0 0 0;
1 0 0 1 0 0 0 0 0 0 0 0 0 0;
1 0 0 0 1 1 0 0 1 0 0 1 0 0;
0 1 0 0 1 1 0 0 1 0 0 1 0 0;
0 0 1 1 0 0 1 0 0 0 1 0 0 1;
0 0 1 1 0 0 0 1 0 0 1 0 0 1;
0 0 0 0 0 1 0 1 0 0 0 0 0 0;
0 0 0 0 0 1 1 0 0 0 0 0 0 0;
0 0 1 1 0 0 0 0 0 1 0 0 0 0;
0 0 0 0 0 0 0 0 1 0 1 0 0 0;
0 0 0 0 1 1 0 0 0 1 0 1 0 0;
0 0 1 1 0 0 0 0 0 0 1 0 1 0;
0 0 0 0 0 0 0 0 0 0 0 1 0 1;
0 0 0 0 1 1 0 0 0 0 0 0 1 0];
# α(G14) = 5
StableBin(A14)
# 松弛之后的解:5.694362954824882
StableSDP(A14)
5. Генерация ленивых констант: аппроксимация сфер L2 с кусочно-линейными функциями в целочисленном программировании
Мы указали, что Gurobi еще не поддерживает комбинацию целочисленного программирования и генерации столбцов (т. е. ветвления и цены), но пользовательский разрез поддерживает это (т. е. ветвления и разрезания)! Короче говоря, мы можем добавить только небольшое количество ограничений в начале, и будут добавлены нарушенные ограничения в процессе решения целочисленного программирования, поэтому это также называется ленивым обратным вызовом.
Этот пример исходит из:GitHub.com/Julia opt/ Согласно…
Задача, которую мы хотим решить, — это задача программирования конусов второго порядка с целочисленными ограничениями:
function solve_ball(c, Γ, ϵ=1e-6)
n = length(c)
m = Model(solver=GurobiSolver(OutputFlag=0))
# 初始consrtaint: 一个box
@variable(m, -Γ ≤ x[1:n] ≤ Γ, Int)
# 定义目标函数
@objective(m, Max, dot(c,x))
#核心:定义callback function,记录加入cut的数量
num_callbacks = 0
function norm_callback(cb)
num_callbacks += 1
N = getvalue(x)
# 求得当前x的L2 norm
L = norm(N)
# 如果足够小,说明已经得到一个可行解,即解最优
if L ≤ Γ + ϵ
return
end
# 不然的话,加入cut!注意这个cut将使得x变得不可行(infeasible),下步迭代必然会得到一个新的解
@lazyconstraint(cb, dot(N,x) ≤ Γ*L)
end
# 将callback函数加入JuMP/Gurobi模型
addlazycallback(m, norm_callback)
#求解
solve(m)
return getvalue(x), num_callbacks
end
#产生一个随机样例
srand(1234)
n = 2
c = rand(n)
Γ = 50.0
# 求解,输出callback次数
sol, num_callbacks = solve_ball(c, Γ)
println(sol)
println(norm(sol))
println(num_callbacks)
После 11 ленивых обратных вызовов мы получаем оптимальное решение. Далее еще один пример взаимодействия, мне лень делать здесь анимацию, просто дайте код. Пожалуйста, испытайте эффект на себе в блокноте Jupyter. Фактически это означает, что вы можете вручную настроить параметры на панели, чтобы увидеть оптимальное решение в разных ситуациях и изображение допустимой области на двухмерной плоскости.
# 这里展示Interact和Compose包,可用来进行交互
# 画出2D情况下的解
set_default_graphic_size(8cm, 8cm)
@manipulate for c1 in -1:0.1:+1, c2 in -1:0.1:+1, logϵ in -4:2
sol, _ = solve_ball([c1,c2], 100, 10.0^logϵ)
compose(context(),
compose(context(),
line([(0.5,0.5),(0.5+sol[1]/300,0.5+sol[2]/300)]),
Compose.stroke("black")),
compose(context(),
circle((0.5 + (100/norm(sol))*sol/300)...,0.02),
fill("red")),
compose(context(),circle(0.5,0.5,0.333),fill("lightblue"))
)
end
6. Общее нелинейное целочисленное программирование: задача о плотно упакованной сфере
Давайте рассмотрим такую задачу: есть k шаров радиуса r и прямоугольная коробка размером d1 × d2, можно ли как-то вместить эти шары в прямоугольник? Очевидно, более общая задача состоит в том, чтобы найти наибольшее k для любого прямоугольника. Однако эту общую проблему можно решить, последовательно решая (увеличивая k) предыдущую проблему решения, поэтому мы обсуждаем проблему решения для данного k.
определить переменную— координаты центра окружности по х и у. Тогда проблема принятия решения может быть решена с помощью следующей задачи невыпуклого нелинейного программирования, см. также: Биргин, Эрнесто Г., Дж. М. Мартинес и Дебора П. Ронкони. Оптимизация упаковка баллонов
в прямоугольный контейнер: нелинейный подход». Европейский журнал операционных исследований 160.1 (2005): 19-33.
Подобно идее из предыдущего раздела, мы также используем здесь кусочно-линейные функции и целочисленное программирование для аппроксимации функции L2. Конечно, здесь мы используем более общий подход и используем пакет PiecewiseLinear для облегчения завершения. В частности, мы рассматриваем целочисленное линейное программирование для аппроксимации невыпуклых ограничений в нелинейном программировании следующим образом.
# 画图
function PlotCirclesRectangle(p1,p2)
p = scatter((p2),(p1))
for i = 1:k
plot!(p,(p2[i]).+r*cos.(linspace(0,2*π,100)), (p1[i]).+r*sin.(linspace(0,2*π,100)), color = "blue", legend=false, aspect_ratio = 1 )
end
plot!(p,linspace(0,d2,100),zeros(100,1),color = "red")
plot!(p,zeros(100,1),linspace(0,d1,100),color = "red")
plot!(p,linspace(0,d2,100),ones(100,1)*d1,color = "red")
plot!(p,d2*ones(100,1),linspace(0,d1,100),color = "red")
p
end
# 考虑这样一组问题
r = 25
d1 = 160
d2 = 190
k = 11;
# MIO
function PackCirclesGen(r,d1,d2,k,Method,MaxTime)
tic()
PackCircles = Model(solver = GurobiSolver(MIPGap = 1e-3, OutputFlag = 0, TimeLimit = MaxTime))
@variable(PackCircles, p1[1:k]>=r)
@variable(PackCircles, p2[1:k]>=r)
@variable(PackCircles, s1[1:k,1:k])
@variable(PackCircles, s2[1:k,1:k]>=0)
@variable(PackCircles, z[1:k,1:k]>=0)
@constraint(PackCircles, p1.<=d1-r)
@constraint(PackCircles, p2.<=d2-r)
for i = 1:k
for j = i+1:k
if j - i > 2
continue
end
@constraint(PackCircles,s1[i,j] == p1[j]-p1[i])
@constraint(PackCircles,s2[i,j] == p2[j]-p2[i])
#使用PiecewiseLinearOpt包近似nonconvex constraint
fun_dist = piecewiselinear(PackCircles, s1[i,j] , s2[i,j], -(j-i)*r*2:r/5:(j-i)*r*2, 0:r/5:(j-i)*r*2, (u,v) -> (u^2+v^2)^0.5, method=Method)
@constraint(PackCircles, z[i,j] >= 2*r - fun_dist )
end
end
@objective(PackCircles, Min, sum(z[i,j] for i = 1:k for j = i+1:k))
solve(PackCircles)
return getvalue(p1),getvalue(p2),toc()
end
p1,p2,time = PackCirclesGen(r,d1,d2,k,:ZigZagInteger,Inf);
# 一个可行解
PlotCirclesRectangle(p1,p2)
Эту программу также можно использовать для исследования этого вопроса Чжиху:
Сколько кругов диаметром 1 может поместиться в прямоугольнике 2×1000?# 不同bivariate分段线性方法的比较
r = 21
d1 = 120
d2 = 120
k = 6;
Method_List = [:CC,:MC,:DisaggLogarithmic,:SOS2,:Logarithmic,:ZigZag,:ZigZagInteger]
# The experiment, note the cutoff time set as 600 seconds
for Method in Method_List
p1,p2,time = PackCirclesGen(r,d1,d2,k,Method,600)
print("The method ", Method, " uses ", time, " seconds!\n")
end
Мы заметили, что скорость расчета различных методов линейной кусочной аппроксимации сильно различается, ZigZag — самый быстрый, а такие формулировки, как CC/MC, вообще не рекомендуются. Подробный теоретический подход см.: Huchette, Joey, and Juan Pablo Vielma, «Невыпуклые кусочно-линейные функции: расширенные формулировки и простые инструменты моделирования».
7. Еще
Ссылка на этот учебник на GitHub, включая другие материалы о встрече, связанные с Джулией:
JuliaCN/MeetUpMaterials
Другие оптимизированные пакеты, поддерживаемые JuMP, можно найти здесь:www.juliaopt.org/packages/
Например, MultiJuMP.jl, который поддерживает многокритериальную оптимизацию, JuMPChance.jl, который поддерживает случайное ограничение, PolyJuMP.jl, который поддерживает полиномиальную оптимизацию и поддерживает стохастическое динамическое программирование, нелинейное управление, задачи ограничения равновесия и алгоритмы теории графов, пакет для метаэвристика и др.
Я также написал текст Джулии Амвей раньше:
Цинь Ханьчжан: Julia: эффективный и простой в использовании язык программирования для численных вычислений/оптимизацииПрактикуйтесь больше, пробуйте больше... Практика делает совершенным!
Любые вопросы или общение, добро пожаловать в контактя!