Я пытаюсь закодировать проблему оптимизации в Julia, используя JuMP
. Целевая функция имеет два умножения матриц.
Сначала умножьте вектор w
размера (10) на матрицу arr_C
размера (20, 10). Таким образом, w
следует преобразовать в размер (1, 10), чтобы выполнить матричное умножение.
Во-вторых, умножьте вектор w_each_sim
размера (20) на результат первого умножения, который также является вектором размера (20). Таким образом, умножение должно быть как (1x20) x (20x1), чтобы получить скаляр. Пожалуйста, прочитайте до последней строки вопроса, потому что я применил обновления в соответствии с предложениями.
Моя первая попытка выглядит следующим образом:
julia> using JuMP, Ipopt
julia> a = rand(20, 10);
julia> b = rand(20); b = b./sum(b)
julia> function port_opt(
n_assets::Int8,
arr_C::Matrix{Float64},
w_each_sim::Vector{Float64})
"""
Calculate weight of each asset through optimization
Parameters
----------
n_assets::Int8 - number of assets
arr_C::Matrix{Float64} - array of C
w_each_sim::Vector{Float64} - weights of each similar TW
Returns
-------
w_opt::Vector{Float64} - weights of each asset
"""
model = Model(Ipopt.Optimizer)
@variable(model, 0<= w[1:n_assets] <=1)
@NLconstraint(model, sum([w[i] for i in 1:n_assets]) == 1)
@NLobjective(model, Max, w_each_sim * log10.([w[i]*arr_C[i] for i in 1:n_assets]))
optimize!(model)
@show value.(w)
return value.(w)
end
julia> port_opt(Int8(10), a, b)
ERROR: UndefVarError: i not defined
Stacktrace:
[1] macro expansion
@ C:\Users\JL\.julia\packages\JuMP\Z1pVn\src\macros.jl:1834 [inlined]
[2] port_opt(n_assets::Int8, arr_C::Matrix{Float64}, w_each_sim::Vector{Float64})
@ Main e:\MyWork\paperbase.jl:237
[3] top-level scope
@ REPL[4]:1
Проблема с линией @NLconstraint
.
Как я могу заставить этот код работать и выполнить оптимизацию?
Как предложил @Shayan, я исправил целевую функцию следующим образом:
function Obj(w, arr_C, w_each_sim)
first_expr = w'*arr_C'
second_expr = map(first_expr) do x
log10(x)
end
return w_each_sim * second_expr
end
function port_opt(
n_assets::Int8,
arr_C::Matrix{Float64},
w_each_sim::Vector{Float64})
....
....
@NLconstraint(model, sum(w[i] for i in 1:n_assets) == 1)
@NLobjective(model, Max, Obj(w, arr_C, w_each_sim))
optimize!(model)
@show value.(w)
return value.(w)
end
a, b = rand(20, 10), rand(20); b = b./sum(b);
port_opt(Int8(10), a, b)
# Threw this:
ERROR: Unexpected array VariableRef[w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], w[9], w[10]] in nonlinear expression. Nonlinear expressions may contain only scalar expressions.
Теперь, основываясь на предложениях @PrzemyslawSzufel, я попробовал это:
function Obj(w, arr_C::Matrix{T}, w_each_sim::Vector{T}) where {T<:Real}
first_expr = zeros(T, length(w_each_sim))
for i∈size(w_each_sim, 1), j∈eachindex(w)
first_expr[i] += w[j]*arr_C[i, j]
end
second_expr = map(first_expr) do x
log(x)
end
res = 0
for idx∈eachindex(w_each_sim)
res += w_each_sim[idx]*second_expr[idx]
end
return res
end
function port_opt(
n_assets::Int8,
arr_C::Matrix{Float64},
w_each_sim::Vector{Float64})
model = Model()
@variable(model, 0<= w[1:n_assets] <=1)
@NLconstraint(model, +(w...) == 1)
register(model, :Obj, Int64(n_assets), Obj, autodiff=true)
@NLobjective(model, Max, Obj(w, arr_C, w_each_sim))
optimize!(model)
@show value.(w)
return value.(w)
end
a, b = rand(20, 10), rand(20); b = b./sum(b);
port_opt(Int8(10), a, b)
# threw this
ERROR: Unable to register the function :Obj because it does not support differentiation via ForwardDiff.
Common reasons for this include:
* the function assumes `Float64` will be passed as input, it must work for any
generic `Real` type.
* the function allocates temporary storage using `zeros(3)` or similar. This
defaults to `Float64`, so use `zeros(T, 3)` instead.
@Шаян, привет! Да, точно. Я попробовал ваше предложение; Он бросил: ERROR: log10 is not defined for type GenericAffExpr. Are you trying to build a nonlinear problem? Make sure you use @NLconstraint/@NLobjective.
Вам нужно переформулировать ограничение как:
@constraint(model, sum([w[i] for i in 1:n_assets]) == 1)
или
@NLconstraint(model, +(w...) == 1)
Что касается цели, указанной в комментариях, что-то не так с векторным умножением. То, что вы хотите иметь, наиболее вероятно (и это работает, и модель решается):
@NLobjective(model, Max,sum(w_each_sim[i]*log(w[i]*arr_C[i]) for i in 1:n_assets))
Обновлено:
далее по тому же вопросу: https://discourse.julialang.org/t/optimization-using-jump/89720
Спасибо! Чтобы убедиться, что это именно то, что должно быть сформулировано, результатом умножения w*arr_C
должен быть вектор, поскольку w
— это вектор, а arr_C
— матрица. Тогда результатом w_each_sim*<RESULT OF PREVIOUS EXPRESSION>
должен быть скаляр. Но вы написали w[i]*arr_C[i])
, что не является матричным умножением. Я прав?
Это w_each_sim[i]*log(w[i]*arr_C[i]) for i in 1:n_assets)
не является выражением умножения матриц.
О, я почему-то предположил, что arr_C
— это вектор — возможно, требуется некоторое редактирование, чтобы точно соответствовать тому, что вы делаете. Вы можете расширить цикл, введя вторую переменную
@PrzemyslawSzufel Я обновил первоначальное объяснение и добавил свою новую попытку в конце. Я буду признателен, если вы проверите это. Спасибо.
Это сводит меня с ума. Мы с Пшемыславом совершили ту же ошибку; discourse.julialang.org/t/optimization-using-jump/89720.
В коде нет входных данных, поэтому человек смотрит на переменные и пытается что-то придумать :)
Об этом спросили на форуме сообщества JuMP: https://discourse.julialang.org/t/optimization-using-jump/89720
Кросс-постинг моего решения:
using JuMP, Ipopt
a = rand(20, 10)
b = rand(20); b = b./sum(b)
"""
Calculate weight of each asset through optimization
Parameters
----------
n_assets::Int8 - number of assets
arr_C::Matrix{Float64} - array of C
w_each_sim::Vector{Float64} - weights of each similar TW
Returns
-------
w_opt::Vector{Float64} - weights of each asset
"""
function port_opt(
n_assets::Int8,
arr_C::Matrix{Float64},
w_each_sim::Vector{Float64},
)
model = Model(Ipopt.Optimizer)
@variable(model, 0<= w[1:n_assets] <=1)
@NLconstraint(model, sum(w[i] for i in 1:n_assets) == 1)
@NLobjective(
model,
Max,
sum(
w_each_sim[i] * log10(sum(w[j] * arr_C[i, j] for j in 1:size(arr_C, 2)))
for i in 1:length(w_each_sim)
)
)
optimize!(model)
return value.(w)
end
port_opt(Int8(10), a, b)
Вы уверены в
[w[i]*arr_C[i] for i in 1:n_assets]
в целевой функции? Кажется, вы хотите применить умножение матриц междуw
иarr_C
, но код, который вы написали, не достигает этого. Думаю, лучше определить цельfunction
. А если попробоватьlog10.(w'*arr_C')
?