Hopefully this helps:
using JuMP, Ipopt
M, N = 3, 5
struct myPoly
vec::Vector{Int}
end
function (f::myPoly)(x)
ret = 0.0
for v in f.vec
ret = x * ret + v
end
return ret
end
bas = [myPoly(collect(1:j)) for j in 1:N]
samp = range(0, 1; length=M)
model = Model(Ipopt.Optimizer)
@variable(model, x[1:N])
# Option 1: if you are only using linear or quadratic terms:
@objective(
model,
Min,
sum((bas[j](samp[i]) * x[j])^2 for j in 1:N, i in 1:M),
)
# Option 2: if you are using higher-order terms:
coefficients = [bas[j](samp[i]) for j in 1:N, i in 1:M]
@NLobjective(
model,
Min,
sum((coefficients[j, i] * x[j])^2 for j in 1:N, i in 1:M),
)
Why? @NL... macros don’t like function calls like bas[j](...) because we
need to construct and expression graph based only on the syntax (we don’t get to
see the value of bas[j] until after we have constructed the expression
graph). As a work-around, store all the coefficients in a matrix (or other data
structure that doesn’t involve function calls) and index into the data structure.
@objective (and @constraint) doesn’t have this problem because it works with the values of things, rather than on the syntax. It’s also one reason why the @NL macros are “different” to the rest of JuMP.