User-defined (nonlinear) objective with vectorized variables in JuMP - julia

Is it possible to use vectorized variables with user-defined objective functions in JuMP for Julia? Like so,
model = Model(GLPK.Optimizer)
A = [
1 1 9 5
3 5 0 8
2 0 6 13
]
b = [7; 3; 5]
c = [1; 3; 5; 2]
#variable(model, x[1:4] >= 0)
#constraint(model, A * x .== b)
# dummy functions, could be nonlinear hypothetically
identity(x) = x
C(x, c) = c' * x
register(model, :identity, 1, identity; autodiff = true)
register(model, :C, 2, C; autodiff = true)
#NLobjective(model, Min, C(identity(x), c))
This throws the error,
ERROR: Unexpected array VariableRef[x[1], x[2], x[3], x[4]] in nonlinear expression. Nonlinear expressions may contain only scalar expression.
Which sounds like no. Is there a workaround to this? I believe scipy.optimize.minimize is capable of optimizing user-defined objectives with vectorized variables?

No, you cannot pass vector arguments to user-defined functions.
Documentation: https://jump.dev/JuMP.jl/stable/manual/nlp/#User-defined-functions-with-vector-inputs
Issue you opened: https://github.com/jump-dev/JuMP.jl/issues/2854
The following is preferable to Prezemyslaw's answer. His suggestion to wrap things in an #expression won't work if the functions are more complicated.
using JuMP, Ipopt
model = Model(Ipopt.Optimizer)
A = [
1 1 9 5
3 5 0 8
2 0 6 13
]
b = [7; 3; 5]
c = [1; 3; 5; 2]
#variable(model, x[1:4] >= 0)
#constraint(model, A * x .== b)
# dummy functions, could be nonlinear hypothetically
identity(x) = x
C(x, c) = c' * x
my_objective(x...) = C(identitiy(collect(x)), c)
register(model, :my_objective, length(x), my_objective; autodiff = true)
#NLobjective(model, Min, my_objective(x...))

Firstly, use optimizer that supports nonlinear models. GLPK does not. Try Ipopt:
using Ipopt
model = Model(Ipopt.Optimizer)
Secondly, JuMP documentation reads (see https://jump.dev/JuMP.jl/stable/manual/nlp/#Syntax-notes):
The syntax accepted in nonlinear macros is more restricted than the syntax for linear and quadratic macros. (...) all expressions must be simple scalar operations. You cannot use dot, matrix-vector products, vector slices, etc.
you need wrap the goal function
#expression(model, expr, C(identity(x), c))
Now you can do:
#NLobjective(model, Min, expr)
To show that it works I solve the model:
julia> optimize!(model)
This is Ipopt version 3.14.4, running with linear solver MUMPS 5.4.1.
...
Total seconds in IPOPT = 0.165
EXIT: Optimal Solution Found.
julia> value.(x)
4-element Vector{Float64}:
0.42307697548737005
0.3461538282496562
0.6923076931757742
-8.46379887234798e-9

Related

Solve linear Equation System

I have two (mathematical) functions:
y = x
y = -2x + 3
This is solved by y = 1 and x = 1. See picture:
How can I make Julia do this for me?
This is a set of linear equations so first rearrange them in the following way:
-x + y = 0
2x + y = 3
and you see that they are in the form of a linear equation system A*v=b where. A is a matrix:
julia> A = [-1 1; 2 1]
2×2 Array{Int64,2}:
-1 1
2 1
and b is a vector:
julia> b = [0, 3]
2-element Array{Int64,1}:
0
3
Now v contains your unknown variables x and y. You can now solve the system using the left division operator \:
julia> A\b
2-element Array{Float64,1}:
1.0
1.0
If you had a more general system of non-linear equations you should use NLsolve.jl package:
julia> using NLsolve
julia> function f!(F, v)
x = v[1]
y = v[2]
F[1] = -x + y
F[2] = 2*x + y - 3
end
f! (generic function with 1 method)
julia> res = nlsolve(f!, [0.0; 0.0])
Results of Nonlinear Solver Algorithm
* Algorithm: Trust-region with dogleg and autoscaling
* Starting Point: [0.0, 0.0]
* Zero: [1.0000000000003109, 0.9999999999999647]
* Inf-norm of residuals: 0.000000
* Iterations: 2
* Convergence: true
* |x - x'| < 0.0e+00: false
* |f(x)| < 1.0e-08: true
* Function Calls (f): 3
* Jacobian Calls (df/dx): 3
julia> res.zero
2-element Array{Float64,1}:
1.0000000000003109
0.9999999999999647
(note that in f! we define two outputs F[1] and F[2] to be equal to zero - you have to rearrange your equations in this way).
For more details how to use NLsolve.jl see https://github.com/JuliaNLSolvers/NLsolve.jl.
Mr #bogumił-kamiński gave an excellent answer. However, just a friendly reminder, the solution MAY NOT EXIST for some other system of linear equations. In that case, you'll get a SingularException. Consider checking if the solution exists or not. For example,
using LinearAlgebra;
"""
y = x => x - y = 0 => |1 -1| X = |0| => AX = B => X = A⁻¹B
y = -2x + 3 2x + y = 3 |2 1| |3|
"""
function solution()
A::Matrix{Int64} = Matrix{Int64}([1 -1; 2 1]);
br::Matrix{Int64} = Matrix{Int64}([0 3]);
bc = transpose(br);
# bc::Matrix{Int64} = Matrix{Int64}([0 ;3;;]); # I missed a semicolon, that's why I got an error
# println(bc);
if(det(A) == 0) # existence check
println("soln doesnot exist. returning...");
return
end
A⁻¹ = inv(A);
println("solution exists:")
X = A⁻¹ * bc;
println(X);
end
solution();

"Syntax error" while writing OCaml function?

I am writing a function in OCaml to raise x to the power of y.
My code is:
#let rec pow x y =
if y == 0 then 1 else
if (y mod 2 = 0) then pow x y/2 * pow x y/2 else
x * pow x y/2 * pow x y/2;;
When I try to execute it, I get an error for syntax in line one, but it doesn't tell me what it is.
When you wrote the code, did you type the #? The # is just a character that the OCaml REPL outputs to prompt for input; it is not part of the code. You should not type it.
Here are some other errors that you should fix:
== is physical equality in OCaml. = is structural equality. Although both work the same for unboxed types (such as int), it's better practice to do y = 0. Note that you use =, the recommended equality, in the expression y mod 2 = 0.
You need parentheses around y/2. pow x y/2 parses as (pow x y) / 2, but you want pow x (y / 2).

Load Error when trying to pass complicated function into Simpson's rule

I have written a method that approximates a definite integral by the composite Simpson's rule.
#=
f integrand
a lower integration bound
b upper integration bound
n number of iterations or panels
h step size
=#
function simpson(f::Function, a::Number, b::Number, n::Number)
n % 2 == 0 || error("`n` must be even")
h = (b - a) / n
s = f(a) + f(b)
s += 4*sum(f(a .+ collect(1:2:n) .* h))
s += 2*sum(f(a .+ collect(2:2:n-1) .* h))
return h/3 * s
end
For "simple" functions, like e^(-x^2), the simpson function works.
Input: simpson(x -> simpson(x -> exp.(-x.^2), 0, 5, 100)
Output: 0.8862269254513949
However, for the more complicated function f(x)
gArgs(x) = (30 .+ x, 0)
f(x) = exp.(-x.^2) .* maximum(generator.(gArgs.(x)...)[1])
where generator(θ, plotsol) is a function that takes in a defect θ in percent and a boolean value plotsol (either 0 or 1) that determines whether the generator should be plotted, and returns a vector with the magnetization in certain points in the generator.
When I try to compute the integral by running the below code
gArgs(x) = (30 .+ x, 0)
f(x) = exp.(-x.^2) .* maximum(generator.(gArgs.(x)...)[1])
println(simpson(x -> f(x), 0, 5, 10))
I encounter the error MethodError: no method matching generator(::Float64). With slight variants of the expression for f(x) I run into different errors like DimensionMismatch("array could not be broadcast to match destination") and InexactError: Bool(33.75). In the end, I think the cause of the error boils down to that I cannot figure out how to properly enter an expression for the integrand f(x). Could someone help me figure out how to enter f(x) correctly? Let me know if anything is unclear in my question.
Given an array x , gArgs.(x) returns an array of Tuples and you are trying to broadcast over an array of tuples. But the behavior of broadcasting with tuples is a bit different. Tuples are not treated as a single element and they themselves broadcast.
julia> println.(gArgs.([0.5, 1.5, 2.5, 3.5, 4.5])...)
30.531.532.533.534.5
00000
This is not what you expected, is it?
You can also see the problem with the following example;
julia> (2, 5) .!= [(2, 5)]
2-element BitArray{1}:
true
true
I believe f is a function that actually takes a scalar and returns a scalar. Instead of making f work on arrays, you should leave the broadcasting to the caller. You are very likely to be better of implementing f element-wise. This is the more Julia way of doing things and will make your job much easier.
That said, I believe your implementation should work with the following modifications, if you do not have an error in generator.
function simpson(f::Function, a::Number, b::Number, n::Number)
n % 2 == 0 || error("`n` must be even")
h = (b - a) / n
s = f(a) + f(b)
s += 4*sum(f.(a .+ collect(1:2:n) .* h)) # broadcast `f`
s += 2*sum(f.(a .+ collect(2:2:n-1) .* h)) # broadcast `f`
return h/3 * s
end
# define `gArg` and `f` element-wise and `generator`, too.
gArgs(x) = (30 + x, 0) # get rid of broadcasting dot. Shouldn't `0` be `false`?
f(x) = exp(-x^2) * maximum(generator(gArgs(x)...)[1]) # get rid of broadcasting dots
println(simpson(f, 0, 5, 10)) # you can just write `f`
You should also define the generator function element-wise.

Prolog:: f(x) recursion

I'm a beginner to Prolog and have two requirements:
f(1) = 1
f(x) = 5x + x^2 + f(x - 1)
rules:
f(1,1).
f(X,Y) :-
Y is 5 * X + X * X + f(X-1,Y).
query:
f(4,X).
Output:
ERROR: is/2: Arguments are not sufficiently instantiated
How can I add value of f(X-1)?
This can be easily solved by using auxiliary variables.
For example, consider:
f(1, 1).
f(X, Y) :-
Y #= 5*X + X^2 + T1,
T2 #= X - 1,
f(T2, T1).
This is a straight-forward translation of the rules you give, using auxiliary variables T1 and T2 which stand for the partial expressions f(X-1) and X-1, respectively. As #BallpointBen correctly notes, it is not sufficient to use the terms themselves, because these terms are different from their arithmetic evaluation. In particular, -(2,1) is not the integer 1, but 2 - 1 #= 1 does hold!
Depending on your Prolog system, you may ned to currently still import a library to use the predicate (#=)/2, which expresses equality of integer expressesions.
Your example query now already yields a solution:
?- f(4, X).
X = 75 .
Note that the predicate does not terminate universally in this case:
?- f(4, X), false.
nontermination
We can easily make it so with an additional constraint:
f(1, 1).
f(X, Y) :-
X #> 1,
Y #= 5*X + X^2 + T1,
T2 #= X - 1,
f(T2, T1).
Now we have:
?- f(4, X).
X = 75 ;
false.
Note that we can use this as a true relation, also in the most general case:
?- f(X, Y).
X = Y, Y = 1 ;
X = 2,
Y = 15 ;
X = 3,
Y = 39 ;
X = 4,
Y = 75 ;
etc.
Versions based on lower-level arithmetic typically only cover a very limited subset of instances of such queries. I therefore recommend that you use (#=)/2 instead of (is)/2. Especially for beginners, using (is)/2 is too hard to understand. Take the many related questions filed under instantiation-error as evidence, and see clpfd for declarative solutions.
The issue is that you are trying to evaluate f(X-1,Y) as if it were a number, but of course it is a predicate that may be true or false. After some tinkering, I found this solution:
f(1,1).
f(X,Y) :- X > 0, Z is X-1, f(Z,N), Y is 5*X + X*X + N.
The trick is to let it find its way down to f(1,N) first, without evaluating anything; then let the results bubble back up by satisfying Y is 5*X + X*X + N. In Prolog, order matters for its search. It needs to satisfy f(Z,N) in order to have a value of N for the statement Y is 5*X + X*X + N.
Also, note the condition X > 0 to avoid infinite recursion.

Prolog: Variable with multiple values

I'm trying to implement a program that takes a variable with multiple values and evaluates all the values. For instance:
foo(X,R) :-
X > 2,
Z is R + 1,
R = Z.
This program might not be valid, but it will help me ask the question regardless.
My question: If X has multiple values, how would I increment the counter for each value X > 2?
In order to instantiate X to increasingly larger integers you can use the following:
?- between(0, inf, X).
X = 0 ;
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
<ETC.>
PS1: Notice that you have to instantiate R as well since it is used in the arithmetic expression Z is R + 1.
PS2: Notice that your program fails for all instantiations of X and R since R =\= R + 1 for finite R. The for instance means that the following query will not terminate:
?- between(0, inf, X), foo(X, 1).
Alternatively, the program can be rewritten in library CLP(FD) (created by Markus Triska):
:- use_module(library(clpfd)).
foo(X,R):-
X #> 2,
Z #= R + 1,
R #= Z.

Resources