2017-06-22 4 views
2

J'essaie d'utiliser JuMP pour résoudre un problème non linéaire, où le nombre de variables est décidé par l'utilisateur - c'est-à-dire, inconnu au moment de la compilation.Julia + JuMP: nombre variable d'arguments à utiliser

Pour ce faire, la ligne @NLobjective ressemble à ceci:

@eval @JuMP.NLobjective(m, Min, $(Expr(:call, :myf, [Expr(:ref, :x, i) for i=1:n]...))) 

Lorsque, par exemple, si n=3, le compilateur interprète la ligne comme identique à:

@JuMP.NLobjective(m, Min, myf(x[1], x[2], x[3])) 

Le problème est que @eval ne fonctionne que dans la portée globale, et lorsqu'il est contenu dans une fonction, une erreur est levée.

Ma question est: comment puis-je accomplir cette même fonctionnalité - obtenir @NLobjective appeler myf avec un nombre variable d'arguments x[1],...,x[n] - dans le cadre d'une fonction locale, non-at connu compilation?

def testme(n) 
    myf(a...) = sum(collect(a).^2) 

    m = JuMP.Model(solver=Ipopt.IpoptSolver()) 

    JuMP.register(m, :myf, n, myf, autodiff=true) 
    @JuMP.variable(m, x[1:n] >= 0.5) 

    @eval @JuMP.NLobjective(m, Min, $(Expr(:call, :myf, [Expr(:ref, :x, i) for i=1:n]...))) 
    JuMP.solve(m) 
end 

testme(3) 

Merci!

+1

Cela ne contribue pas 'JuMP', mais si vous avez vraiment coincé,' NLopt' permet le nombre d'arguments à ne pas connu à la compilation, et l'API est relativement simple. –

+0

Merci! Je vais regarder dedans. –

Répondre

3

Comme expliqué dans http://jump.readthedocs.io/en/latest/nlp.html#raw-expression-input, les fonctions d'objectif peuvent être données sans la macro. L'expression pertinente:

JuMP.setNLobjective(m, :Min, Expr(:call, :myf, [x[i] for i=1:n]...)) 

est encore plus simple que celui basé @eval et travaille dans la fonction. Le code est:

using JuMP, Ipopt 

function testme(n) 
    myf(a...) = sum(collect(a).^2) 

    m = JuMP.Model(solver=Ipopt.IpoptSolver()) 

    JuMP.register(m, :myf, n, myf, autodiff=true) 
    @JuMP.variable(m, x[1:n] >= 0.5) 

    JuMP.setNLobjective(m, :Min, Expr(:call, :myf, [x[i] for i=1:n]...)) 
    JuMP.solve(m) 
    return [getvalue(x[i]) for i=1:n] 
end 

testme(3) 

et il retourne:

julia> testme(3) 

: 

EXIT: Optimal Solution Found. 
3-element Array{Float64,1}: 
0.5 
0.5 
0.5 
+2

C'est une belle astuce, je n'y avais même pas pensé. Pour ceux qui lisent ceci, je déconseille d'utiliser 'autodiff = true' avec des fonctions d'entrée de grande dimension. La mise en œuvre actuelle utilise le mode avancé AD qui ne s'adapte pas bien à mesure que la dimension d'entrée augmente. – mlubin

+0

@mlubin Quelles sont les alternatives pour 'autodiff'? –

+0

[ReverseDiff.jl] (https://github.com/JuliaDiff/ReverseDiff.jl) est une bonne implémentation en mode inverse que vous pouvez utiliser pour fournir une fonction de dégradé à JuMP. – mlubin