Evaluation and Derivatives
This guide covers objective evaluation, registered analytical derivatives, output preallocation, and numeric types. Exact signatures and all constraint evaluation methods are listed in the API Reference.
Evaluate objectives and derivatives
Construct the benchmark and evaluate all of its objectives with eval_f:
julia> using MOProblems
julia> using Random
julia> prob = ZDT1();
julia> lower, upper = recommended_bounds(prob);
julia> rng = MersenneTwister(1234);
julia> α = rand(rng, prob.nvar);
julia> x = lower .+ α .* (upper .- lower);
julia> values = eval_f(prob, x)
2-element Vector{Float64}:
0.5383210129299967
3.359922200388914The objective vector has length prob.nobj. The Jacobian holds one objective gradient per row, so its size is (prob.nobj, prob.nvar):
julia> J = eval_jacobian(prob, x);
julia> size(J)
(2, 30)Use eval_jacobian_row(prob, x, i) when only objective i is needed; it returns that gradient without forming the full matrix:
julia> eval_jacobian_row(prob, x, 1) == J[1, :]
trueConstrained benchmarks expose the same pattern through eval_c, eval_constraint_jacobian, and eval_constraint_jacobian_row.
Registered analytical derivatives
Analytical Jacobians and Hessians are available only when a benchmark registers them. ProblemMeta records the registration and filter_problems queries it, so derivative support can be checked before any instance is constructed. No ZDT problem registers a Hessian:
julia> filter_problems(name_pattern = r"^ZDT", has_hessian = true)
String[]
julia> META["ZDT1"].has_hessian
falseCalling a derivative evaluator that the benchmark does not register raises an explicit error, and it does so before x or the output buffers are validated:
julia> eval_hessian(prob, x)
ERROR: Analytical Hessian is not registered for problem 'ZDT1'.AP1 does register Hessians. eval_hessian returns one matrix per objective, each of size (nvar, nvar):
julia> ap = AP1();
julia> xap = fill(0.5, ap.nvar);
julia> H = eval_hessian(ap, xap);
julia> length(H), size(H[1])
(3, (2, 2))
julia> H[1]
2×2 Matrix{Float64}:
0.75 0.0
0.0 13.5For a single objective, eval_hessian_row(ap, xap, i) returns only the i-th matrix. Constraint Hessians follow the same pair of methods, eval_constraint_hessian and eval_constraint_hessian_row.
Reuse output buffers
Every allocating evaluator has an in-place counterpart whose name ends in !. It writes into a buffer supplied by the caller and returns that same buffer, so a routine that evaluates one benchmark repeatedly can allocate its outputs once and reuse them at every point:
julia> y = Vector{Float64}(undef, prob.nobj);
julia> eval_f!(y, prob, x) === y
true
julia> y == values
trueThe Jacobian buffer is a matrix, and the buffer for a single row is a vector:
julia> Jbuf = Matrix{Float64}(undef, prob.nobj, prob.nvar);
julia> eval_jacobian!(Jbuf, prob, x) == J
true
julia> row = Vector{Float64}(undef, prob.nvar);
julia> eval_jacobian_row!(row, prob, x, 1) == J[1, :]
trueBecause eval_hessian returns one matrix per objective, its buffer is a vector of matrices, allocated once before the loop that consumes it:
julia> Hs = [Matrix{Float64}(undef, ap.nvar, ap.nvar) for _ in 1:ap.nobj];
julia> eval_hessian!(Hs, ap, xap) === Hs
true
julia> Hs == H
trueA single objective needs only one matrix:
julia> Hbuf = Matrix{Float64}(undef, ap.nvar, ap.nvar);
julia> eval_hessian_row!(Hbuf, ap, xap, 1) == H[1]
trueThe buffer shapes are fixed by the problem dimensions:
| Method | Buffer | Shape |
|---|---|---|
eval_f! | vector | prob.nobj |
eval_c! | vector | prob.ncon |
eval_jacobian! | matrix | (prob.nobj, prob.nvar) |
eval_constraint_jacobian! | matrix | (prob.ncon, prob.nvar) |
eval_jacobian_row!, eval_constraint_jacobian_row! | vector | prob.nvar |
eval_hessian! | vector of matrices | prob.nobj matrices of (prob.nvar, prob.nvar) |
eval_constraint_hessian! | vector of matrices | prob.ncon matrices of (prob.nvar, prob.nvar) |
eval_hessian_row!, eval_constraint_hessian_row! | matrix | (prob.nvar, prob.nvar) |
A buffer whose shape does not match raises a DimensionMismatch, and its element type must equal the element type of x.
Numeric types
The input vector controls the numeric type of allocated objective and derivative outputs:
julia> x32 = fill(0.5f0, prob.nvar);
julia> eltype(eval_f(prob, x32))
Float32
julia> eltype(eval_jacobian(prob, x32))
Float32Both outputs have Float32 elements. Caller-provided buffers must use the same type, so a buffer allocated for Float64 cannot be reused for a Float32 evaluation of the same problem.
Domain restrictions
Registration means that an analytical evaluator is provided; it does not assert differentiability at every boundary point of the benchmark domain. Family pages document restrictions, and an evaluator may throw a DomainError at a point where the requested derivative is undefined.