Write an ExaModels.jl model to AMPL's
.nl interchange format, so it can be consumed by any ASL-based tool (AMPL-compatible
solvers, AmplNLReader.jl, ...)
without building a JuMP model first.
using ExaModels, ExaModelsAMPL
core = ExaCore(concrete = Val(true))
core, x = add_var(core, 10; start = fill(0.5, 10))
core, _ = add_obj(core, 100*(x[i-1]^2 - x[i])^2 + (x[i-1]-1)^2 for i in 2:10)
model = ExaModel(core)
write_nl("rosenbrock.nl", model)write_nl also accepts any IO in place of a path.
The nonlinear expressions are emitted by walking ExaModels' parameterized expression trees at each data point; subtrees that do not reference the decision variables are evaluated numerically and emitted as constants (problem parameters are baked in at write time).
Supported:
- objectives (any number of objective blocks — ExaModels sums them into one), constraints, variable and constraint bounds, ranges, equalities, initial points;
- multi-dimensional variable and constraint blocks;
- constraint augmentations (
add_con!/@add_con!), scalar- and tuple-indexed: row placement goes through ExaModels' ownoffset0, so it matches the evaluator by construction; sum(...)/prod(...)reduction nodes;- problem parameters (
add_par), baked in as constants at write time; - every operation ExaModels registers. Those with no direct ASL opcode are
rewritten exactly:
inv,exp2,exp10,expm1,log2,log1p,acot,csc,sec,cot,csch,sech,coth,acoth,hypot, the degree-based trigonometric family,sinpi/cospi/sinc,deg2rad/rad2deg, and — through ASL's conditional operator —sign,signbitandcbrt.
Not supported; write_nl raises rather than emitting something wrong:
- operations a user registers with
@register_univariate/@register_bivariatethat have no.nlcounterpart; - named subexpressions and external oracles;
- expression node types the emitter does not recognise. An unrecognised node is an error, never silently constant-folded.
Two exported models differ from the ExaModel at a single point each: sinc(0),
whose removable singularity .nl cannot express, and signbit(-0.0).
Pkg.test("ExaModelsAMPL") round-trips models through write_nl and AmplNLReader.jl
(ASL) and compares objective, constraint, gradient, Jacobian and Lagrangian Hessian
values — and Jacobian sparsity structure — at randomized points against ExaModels'
own evaluations. Every registered operation is covered, on both sides of the kink for
those that branch on the sign of their argument.
Comparison points are the largest perturbation of x0 at which the ExaModel itself
stays in domain: where the model already evaluates to NaN, agreement means nothing,
and the ASL signals out-of-domain by raising rather than by returning NaN.
using Pkg; Pkg.test("ExaModelsAMPL")bench/verify_suites.jl runs the same round-trip over whole benchmark sets —
ExaModelsPower (ACOPF polar/rect, DCOPF, multi-period OPF over pglib cases),
Luksan–Vlcek, and COPS.
bench/solve_cops.jl goes further: it solves the written .nl with Ipopt,
driven entirely by ASL evaluations of that file, and compares the optimum
against the objective values published in the COPS 3.0 report. A misclassified
bound or a bad initial point changes the optimum even when derivatives agree at
a sampled point.
Neither script's dependencies are test dependencies; each header shows the environment it needs.