ExaModels

ExaModels.ExaModelsModule
ExaModels

An algebraic modeling and automatic differentiation tool in Julia, specialized for SIMD-parallel evaluation of nonlinear programs on CPUs and GPUs.

Computation graph

Expressions are represented as trees of AbstractNode subtypes:

  • Var / DataIndexed — decision variables and data fields
  • Node1 / Node2 — unary / binary operations
  • Constant{T} — compile-time scalar constant; the value T is stored as a type parameter so it is visible to the compiler and to juliac --trim=safe without any runtime storage.
  • SumNode / ProdNode — reduction over a tuple of nodes

Algebraic simplification

Operations involving Constant{T} are simplified at model-construction time: x * Constant(1) → x, x ^ Constant(2) → abs2(x), etc. (see specialization.jl). Combined Constant OP Constant expressions are folded to a new Constant via the rules in register.jl.

For more information, please visit https://github.com/exanauts/ExaModels.jl

source
ExaModels.AbstractNodeType
AbstractNode

Root abstract type for all nodes in the ExaModels symbolic computation graph. Every node is callable as node(i, x, θ) and returns the scalar value of the expression at the current evaluation point, where i is the iteration index, x the primal variable vector, and θ the parameter vector.

source
ExaModels.AdjointNode1Type
AdjointNode1{F, T, I} <: AbstractAdjointNode

Gradient-pass node for a unary operation F. Stores the primal value and the partial derivative alongside the child so a single forward sweep simultaneously computes both.

Fields

  • x::T: primal value F(inner.x)
  • y::T: derivative F'(inner.x)
  • inner::I: child node
source
ExaModels.AdjointNode2Type
AdjointNode2{F, T, I1, I2} <: AbstractAdjointNode

Gradient-pass node for a binary operation F. Stores the primal value and both partial derivatives.

Fields

  • x::T: primal value F(inner1.x, inner2.x)
  • y1::T: partial derivative w.r.t. first argument
  • y2::T: partial derivative w.r.t. second argument
  • inner1::I1: left child
  • inner2::I2: right child
source
ExaModels.AdjointNodeSourceType
AdjointNodeSource{VT}

Factory for AdjointNodeVar leaves. Indexing with i returns AdjointNodeVar(i, inner[i]), seeding the gradient-pass tree with the current primal value.

Fields

  • inner::VT: primal variable vector (or nothing for a zero-valued seed)
source
ExaModels.AdjointNodeVarType
AdjointNodeVar{I, T} <: AbstractAdjointNode

Leaf node in the gradient-pass tree corresponding to a decision variable.

Fields

  • i::I: variable index
  • x::T: primal value x[i] at the current evaluation point
source
ExaModels.AdjointNullType
AdjointNull{V} <: AbstractAdjointNode

Leaf node in the first-order adjoint tree representing a constant (zero contribution to the gradient). Carries the primal value x so that upstream nodes can read it without recomputation.

source
ExaModels.CompressedNLPModelType
CompressedNLPModel

A wrapper around an AbstractNLPModel that sums duplicate (row, col) entries in the sparse Jacobian and Hessian.

Duplicates arise when multiple constraint or objective patterns contribute to the same matrix position (e.g. after augmentation via add_con!). CompressedNLPModel detects them once at construction and accumulates them on every subsequent jac_coord! / hess_coord! call, so callers receive a matrix with no repeated coordinates.

Construct via CompressedNLPModel(m).

source
ExaModels.CompressedNLPModelMethod
CompressedNLPModel(m)

Wraps m in a CompressedNLPModel.

Queries the full Jacobian and Hessian sparsity patterns from m, identifies duplicate (row, col) pairs, builds pointer arrays for O(nnz) accumulation on subsequent jac_coord! / hess_coord! calls.

source
ExaModels.CompressorType
Compressor{I}

Data structure for the sparse index

Fields:

  • inner::I: stores the sparse index as a tuple form
source
ExaModels.ConAugPairType
ConAugPair{C, P}

Carries a constraint reference (con) alongside its Pair(idx, expr) during the probe phase of add_con!(core, g[i] += expr for ...). The replace_T method unwraps to the inner Pair, so SIMDFunction stores a plain Pair and all existing offset0 dispatches remain unchanged.

source
ExaModels.ConstantType
Constant{T} <: AbstractNode

A compile-time constant node whose value T is embedded as a type parameter.

Design rationale

Because T is a type parameter rather than a struct field, the Julia compiler (and juliac --trim=safe) can propagate T concretely through every downstream Node1 / Node2 type, making the entire computation graph type-stable with no runtime storage for the constant.

Construction

Constant(2)      # → Constant{2}()
Constant(3.14)   # → Constant{3.14}()

Evaluation

c = Constant(42)
c(i, x, θ)  # → 42

Algebraic simplification

specialization.jl registers identities for Constant combined with AbstractNode operands:

expressionsimplification
x + Constant(0)x
x * Constant(1)x
x / Constant(1)x
x ^ Constant(0)Constant(1)
x ^ Constant(1)x
x ^ Constant(2)Node1(abs2, x)
x ^ Constant(-1)inv(x)
Constant(0) * xConstant(0)
Constant(0) / xConstant(0)

These rules fire at model-construction time (when the ExaCore is being built), collapsing trivial branches before the graph is finalized.

source
ExaModels.ConstraintType
Constraint

A block of constraints added to an ExaCore via add_con / @add_con. Each element of the iterator corresponds to one constraint row. Row k of this block maps to global constraint index offset + k. Dual solution values can be retrieved with multipliers. An optional tag field carries user-defined metadata.

source
ExaModels.ConstraintAugmentationType
ConstraintAugmentation

An augmentation layer added to an existing Constraint via add_con! / @add_con!. Each element of the iterator yields an idx => expr pair: expr is accumulated into the constraint row identified by idx at evaluation time. Multiple ConstraintAugmentation objects can be stacked on the same base constraint to aggregate contributions from several data sources (e.g. summing arc flows into nodal balance constraints). An optional tag field carries user-defined metadata.

source
ExaModels.ConstraintSlotType
ConstraintSlot{C, I}

A lightweight handle returned by getindex on a Constraint or ConstraintAugmentation. When added to an expression node via +, it produces a Pair(idx, expr) suitable for constraint augmentation.

This enables the g[idx] += expr for ... syntactic sugar:

c, _ = add_con!(c, g[i] += sin(x[i]) for i in 1:N)
source
ExaModels.DataIndexedType
DataIndexed{I, J} <: AbstractNode

A node representing the lookup inner.J (or inner[J]) where inner is a DataSource or another DataIndexed. The field/index J is encoded as a type parameter so that getfield / getindex can be resolved at compile time.

Constructed implicitly via getproperty / getindex on a DataSource.

source
ExaModels.DataSourceType
DataSource <: AbstractNode

Sentinel node used as the root of a parameter (data) array. Indexing or accessing fields on a DataSource returns a DataIndexed node that encodes the access path as nested type parameters for zero-overhead data lookup.

source
ExaModels.EachScenarioType
EachScenario

Marker type used with add_var, add_par, and add_con to indicate that the declaration is replicated for each scenario in a two-stage stochastic program.

Pass EachScenario() as the second positional argument (before dimensions) to create per-scenario (recourse) variables, parameters, or constraints. Omitting it creates first-stage (design) components shared across all scenarios.

Example

core = TwoStageExaCore(3)                          # 3 scenarios
c, d = add_var(core, 2)                            # 2 design variables (shared)
c, v = add_var(core, EachScenario(), 4)            # 4 recourse variables per scenario
c, g = add_con(core, EachScenario(), v[i] for i in 1:4)  # per-scenario constraints
source
ExaModels.ExaCoreType
ExaCore([array_eltype::Type; backend = nothing, minimize = true, name = :Generic])

Creates an intermediate data object ExaCore, which later can be used for creating an ExaModel

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true))
An ExaCore

  Float type: ...................... Float64
  Array type: ...................... Vector{Float64}
  Backend: ......................... Nothing

  number of objective patterns: .... 0
  number of constraint patterns: ... 0

julia> c = ExaCore(Float32; concrete = Val(true))
An ExaCore

  Float type: ...................... Float32
  Array type: ...................... Vector{Float32}
  Backend: ......................... Nothing

  number of objective patterns: .... 0
  number of constraint patterns: ... 0

julia> using CUDA

julia> c = ExaCore(Float32; backend = CUDABackend(), concrete = Val(true))
An ExaCore

  Float type: ...................... Float32
  Array type: ...................... CUDA.CuArray{Float32, 1, CUDA.DeviceMemory}
  Backend: ......................... CUDA.CUDAKernels.CUDABackend

  number of objective patterns: .... 0
  number of constraint patterns: ... 0
source
ExaModels.ExaModelMethod
ExaModel(core)

Returns an ExaModel object, which can be solved by nonlinear optimization solvers within JuliaSmoothOptimizer ecosystem, such as NLPModelsIpopt or MadNLP.

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));                           # create an ExaCore object

julia> c, x = add_var(c, 1:10);               # create variables

julia> c, _ = add_obj(c, x[i]^2 for i in 1:10); # set objective function

julia> m = ExaModel(c)                          # create an ExaModel object
An ExaModel{Float64, Vector{Float64}, ...}

  Problem name: Generic
   All variables: ████████████████████ 10     All constraints: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
            free: ████████████████████ 10                free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
           lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                lower: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
           upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                upper: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
         low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0              low/upp: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
           fixed: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0                fixed: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
          infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0               infeas: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
            nnzh: ( 81.82% sparsity)   10              linear: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
                                                    nonlinear: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
                                                         nnzj: (------% sparsity)
                                                     lin_nnzj: (------% sparsity)
                                                     nln_nnzj: (------% sparsity)

julia> using NLPModelsIpopt

julia> result = ipopt(m; print_level=0)    # solve the problem
"Execution stats: first-order stationary"
source
ExaModels.ExpressionType
Expression

A subexpression created by add_expr / @add_expr. When indexed (e.g. s[i]), the expression is substituted directly into the enclosing objective or constraint — no auxiliary variables or equality constraints are introduced. Use Expression to share common sub-expressions across multiple objectives or constraints without duplicating the expression tree. An optional tag field carries user-defined metadata.

source
ExaModels.LegacyExaCoreType
LegacyExaCore{T,VT,B,S}

A mutable wrapper around an immutable ExaCore that provides the legacy mutating API (variable, constraint, etc.).

ExaCore() returns a LegacyExaCore by default. Use ExaCore(concrete = Val(true)) to obtain the bare immutable ExaCore required for AOT compilation.

source
ExaModels.Node1Type
Node1{F, I} <: AbstractNode

An interior node representing a unary operation F applied to one child node.

Evaluated as F(inner(i, x, θ)).

Fields

  • inner::I: child node
source
ExaModels.Node2Type
Node2{F, I1, I2} <: AbstractNode

An interior node representing a binary operation F applied to two children. Either child can be a node or a plain scalar (e.g. Real), allowing numeric coefficients to be stored directly without wrapping in Constant.

Evaluated as F(inner1(i, x, θ), inner2(i, x, θ)).

Fields

  • inner1::I1: left child
  • inner2::I2: right child
source
ExaModels.NullType
Null{T} <: AbstractNode

A leaf node that always evaluates to a fixed scalar. The value is stored in a field (as opposed to Constant, which encodes the value as a type parameter). Used when the constant is not known at compile time.

Null() creates a zero-valued node; Null(v) creates a node that returns T(v) where T is the element type of the primal vector.

source
ExaModels.ParameterType
Parameter

A handle to a block of model parameters added to an ExaCore via add_par / @add_par. Parameter values can be updated at any time with set_parameter! without rebuilding the model. Use indexing (e.g. θ[i]) to embed parameter values in expressions. An optional tag field carries user-defined metadata.

source
ExaModels.SIMDFunctionType
SIMDFunction(gen::Base.Generator, o0 = 0, o1 = 0, o2 = 0)

Returns a SIMDFunction using the gen.

Arguments:

  • gen: an iterable function specified in Base.Generator format
  • o0: offset for the function evaluation
  • o1: offset for the derivative evalution
  • o2: offset for the second-order derivative evalution
source
ExaModels.SecondAdjointNode1Type
SecondAdjointNode1{F, T, I} <: AbstractSecondAdjointNode

Hessian-pass node for a unary operation F. A single forward sweep stores the primal value, first derivative, and second derivative needed for the reverse Hessian accumulation.

Fields

  • x::T: primal value F(inner.x)
  • y::T: first derivative F'(inner.x)
  • h::T: second derivative F''(inner.x)
  • inner::I: child node
source
ExaModels.SecondAdjointNode2Type
SecondAdjointNode2{F, T, I1, I2} <: AbstractSecondAdjointNode

Hessian-pass node for a binary operation F.

Fields

  • x::T: primal value F(inner1.x, inner2.x)
  • y1::T: partial derivative w.r.t. first argument
  • y2::T: partial derivative w.r.t. second argument
  • h11::T: second partial w.r.t. first argument
  • h12::T: mixed second partial
  • h22::T: second partial w.r.t. second argument
  • inner1::I1: left child
  • inner2::I2: right child
source
ExaModels.SecondAdjointNodeVarType
SecondAdjointNodeVar{I, T} <: AbstractSecondAdjointNode

Leaf node in the Hessian-pass tree corresponding to a decision variable.

Fields

  • i::I: variable index
  • x::T: primal value x[i] at the current evaluation point
source
ExaModels.SecondAdjointNullType
SecondAdjointNull{V} <: AbstractSecondAdjointNode

Leaf node in the second-order adjoint tree representing a constant (zero contribution to both the gradient and the Hessian). Carries the primal value x for upstream consumption.

source
ExaModels.SumNodeType
SumNode{I} <: AbstractNode

A node representing the sum of a tuple of child nodes.

Constructed by exa_sum. Within @add_obj, @add_con, and @add_expr macros, sum(body for k in range) is automatically rewritten to exa_sum(k -> body, Val(range)) with the Val hoisted outside the generator closure for type stability under juliac --trim=safe.

In adjoint / second-adjoint mode the children are evaluated and folded via reduce(+, …) (or reduce(*, …) for ProdNode), reusing the existing registered + / * dispatch. No dedicated adjoint node types are needed.

source
ExaModels.TimedNLPModelType
TimedNLPModel

A transparent wrapper around any AbstractNLPModel that records wall-clock timings and call counts for each NLP callback (obj, cons!, grad!, jac_coord!, hess_coord!, jac_structure!, hess_structure!).

Statistics accumulate in the stats field and can be printed with print(m). Construct via TimedNLPModel(m).

source
ExaModels.TwoStageExaCoreMethod
TwoStageExaCore(nscen; backend = nothing, concrete = Val(false), kwargs...)

Create an ExaCore for building two-stage stochastic programs with nscen scenarios.

Use add_var, add_par, and add_con with EachScenario() to declare per-scenario components, or without it for first-stage (design) components.

Example

core = TwoStageExaCore(5)                    # 5 scenarios
c, d = add_var(core, 3)                      # 3 design variables
c, v = add_var(c, EachScenario(), 2)         # 2 recourse variables per scenario
model = ExaModel(c)
source
ExaModels.VarType
Var{I} <: AbstractNode

A leaf node representing the i-th decision variable.

When evaluated as v(i, x, θ), returns x[v.i] (or x[v.i(i,x,θ)] when the index is itself a node).

source
ExaModels.VarSourceType
VarSource <: AbstractNode

Sentinel node used as the root of a variable array. Indexing a VarSource with an integer i returns Var(i), building the leaf node that refers to the i-th decision variable.

source
ExaModels.VariableType
Variable

A handle to a block of optimization variables added to an ExaCore via add_var / @add_var. Use indexing (e.g. x[i]) to reference individual entries in objective and constraint expressions. Retrieve solution values with solution. An optional tag field carries user-defined metadata (e.g. scenario identifiers for two-stage models).

source
ExaModels.WrapperNLPModelType
WrapperNLPModel

An NLPModels.AbstractNLPModel wrapper that bridges a model whose internal array type differs from the host array type (e.g. a GPU-backed ExaModel wrapped for a CPU-only solver). All NLP callbacks are forwarded to the inner model through intermediate copy buffers.

Use WrapperNLPModel(m) or WrapperNLPModel(VT, m) rather than constructing this struct directly.

source
ExaModels.WrapperNLPModelMethod
WrapperNLPModel(m)

Returns a WrapperNLPModel{Float64,Vector{Float64}} wrapping m, forwarding all NLP callbacks through Float64 CPU copy buffers.

source
ExaModels._needs_overloadMethod
_needs_overload(f, types)

Return true when ExaModels should add a method for f with the given argument types.

Plain hasmethod is too conservative for Base generics such as max(x,y) = ifelse(isless(x,y),y,x): those definitions match any AbstractNode argument, so hasmethod returns true and would prevent the ExaModels-specific overload from being added. We instead check the owner module of the matching method and only skip when ExaModels already owns it.

source
ExaModels.add_con!Method
add_con!(core, c1, generator; tag = nothing)

Augments the existing constraint c1 by adding extra expression terms to a subset of its rows, and returns (core, ConstraintAugmentation).

This is the primary mechanism for building constraints that aggregate contributions from multiple data sources — for example, nodal power-balance constraints that sum flows over all arcs incident to each bus. Each call to add_con! appends one new "augmentation layer"; multiple layers for the same base constraint are summed at evaluation time.

The bounds (lcon/ucon) remain those set on the original c1 and cannot be changed via add_con!.

Arguments

  • core: The ExaCore to modify.
  • c1: The base Constraint (or a previous ConstraintAugmentation) whose rows are being augmented.
  • generator: A Base.Generator yielding idx => expr pairs, where
    • idx is an index (or tuple of indices) into c1 identifying which constraint row receives the term, and
    • expr is the scalar expression to add to that row.

Notes

  • The index idx must be a valid index of c1's iterator (e.g. an integer for a 1-D constraint, or a tuple (i, j) for a multi-dimensional one).
  • One generator element maps to one non-zero Jacobian row; elements with the same idx are accumulated.
  • The iterator of the generator becomes the SIMD work set, so performance is best when it is a contiguous collection (array, range, or product iterator).

Example

Single-index augmentation — add sin(x[i+1]) to constraint rows 4, 5, 6:

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 10);

julia> c, c1 = add_con(c, x[i] + x[i+1] for i=1:9; lcon = -1, ucon = (1+i for i=1:9));

julia> c, c2 = add_con!(c, c1, i => sin(x[i+1]) for i=4:6);

julia> c2
Constraint Augmentation

  g♭ ≤ (...) + ∑_{i ∈ I} h(x,i) ≤ g♯

  h(x,i) = sin(x[i.1 + 1])

  where |I| = 3

Multi-source augmentation (typical power-flow use case) — accumulate arc flows into bus balance constraints:

c, bus = add_con(c, pd[b.i] + gs[b.i]*vm[b.i]^2 for b in data.bus)   # one row per bus
add_con!(c, bus, arc.bus => p[arc.i] for arc in data.arc)              # add arc flows
add_con!(c, bus, gen.bus => -pg[gen.i] for gen in data.gen)            # subtract generation
source
ExaModels.add_con!Method
add_con!(core::ExaCore, gen::Base.Generator; tag = nothing)

Two-argument form of add_con! supporting the constraint[idx] += expr sugar. The generator must use the pattern g[idx] += expr for ... in itr, where g is an existing Constraint or ConstraintAugmentation.

Equivalent to add_con!(core, g, idx => expr for ... in itr).

Example

c = ExaCore(concrete = Val(true))
c, x = add_var(c, 10)
c, g = add_con(c, 9; lcon = -1.0, ucon = 1.0)
c, _ = add_con!(c, g[i] += x[i] + x[i+1] for i = 1:9)
source
ExaModels.add_conMethod
add_con(core::TwoStageExaCore, ::EachScenario, dims_or_gen...; start = 0, lcon = 0, ucon = 0, name = nothing)

Add second-stage (per-scenario) constraints to a two-stage core. The constraint expression is replicated for each scenario. The iterator element is wrapped so that DataSource()[1] accesses the original data and DataSource()[2] gives the scenario index.

source
ExaModels.add_conMethod
add_con(core::TwoStageExaCore, dims_or_gen...; start = 0, lcon = 0, ucon = 0, name = nothing)

Add first-stage constraints to a two-stage core. Accepts the same forms as the base add_con (generator or dims). Tagged with FirstStageTag().

source
ExaModels.add_conMethod
add_con(core, generator; start = 0, lcon = 0, ucon = 0, name = nothing, tag = nothing)
add_con(core, dims...; start = 0, lcon = 0, ucon = 0, name = nothing, tag = nothing)

Adds constraints to core and returns (core, Constraint).

Generator form: pass a generator that yields one expression per constraint row.

Dims form: pass integer or UnitRange dimensions to create empty constraints, then use add_con! / @add_con! to accumulate terms afterwards. dims can be a single integer (add_con(c, 9)), multiple integers (add_con(c, 3, 4) for a 3×4 grid), or AbstractUnitRange values (add_con(c, 1:3, 2:5)) — matching the convention used by add_var.

Keyword Arguments

  • start: The initial guess of the dual solution. Can either be Number, AbstractArray, or Generator.
  • lcon : The constraint lower bound. Can either be Number, AbstractArray, or Generator.
  • ucon : The constraint upper bound. Can either be Number, AbstractArray, or Generator.
  • name : When given as Val(:name), registers the constraint in core for later retrieval as core.name or model.name. See @add_con for the idiomatic named interface.
  • tag : User-defined metadata attached to the constraint block.

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 10);

julia> c, con = add_con(c, x[i] + x[i+1] for i=1:9; lcon = -1, ucon = (1+i for i=1:9));

julia> con
Constraint

  g♭ ≤ [g(x,i)]_{i ∈ I} ≤ g♯

  g(x,i) = x[i] + x[i + 1]

  where |I| = 9

Empty constraint with augmentation:

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 10);

julia> c, g = add_con(c, 9; lcon = -1.0, ucon = 1.0);

julia> c, _ = add_con!(c, g, i => x[i] + x[i+1] for i = 1:9);

julia> g
Constraint

  g♭ ≤ [g(x,i)]_{i ∈ I} ≤ g♯

  g(x,i) = 0

  where |I| = 9
source
ExaModels.add_exprMethod
add_expr(core, generator; name = nothing, tag = nothing)

Creates a subexpression from a generator and returns (core, Expression). The expression is stored for direct substitution (inlining) when indexed — no auxiliary variables or constraints are added to the problem.

Keyword Arguments

  • name: When given as Val(:name), registers the subexpression in core for later retrieval as core.name or model.name. See @add_expr for the idiomatic named interface.
  • tag : User-defined metadata attached to the expression.

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 10);

julia> c, s = add_expr(c, x[i]^2 for i in 1:10);

julia> s
Subexpression (reduced)

  s ∈ R^{10}
  s(x,i) = x[i]^2

julia> c, _ = add_obj(c, s[i] + s[i+1] for i in 1:9);

Multi-dimensional example

c = ExaCore(concrete = Val(true))
c, x = add_var(c, 1:N, 1:K)
itr = [(i, k) for i in 1:N, k in 1:K]
c, s = add_expr(c, x[i, k]^2 for (i, k) in itr)
# s[i, k] substitutes x[i,k]^2 directly
source
ExaModels.add_objMethod
add_obj(core::ExaCore, generator; name = nothing)

Adds objective terms specified by a generator to core, and returns (core, Objective). The terms are summed.

Keyword Arguments

  • name: When given as Val(:name), registers the objective in core for later retrieval as core.name or model.name. See @add_obj for the idiomatic named interface.

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 10);

julia> c, obj = add_obj(c, x[i]^2 for i=1:10);

julia> obj
Objective

  ∑_{i ∈ I} f(x,i)

  f(x,i) = x[i]^2

  where |I| = 10
source
ExaModels.add_objMethod
add_obj(core::ExaCore, expr [, pars]; name = nothing)

Low-level form of add_obj that accepts a pre-built AbstractNode expression expr evaluated over pars, and returns (core, Objective).

When name is given as Val(:name), the objective is also accessible as core.name or model.name.

Prefer the generator form (add_obj(core, gen)) for typical use; this form is intended for code that builds expression trees programmatically.

source
ExaModels.add_parMethod
add_par(core::TwoStageExaCore, value::AbstractArray; name = nothing)
add_par(core::TwoStageExaCore, n::AbstractRange; name = nothing, value = 0)
add_par(core::TwoStageExaCore, dims...; name = nothing, value = 0)

Add first-stage parameters to a two-stage core, tagged with FirstStageTag().

Mirrors the add_par convention from ExaCore:

  • Pass value::AbstractArray as the first positional argument to use its values and infer dimensions from size(value).
  • Pass dimensions (Integer or AbstractRange) as positional arguments and supply the uniform initial value via the value keyword.
source
ExaModels.add_parMethod
add_par(core::TwoStageExaCore, ::EachScenario, value::AbstractVector; name = nothing)

Add second-stage parameters to a two-stage core. The parameter vector value is replicated for each scenario, tagged with SecondStageTag().

source
ExaModels.add_parMethod
add_par(core, dims...; value = 0, name = nothing, tag = nothing)
add_par(core, value::AbstractArray; name = nothing, tag = nothing)

Adds parameters to core and returns (core, Parameter).

The first form specifies dimensions with dims (each an Integer or UnitRange) and initial values via the value keyword. The second form is a convenience that uses size(value) as the dimensions.

Keyword Arguments

  • value: Initial parameter values. Can be a Number, AbstractArray, or Generator.
  • name : When given as Val(:name), registers the parameter in core for later retrieval as core.name or model.name. See @add_par for the idiomatic named interface.
  • tag : User-defined metadata attached to the parameter block.

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, θ = add_par(c, ones(10));

julia> θ
Parameter

  θ ∈ R^{10}
source
ExaModels.add_varMethod
add_var(core::TwoStageExaCore, ::EachScenario, dims...; start = 0, lvar = -Inf, uvar = Inf, name = nothing)

Add second-stage (recourse) variables to a two-stage core. Creates prod(dims) * nscen variables total — one copy of the block per scenario — tagged with SecondStageTag(). The last dimension of the resulting variable indexes the scenario.

source
ExaModels.add_varMethod
add_var(core::TwoStageExaCore, dims...; start = 0, lvar = -Inf, uvar = Inf, name = nothing)

Add first-stage (design) variables to a two-stage core. These are shared across all scenarios and tagged with FirstStageTag().

source
ExaModels.add_varMethod
add_var(core, gen::Base.Generator; kwargs...)

Create variables constrained to equal the expressions produced by gen. Equivalent to creating length(gen.iter) variables with equality constraints tying each to the corresponding generator expression. Returns (core, Variable).

source
ExaModels.add_varMethod
add_var(core, dims...; start = 0, lvar = -Inf, uvar = Inf, name = nothing, tag = nothing)

Adds variables with dimensions specified by dims to core. dims can be either Integer or UnitRange. Returns (core, Variable).

Keyword Arguments

  • start: The initial guess of the solution. Can either be Number, AbstractArray, or Generator.
  • lvar : The variable lower bound. Can either be Number, AbstractArray, or Generator.
  • uvar : The variable upper bound. Can either be Number, AbstractArray, or Generator.
  • name : When given as Val(:name), registers the variable in core for later retrieval as core.name or model.name. See @add_var for the idiomatic named interface.
  • tag : User-defined metadata attached to the variable block (e.g., scenario identifier for two-stage models).

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 10; start = (sin(i) for i=1:10));

julia> x
Variable

  x ∈ R^{10}

julia> c, y = add_var(c, 2:10, 3:5; lvar = zeros(9,3), uvar = ones(9,3));

julia> y
Variable

  x ∈ R^{9 × 3}
source
ExaModels.drpassMethod
drpass(d::D, y, adj)

Performs dense gradient evaluation via the reverse pass on the computation (sub)graph formed by forward pass

Arguments:

  • d: first-order computation (sub)graph
  • y: result vector
  • adj: adjoint propagated up to the current node
source
ExaModels.exa_prodMethod
exa_prod(f, itr)
exa_prod(f, ::Val{range})
exa_prod(gen::Base.Generator)

Build a ProdNode representing ∏ f(k) for k ∈ itr. Inside @add_obj, @add_con, and @add_expr macros, prod(body for k in range) is automatically rewritten to exa_prod(k -> body, Val(range)) with the Val hoisted outside the generator closure.

See exa_sum for supported iterators and juliac usage notes.

source
ExaModels.exa_sumMethod
exa_sum(f, itr)
exa_sum(f, ::Val{range})
exa_sum(gen::Base.Generator)

Build a SumNode representing ∑ f(k) for k ∈ itr. Inside @add_obj, @add_con, and @add_expr macros, sum(body for k in range) is automatically rewritten to exa_sum(k -> body, Val(range)) with the Val hoisted outside the generator closure.

Supported iterators

  • Tuple: type-stable via tail recursion.
  • UnitRange{Int}: type-stable when the length is a compile-time constant.
  • Val{range} (preferred for juliac): exa_sum(f, Val(1:nc)) embeds the range in the type parameter, ensuring juliac --trim=safe can resolve Val{N}.

juliac / AOT usage

Julia's inference cannot propagate constants like nc = 3 through a generator closure boundary. When calling add_con/add_obj programmatically (not via macro), hoist Val(range) outside the generator:

v = Val(1:nc)                                          # outside generator
c, con = add_con(c, (exa_sum(j -> x[j], v) for i in 1:nh))  # v captured
source
ExaModels.fulltypeMethod
fulltype(node)
fulltype(io::IO, node)

Print the full unabbreviated type of a node.

By default, show for ExaModels node types abbreviates inner graph-structure parameters with to keep output readable (e.g. Node2{+,…} instead of Node2{typeof(+),Var{Int64},Var{Int64}}). fulltype bypasses this by passing the IOContext key :fulltype => true, which all show(::IO, ::Type{...}) overloads in this file respect.

Examples

node = Var(1) + Var(2)          # Node2{typeof(+),Var{Int64},Var{Int64}}
show(stdout, typeof(node))      # → Node2{+,…}          (default)
fulltype(node)                  # → Node2{+,Var{Int64},Var{Int64}}

# Equivalent manual form:
show(IOContext(stdout, :fulltype => true), typeof(node))

IOContext flag

Any show call that passes :fulltype => true in its IOContext will render the complete parametric type. This is useful when you need full type output programmatically without calling fulltype directly:

io = IOContext(stderr, :fulltype => true)
show(io, typeof(node))
source
ExaModels.fulltype_display!Method
fulltype_display!(enabled::Bool)

Globally enable or disable full-type display for ExaModels node types.

When enabled, typeof(node) and error messages (MethodErrors, stacktraces) show the complete parametric type instead of the abbreviated form.

ExaModels.fulltype_display!(true)   # enable — e.g. typeof(x) → Node2{+,Var{Int64},Var{Int64}}
ExaModels.fulltype_display!(false)  # disable — e.g. typeof(x) → Node2{+,…}

For one-off inspection without changing the global setting, use fulltype.

source
ExaModels.get_con_scenMethod
get_con_scen(model::TwoStageExaModel)

Return the scenario-index vector for constraints. Entry k holds the scenario number of the k-th constraint (0 = shared).

findall(==(s), get_con_scen(model))  # constraint indices for scenario s
source
ExaModels.get_startMethod
get_start(model, var::Variable)
get_start(model, con::Constraint)

Return a view of the initial-point values (x0 for variables, y0 for constraints).

source
ExaModels.get_startMethod
get_start(model::TwoStageExaModel, con::SecondStageConstraint, scen)
get_lcon(model::TwoStageExaModel, con::SecondStageConstraint, scen)
get_ucon(model::TwoStageExaModel, con::SecondStageConstraint, scen)

Return a view of y0, lcon, or ucon for con restricted to scenario scen.

source
ExaModels.get_startMethod
get_start(model::TwoStageExaModel, var::SecondStageVariable, scen)
get_lvar(model::TwoStageExaModel, var::SecondStageVariable, scen)
get_uvar(model::TwoStageExaModel, var::SecondStageVariable, scen)

Return a view of x0, lvar, or uvar for var restricted to scenario scen.

source
ExaModels.get_valueMethod
get_value(model, param)

Return a view of all values for param in model.θ.

For second-stage parameters in a two-stage model, use get_value(model, param, scen) to extract a single scenario's slice.

source
ExaModels.get_valueMethod
get_value(model::TwoStageExaModel, param::SecondStageParameter, scen)

Return a view of the values for param in scenario scen.

The parameter must have been added with EachScenario. scen must be an integer in 1:get_nscen(model).

source
ExaModels.get_var_scenMethod
get_var_scen(model::TwoStageExaModel)

Return the scenario-index vector for variables. Entry k holds the scenario number of the k-th variable (0 = design variable shared across all scenarios).

findall(==(0), get_var_scen(model))  # design variable indices
findall(==(s), get_var_scen(model))  # indices for scenario s
source
ExaModels.gradient!Method
gradient!(y, f, x, adj)

Performs dense gradient evalution

Arguments:

  • y: result vector
  • f: the function to be differentiated in SIMDFunction format
  • x: variable vector
  • adj: initial adjoint
source
ExaModels.grpassMethod
grpass(d::D, comp, y, o1, cnt, adj)

Performs dsparse gradient evaluation via the reverse pass on the computation (sub)graph formed by forward pass

Arguments:

  • d: first-order computation (sub)graph
  • comp: a Compressor, which helps map counter to sparse vector index
  • y: result vector
  • o1: index offset
  • cnt: counter
  • adj: adjoint propagated up to the current node
source
ExaModels.hdrpassMethod
hdrpass(t1::T1, t2::T2, comp, y1, y2, o2, cnt, adj)

Performs sparse hessian evaluation ((df1/dx)(df2/dx)' portion) via the reverse pass on the computation (sub)graph formed by second-order forward pass

Arguments:

  • t1: second-order computation (sub)graph regarding f1
  • t2: second-order computation (sub)graph regarding f2
  • comp: a Compressor, which helps map counter to sparse vector index
  • y1: result vector #1
  • y2: result vector #2 (only used when evaluating sparsity)
  • o2: index offset
  • cnt: counter
  • adj: second adjoint propagated up to the current node
source
ExaModels.jrpassMethod
jrpass(d::D, comp, i, y1, y2, o1, cnt, adj)

Performs sparse jacobian evaluation via the reverse pass on the computation (sub)graph formed by forward pass

Arguments:

  • d: first-order computation (sub)graph
  • comp: a Compressor, which helps map counter to sparse vector index
  • i: constraint index (this is i-th constraint)
  • y1: result vector #1
  • y2: result vector #2 (only used when evaluating sparsity)
  • o1: index offset
  • cnt: counter
  • adj: adjoint propagated up to the current node
source
ExaModels.multipliersMethod
multipliers(result, y)

Returns the multipliers for constraints y associated with result, obtained by solving the model.

Example

julia> using ExaModels, NLPModelsIpopt

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 1:10; lvar = -1, uvar = 1);

julia> c, _ = add_obj(c, (x[i]-2)^2 for i in 1:10);

julia> c, y = add_con(c, x[i] + x[i+1] for i=1:9; lcon = -1, ucon = (1+i for i=1:9));

julia> m = ExaModel(c);

julia> result = ipopt(m; print_level=0);

julia> val = multipliers(result, y);


julia> val[1] ≈ 0.81933930
true
source
ExaModels.multipliers_LMethod
multipliers_L(result, x)

Returns the lower-bound dual variables for variable block x associated with result, obtained by solving the model.

multipliers_L[i] ≥ 0 is the dual variable for the bound constraint x[i] ≥ lvar[i]. A nonzero value indicates that the lower bound is active at the solution; the magnitude measures how much the objective would improve if that bound were relaxed.

Example

julia> using ExaModels, NLPModelsIpopt

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 1:10; lvar = -1, uvar = 1);

julia> c, _ = add_obj(c, (x[i]-2)^2 for i in 1:10);

julia> m = ExaModel(c);

julia> result = ipopt(m; print_level=0);

julia> val = multipliers_L(result, x);

julia> isapprox(val, fill(0, 10), atol=sqrt(eps(Float64)), rtol=Inf)
true
source
ExaModels.multipliers_UMethod
multipliers_U(result, x)

Returns the upper-bound dual variables for variable block x associated with result, obtained by solving the model.

multipliers_U[i] ≥ 0 is the dual variable for the bound constraint x[i] ≤ uvar[i]. A nonzero value indicates that the upper bound is active at the solution; the magnitude measures how much the objective would improve if that bound were relaxed.

Example

julia> using ExaModels, NLPModelsIpopt

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 1:10; lvar = -1, uvar = 1);

julia> c, _ = add_obj(c, (x[i]-2)^2 for i in 1:10);

julia> m = ExaModel(c);

julia> result = ipopt(m; print_level=0);

julia> val = multipliers_U(result, x);

julia> isapprox(val, fill(2, 10), atol=sqrt(eps(Float64)), rtol=Inf)
true
source
ExaModels.set_parameter!Method
set_parameter!(core, param, values)

Updates the values of parameters in the core.

Example

julia> using ExaModels

julia> c = ExaCore(concrete = Val(true));

julia> c, p = add_par(c, ones(5));

julia> set_parameter!(c, p, ones(5))
source
ExaModels.set_start!Method
set_start!(model, var::Variable, values)
set_start!(model, con::Constraint, values)

Update the initial-point values in-place (x0 for variables, y0 for constraints).

source
ExaModels.set_start!Method
set_start!(model::TwoStageExaModel, con::SecondStageConstraint, scen, values)
set_lcon!(model::TwoStageExaModel, con::SecondStageConstraint, scen, values)
set_ucon!(model::TwoStageExaModel, con::SecondStageConstraint, scen, values)

Update y0, lcon, or ucon for con in scenario scen in-place.

source
ExaModels.set_start!Method
set_start!(model::TwoStageExaModel, var::SecondStageVariable, scen, values)
set_lvar!(model::TwoStageExaModel, var::SecondStageVariable, scen, values)
set_uvar!(model::TwoStageExaModel, var::SecondStageVariable, scen, values)

Update x0, lvar, or uvar for var in scenario scen in-place.

source
ExaModels.set_value!Method
set_value!(model::TwoStageExaModel, param::SecondStageParameter, scen, values)

Update the values for param in scenario scen to values.

The parameter must have been added with EachScenario. values must have param.length ÷ get_nscen(model) elements.

source
ExaModels.sgradient!Method

sgradient!(y, f, x, adj)

Performs sparse gradient evalution

Arguments:

  • y: result vector
  • f: the function to be differentiated in SIMDFunction format
  • x: variable vector
  • adj: initial adjoint
source
ExaModels.shessian!Method
shessian!(y1, y2, f, x, adj1, adj2)

Performs sparse jacobian evalution

Arguments:

  • y1: result vector #1
  • y2: result vector #2 (only used when evaluating sparsity)
  • f: the function to be differentiated in SIMDFunction format
  • x: variable vector
  • adj1: initial first adjoint
  • adj2: initial second adjoint
source
ExaModels.sjacobian!Method
sjacobian!(y1, y2, f, x, adj)

Performs sparse jacobian evalution

Arguments:

  • y1: result vector #1
  • y2: result vector #2 (only used when evaluating sparsity)
  • f: the function to be differentiated in SIMDFunction format
  • x: variable vector
  • adj: initial adjoint
source
ExaModels.solutionMethod
solution(result, x)

Returns the primal solution values for variable block x associated with result, obtained by solving the model. The returned array has the same shape as x.

Example

julia> using ExaModels, NLPModelsIpopt

julia> c = ExaCore(concrete = Val(true));

julia> c, x = add_var(c, 1:10; lvar = -1, uvar = 1);

julia> c, _ = add_obj(c, (x[i]-2)^2 for i in 1:10);

julia> m = ExaModel(c);

julia> result = ipopt(m; print_level=0);

julia> val = solution(result, x);

julia> isapprox(val, fill(1, 10), atol=sqrt(eps(Float64)), rtol=Inf)
true
source
ExaModels.@add_con!Macro
@add_con!(core, c1, generator; kwargs...)
@add_con!(core, c1[idx] += expr for ...; kwargs...)

Macro interface for add_con!. Updates core in the calling scope and returns the new ConstraintAugmentation.

Two calling conventions are supported:

  • Three-argument (@add_con!(core, c1, idx => expr for ...)): the constraint c1 is passed explicitly and the generator yields idx => expr pairs.
  • Two-argument (@add_con!(core, c1[idx] += expr for ...)): the constraint and index are embedded in the generator via += syntax. This form delegates to the function-level add_con!(core, gen), which extracts the target constraint automatically.

See add_con! for full semantics and usage notes.

Example

c = ExaCore(concrete = Val(true))
@add_var(c, x, 10)
@add_con(c, g, x[i] + x[i+1] for i in 1:9; lcon = -1, ucon = 1)

## Three-argument form:
aug = @add_con!(c, g, i => sin(x[i+1]) for i in 4:6)

## Two-argument form (equivalent):
aug = @add_con!(c, g[i] += sin(x[i+1]) for i in 4:6)
source
ExaModels.@add_conMacro
@add_con(core, [name,] generator; kwargs...)

Macro interface for add_con. Updates core in the calling scope.

  • Named (@add_con(core, g, generator)): binds g to the new Constraint in the local scope and registers it in core for later retrieval as core.g or model.g.
  • Anonymous (@add_con(core, generator)): equivalent to c, g = add_con(c, generator).

Accepts the same keyword arguments as add_con (lcon, ucon, start, etc.).

Example

c = ExaCore(concrete = Val(true))
@add_var(c, x, 10)
@add_con(c, g, x[i] + x[i+1] for i in 1:9; lcon = -1, ucon = 1)  # g in scope; c.g also works
source
ExaModels.@add_exprMacro
@add_expr(core, [name,] generator; kwargs...)

Macro interface for add_expr. Updates core in the calling scope.

  • Named (@add_expr(core, s, generator)): binds s to the new Expression in the local scope and registers it in core for later retrieval as core.s or model.s.
  • Anonymous (@add_expr(core, generator)): equivalent to c, s = add_expr(c, generator).

Example

c = ExaCore(concrete = Val(true))
@add_var(c, x, 10)
@add_expr(c, s, x[i]^2 for i in 1:10)    # s in scope; c.s also works
@add_obj(c, s[i] + s[i+1] for i in 1:9)
source
ExaModels.@add_objMacro
@add_obj(core, [name,] generator; kwargs...)

Macro interface for add_obj. Updates core in the calling scope.

  • Named (@add_obj(core, f, generator)): binds f to the new Objective in the local scope and registers it in core for later retrieval as core.f or model.f.
  • Anonymous (@add_obj(core, generator)): equivalent to c, o = add_obj(c, generator).

Example

c = ExaCore(concrete = Val(true))
@add_var(c, x, 10)
@add_obj(c, x[i]^2 for i in 1:10)
source
ExaModels.@add_parMacro
@add_par(core, [name,] start; kwargs...)

Macro interface for add_par. Updates core in the calling scope.

  • Named (@add_par(core, θ, start)): binds θ to the new Parameter in the local scope and registers it in core for later retrieval as core.θ or model.θ.
  • Anonymous (@add_par(core, start)): equivalent to c, p = add_par(c, start).

Example

c = ExaCore(concrete = Val(true))
@add_par(c, θ, ones(10))  # θ is now in scope; c.θ also works
source
ExaModels.@add_varMacro
@add_var(core, [name,] dims...; kwargs...)

Macro interface for add_var. Updates core in the calling scope.

  • Named (@add_var(core, x, dims...)): binds x to the new Variable in the local scope and registers it in core for later retrieval as core.x or model.x.
  • Anonymous (@add_var(core, dims...)): equivalent to c, v = add_var(c, dims...).

Accepts the same keyword arguments as add_var.

Example

c = ExaCore(concrete = Val(true))
@add_var(c, x, 10; lvar = -1, uvar = 1)  # x is now in scope; c.x also works
@add_var(c, y, 1:5)                        # y is in scope; c.y also works
source
ExaModels.@register_bivariateMacro
@register_bivariate(f, df1, df2, ddf11, ddf12, ddf22)

Register a bivariate function f so it can be used inside @add_obj / @add_con expressions. The macro adds four method groups:

  1. Node OP Nodef(d1::AbstractNode, d2::AbstractNode) → Node2(f, d1, d2).
  2. Node OP Realf(d1::AbstractNode, d2::Real) → Node2(f, d1, d2). Numeric scalars (including iterator-derived values like factorial(j)) are stored directly in Node2 without wrapping in Constant.
  3. Real OP Node — symmetric counterpart of the above.
  4. Constant foldingf(Constant{I1}(), Constant{I2}()) → Constant(f(I1, I2)). Both-constant expressions are evaluated immediately.

Arguments

  • f: the function to register
  • df1: partial derivative w.r.t. the first argument
  • df2: partial derivative w.r.t. the second argument
  • ddf11: second partial w.r.t. the first argument
  • ddf12: mixed second partial
  • ddf22: second partial w.r.t. the second argument

Example

julia> using ExaModels

julia> relu23(x,y) = (x > 0 || y > 0) ? (x + y)^3 : zero(x)
relu23 (generic function with 1 method)

julia> drelu231(x,y) = (x > 0 || y > 0) ? 3 * (x + y)^2 : zero(x)
drelu231 (generic function with 1 method)

julia> drelu232(x,y) = (x > 0 || y > 0) ? 3 * (x + y)^2  : zero(x)
drelu232 (generic function with 1 method)

julia> ddrelu2311(x,y) = (x > 0 || y > 0) ? 6 * (x + y) : zero(x)
ddrelu2311 (generic function with 1 method)

julia> ddrelu2312(x,y) = (x > 0 || y > 0) ? 6 * (x + y) : zero(x)
ddrelu2312 (generic function with 1 method)

julia> ddrelu2322(x,y) = (x > 0 || y > 0) ? 6 * (x + y) : zero(x)
ddrelu2322 (generic function with 1 method)

julia> @register_bivariate(relu23, drelu231, drelu232, ddrelu2311, ddrelu2312, ddrelu2322)
source
ExaModels.@register_univariateMacro
@register_univariate(f, df, ddf)

Register a univariate function f so it can be used inside @add_obj / @add_con expressions. The macro adds three method groups:

  1. Primal graph nodef(n::AbstractNode) → Node1(f, n). Applied at model-construction time to build the symbolic graph.

  2. Constant foldingf(::Constant{T}) → Constant(f(T)). When the argument is a Constant (value encoded as a type parameter), the result is evaluated immediately and stored as a new Constant, avoiding a Node1 allocation entirely.

  3. Adjoint / second-adjoint nodes — forward-pass nodes for gradient and Hessian computation, using df and ddf as the derivative functions.

Arguments

  • f: the function to register
  • df: first derivative f'(x)
  • ddf: second derivative f''(x)

Example

julia> using ExaModels

julia> relu3(x) = x > 0 ? x^3 : zero(x)
relu3 (generic function with 1 method)

julia> drelu3(x) = x > 0 ? 3*x^2 : zero(x)
drelu3 (generic function with 1 method)

julia> ddrelu3(x) = x > 0 ? 6*x : zero(x)
ddrelu3 (generic function with 1 method)

julia> @register_univariate(relu3, drelu3, ddrelu3)
source