UndefVarError при оптимизации с использованием JuMP

Я пытаюсь закодировать проблему оптимизации в 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.

Вы уверены в [w[i]*arr_C[i] for i in 1:n_assets] в целевой функции? Кажется, вы хотите применить умножение матриц между w и arr_C, но код, который вы написали, не достигает этого. Думаю, лучше определить цель function. А если попробовать log10.(w'*arr_C') ?

Shayan 03.11.2022 13:58

@Шаян, привет! Да, точно. Я попробовал ваше предложение; Он бросил: ERROR: log10 is not defined for type GenericAffExpr. Are you trying to build a nonlinear problem? Make sure you use @NLconstraint/@NLobjective.

Shayan 03.11.2022 14:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
108
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно переформулировать ограничение как:

@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]), что не является матричным умножением. Я прав?

Shayan 03.11.2022 14:42

Это w_each_sim[i]*log(w[i]*arr_C[i]) for i in 1:n_assets) не является выражением умножения матриц.

Shayan 03.11.2022 14:45

О, я почему-то предположил, что arr_C — это вектор — возможно, требуется некоторое редактирование, чтобы точно соответствовать тому, что вы делаете. Вы можете расширить цикл, введя вторую переменную

Przemyslaw Szufel 03.11.2022 15:07

@PrzemyslawSzufel Я обновил первоначальное объяснение и добавил свою новую попытку в конце. Я буду признателен, если вы проверите это. Спасибо.

Shayan 03.11.2022 15:24

Это сводит меня с ума. Мы с Пшемыславом совершили ту же ошибку; discourse.julialang.org/t/optimization-using-jump/89720.

Oscar Dowson 03.11.2022 21:55

В коде нет входных данных, поэтому человек смотрит на переменные и пытается что-то придумать :)

Przemyslaw Szufel 03.11.2022 23:26
Ответ принят как подходящий

Об этом спросили на форуме сообщества 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)

Другие вопросы по теме