Skip to content

Environnement en développement actif : vous pouvez remarquer des changements ou des fonctionnalités incomplètes.

JAOT

JModel DSL

JModel is JAOT's declarative modeling language, available as the fourth authoring lens of the Build tab. Instead of enumerating every variable and constraint by hand, you declare sets, params, and indexed families — and the compiler grounds them into the flat optimization problem the solvers run.

JModel is the right lens when your model has repeating structure ("one binary per worker–task pair", "one flow variable per arc") and when you want to separate the model from its data so one formulation can run against many datasets.

Availability: the JModel lens and datasets are feature-gated per instance. If the lens is not visible, the feature is disabled on your instance.

A complete example

A 3×3 assignment problem — 9 binary variables and 6 constraints from a few declarations:

set WORKERS := {A, B, C};
set TASKS := {1, 2, 3};
 
param cost{WORKERS, TASKS} :=
    A 1 9, A 2 2, A 3 7,
    B 1 6, B 2 4, B 3 3,
    C 1 5, C 2 8, C 3 1;
 
var assign{WORKERS, TASKS} binary;
 
minimize total_cost:
    sum{w in WORKERS, t in TASKS} cost[w, t] * assign[w, t];
 
subject to one_worker_per_task{t in TASKS}:
    sum{w in WORKERS} assign[w, t] == 1;
 
subject to one_task_per_worker{w in WORKERS}:
    sum{t in TASKS} assign[w, t] == 1;

The compiler expands this deterministically: assign{WORKERS, TASKS} becomes flat variables assign_A_1, assign_A_2, …, and each indexed constraint family becomes one row per member. The result is a normal model — Analyze, Solve, versioning, and export all work on it.

Language reference

Sets

set ITEMS := {a, b, c, d};      # inline members
set T := 1..96;                 # integer range, inclusive
set I;                          # declaration-only — values come from a dataset
set ARCS := {(a, b), (b, c)};   # tuple members (2-dimensional)
set PAIRS dimen 2;              # declaration-only tuple set
set S := A union B;             # computed: union, diff, cross

Tuple sets are sparse: a variable or param indexed over ARCS only exists for the arcs you list, never the full cartesian product. Qualifiers unpack tuples — sum{(i, j) in ARCS} d[i,j]*x[i,j] — and a family indexed over a tuple set takes the flat component count as subscripts (var x{ARCS, K};x[i,j,k]).

Params

param cap := 50;                              # scalar
param value{ITEMS} := a 60, b 100, c 120;     # indexed
param cost{WORKERS, TASKS} := A 1 9, ...;     # multi-dimensional
param w{I};                                   # declaration-only — filled by a dataset

Variables

var take{ITEMS} binary;
var flow{ARCS} >= 0;
var x integer >= 0 <= 100;

Types: continuous (default), integer, binary.

Objective and constraints

maximize total_value:
    sum{i in ITEMS} value[i] * take[i];
 
subject to capacity:
    sum{i in ITEMS} weight[i] * take[i] <= cap;
 
subject to one_out{i in NODES}:
    sum{j in NODES: i != j} pick[i, j] == 1;

Qualifiers accept filters after a colon (: i != j). Equality filters that pin a tuple component to a known index are applied as slices, so sparse formulations ground in linear time.

Conditional expressions

sum{i in NODES, j in NODES} (if i != j then d[i, j]) * pick[i, j]

if <cond> then <term> else <term> selects at grounding time — conditions compare indices, set members, numbers, and param values (never variables). A missing else means 0, so the untaken branch is simply never generated.

Quadratic terms

Products of two variables (x*y) and squares (x^2) are supported and ground into degree-2 terms — the model classifies as QP/MIQP and solves on the capable solvers. Anything beyond total degree 2 is a structured compile error.

Model / data separation

A set I; or param w{I}; with no := body declares structure whose values must come from a dataset. This is the heart of Datasets & Scenarios: the JModel source is the formulation; each dataset fills the open sets and params with a scenario's values.

Rules the compiler enforces so data errors never pass silently:

  • A dataset always replaces the whole symbol (never a per-key merge), and may also override an inline := default.
  • A dataset key the model does not declare is an error — a typo'd name can never silently fall back to the inline value.
  • A declared set/param that ends up with no values is an error naming the missing symbol.

In the workspace

The JModel lens compiles as you type, with a status pill (Valid model / Compile error with position). A valid compile updates the canonical model — Analyze and Solve see the grounded problem. While the source has a compile error, solving and committing are blocked so you never act on a model that didn't build.

A dataset selector in the lens lets you compile against a chosen dataset (or "No dataset" for inline values). If the model is changed from another lens, the JModel source is marked out of date and kept un-applied until you explicitly recompile from it. After a page reload (or a version restore) the editor cannot know whether the stored source still matches the current model, so it comes back read-only with the same notice — recompiling verifies the source (and unlocks instantly when it matches), so a stray keystroke can never silently overwrite a model you last edited elsewhere.

Committed versions store the JModel source alongside the grounded model, so your formulation is versioned too.

Mathematical notation view

Toggle Math in the lens header to open a split pane that renders your source as symbolic mathematics — the indexed objective, the ∀-quantified constraint families, and each variable's domain, the way they would appear in a paper or thesis.

The rendering is parse-only and deterministic: it walks the parsed model before grounding, so the sum and quantifier structure survives instead of flattening into thousands of scalar rows — and it works even for declaration-only sources that cannot compile yet (say, before a dataset is selected). While you type through an invalid state, the pane keeps the last good rendering dimmed rather than flickering to an error on every keystroke; the editor's own error box stays the authoritative message.

Greek-named identifiers (alpha, lambda, Sigma, …) render as their letters, so minimize obj: alpha * sum{...} reads as α · Σ ….

Derive a draft from an existing model

A model built on the visual canvas or imported from MPS/LP/CIP has no JModel source. Derive draft reconstructs one: variable families over sets, sum objectives, and ∀-quantified constraint families, recovered from the flat model.

Families are recognized from conventional flat naming: trailing index segments that are numeric (assign_3_5) or letters-then-digits (xsc_s1_c1_k1 — supplier/customer/vehicle-style composite labels) become the family's indices, and their labels become set members. A purely alphabetic tail is never guessed to be an index (total_cost stays a scalar), and a name that mixes shapes ambiguously stays flat rather than being mislabeled.

Reconstruction is heuristic, so it is honest by construction: the candidate draft is recompiled and verified to be equivalent to your model before it is offered. If no compact structure round-trips — sparse families, partially covered objectives, exotic shapes — JAOT declines with a note instead of showing a draft that lies about the model. A small flat model with no indexed structure derives as a plain scalar JModel instead.

The derive respects JModel's model/data separation: the source you get is the general formulation — declaration-only sets and params, the objective, and the ∀-quantified constraint families, with uniform constants inline — while every set member and param value lands in an automatically created "Derived data" dataset, selected for you so the draft compiles immediately. A 100×100 model derives as a dozen readable lines, not a wall of twenty-two thousand numbers; the verification compiles source + dataset and checks equivalence, exactly like everything else in this lens. (On an unsaved project the draft stays self-contained instead, since there is nowhere to store a dataset yet.)

The derived draft lands in the editor as a normal edit: review it, compile it, and from then on the source is the formulation.

Generate with AI

Generate with AI writes a JModel source from a plain-language description, a screenshot, or a PDF of a formulation — the model reads images and documents directly, no OCR step. Attach up to 4 files (PNG, JPEG, GIF, WebP or PDF, 5 MB each), describe the problem, or both; if the editor already holds a draft, the request refines it instead of starting over.

The loop is compile-verified: the AI proposes a source, the JModel compiler validates it, and any compile error is fed straight back for a retry (up to 3 rounds). A source applied with a success toast has verifiably compiled — it is never taken on faith. If it still fails after the retries, you get the best-effort draft plus the exact compile error, so you land on an editable starting point rather than nothing.

Generation runs under the same guardrails as the AI assistant: content moderation, rate limits, the instance's monthly AI budget, and bring-your-own-key support (an org key runs on the org's own account).

Scale and safety

  • Grounding is budgeted: expansion stops with a clear error before an accidental combinatorial blowup (three-index families over huge sets) can pin the server. Honest sparse models with hundreds of thousands of grounded elements compile fine.
  • Every referenced set/param/variable must be declared; every index must belong to the family's declared set — no "ghost" variables.
  • Scenario launches compile server-side: the browser sends the source + a dataset reference, not megabytes of flattened model. See Datasets & Scenarios.

API

The lens is backed by the /api/v2/dsl/* endpoints. All require authentication:

POST /api/v2/dsl/compile     # source (+ optional dataset) → grounded problem, or a structured error
POST /api/v2/dsl/inspect     # source → the sets, params and variable families it declares
POST /api/v2/dsl/latex       # parse-only symbolic-math rendering (the Math split pane)
POST /api/v2/dsl/deground    # flat problem → JModel draft, or null when none round-trips
POST /api/v2/dsl/generate    # description/screenshots/PDF → compile-verified JModel source

Compile-shaped endpoints return 200 with ok: false + a structured error (message + position) for invalid sources — the editor calls them on every debounced keystroke, so a bad source is a result, not an exception.