TrackingDSL

Name-driven DSL for specifying multi-agent tracking problems as time-expanded cellular sheaves. @tracking_problem declares structure first; concrete numeric values are supplied later via a context dict at resolve time. See NetworkSheaves.CellularSheafParser (the dsl.md page) for the unrelated @cellular_sheaf macro.

CellularSheaves.ControlSheaves.TrackingDSLModule
TrackingDSL

Name-driven DSL for specifying multi-agent tracking problems as time-expanded cellular sheaves.

This module provides a symbolic specification language (@tracking_problem, parse_tracking_program) that decouples the structural description of a tracking scenario from the numeric values of matrices and vectors. Names are declared first; concrete values are supplied via a context dict at resolve time. The full pipeline is:

parse → validate → resolve(ctx) → lower → TrackingProblem

Quickstart

using CellularSheaves
using CellularSheaves.ControlSheaves.TrackingDSL

prog = @tracking_problem begin
    space(X) = R^2
    space(U) = R^1
    map_decl(A, X, X)
    map_decl(B, U, X)
    map_decl(R_y, X, X)
    agent(a1; dynamics=(A,B), period=dt)
    agent(a2; dynamics=(A,B), period=dt)
    target(t1)
    horizon(K)
    times(Tall = 0:K)
    consensus(c1; agents=(a1,a2), maps=(R_y,R_y), at=Tall)
    track(tr1; agent=a1, target=t1, maps=(R_y,R_y), at=Tall[end])
    boundary(:agent, a1; at=Tall[begin], value=x0_a1)
end

ctx = Dict{Symbol,Any}(
    :K    => 10,
    :A    => [1.0 0.0; 0.0 1.0],
    :B    => [0.0; 1.0;;],
    :R_y  => [1.0 0.0; 0.0 0.0],
    :dt   => 0.05,
    :x0_a1 => [0.0, 0.0],
)
result = lower_tracking_program(prog, ctx)
sheaf  = build_time_expanded_tracking_sheaf(result.problem)

Special time references

t[begin] always resolves to 0. t[end] resolves to the value of the horizon k declared with horizon K (after K is bound in ctx). The qualifying name t is syntactic decoration and is ignored.

Sub-modules

Sub-moduleRole
TrackingDSLTermAST node definitions and exception types
TrackingDSLParser@tracking_problem macro and parse_tracking_program
TrackingDSLValidatorvalidate_tracking_program semantic checks
TrackingDSLResolverresolve_tracking_program, set_indexed!, ResolvedProgram
TrackingDSLLoweringlower_tracking_program, LoweredTrackingProblem
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLTermModule
TrackingDSLTerm

Abstract data type (AST) definitions for the name-driven Tracking DSL.

The DSL allows symbolic specification of multi-agent tracking problems with late binding of numeric values via an external context dict. A TrackingProgram is the root node holding a flat list of TrackingStmt nodes. Statements cover:

  • Space and map declarations (space X = R^6, map A : X -> X)
  • Agent/target declarations with optional dynamics (agent a1 dynamics (A, B) period dt)
  • Time declarations (horizon K, times Tall = 0:K)
  • Constraints (consensus, track, consensus_sheaf)
  • Boundary conditions (boundary agent a1 at t[begin] = x0)
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLTerm.TimeRefType
TimeRef

A reference to a single point in time. Can be:

  • LiteralTime(n) — an integer literal (e.g. 0, 7)
  • NamedTime(name) — a named time alias (e.g. tcapture)
  • BeginTime() — the special alias resolving to 0 (written t[begin] in the DSL)
  • EndTime() — the special alias resolving to k from horizon (written t[end] in the DSL)
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLTerm.TimeSpecType
TimeSpec

Describes the set of timesteps at which a constraint is active. Variants:

  • SingletonTime(t) — activate at exactly one TimeRef
  • TimeList(ts) — activate at an explicit list of TimeRefs
  • TimeRange(lo, hi) — a range lo:hi of TimeRefs
  • NamedTimeSet(name) — reference to a declared times alias
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLParserModule
TrackingDSLParser

MLStyle-driven parser for the name-driven Tracking DSL.

Provides the @tracking_problem macro and the functional parse_tracking_program entry point. All statement forms use valid Julia syntax so that Julia's own parser tokenises the block before any DSL parsing occurs.

Grammar

Statements inside a @tracking_problem begin ... end block must be valid Julia expressions. The DSL interprets them by their leading function name:

@tracking_problem begin
    # Space declaration (method-definition syntax)
    space(X) = R^6
    space(U) = R^2
    space(Stalk) = X ⊕ U      # direct sum; also X * U or X × U

    # Map declaration  (use map_decl to avoid shadowing Base.map)
    map_decl(A, X, X)          # A : X → X
    map_decl(B, U, X)          # B : U → X
    map_decl(R_y, Stalk, R^1)

    # Agent / target declarations
    agent(a1; dynamics=(A,B), period=dt)
    agent(a2; dynamics=(A,B), period=dt)
    target(t1)
    target(t2)

    # Horizon
    horizon(K)

    # Time aliases (keyword-argument syntax)
    time(tcapture = 7)

    # Time sets
    times(Tall = 0:K)

    # Constraints
    consensus(c1; agents=(a1,a2), maps=(R_y,R_y), at=Tall)
    track(tr1; agent=a1, target=t1, maps=(R_z,R_z), at=Tall[end])
    consensus_sheaf(cS; template=CTemplate, over=(a1,a2,a3), at=tcapture)

    # Boundary conditions
    boundary(:agent, a1; at=Tall[begin], value=x0_a1)
    boundary(:target, t1; at=t, value=x_ref[t])
    boundary(:agent, a; at=t, value=x[a,t])   # indexed reference
end

Time specifications

At any at= keyword, time can be given as:

  • a named time set: at=Tall (NamedTimeSet)
  • begin/end of horizon: at=t[begin] (BeginTime = 0), at=t[end] (EndTime = k)
  • a literal integer: at=0
  • a range: at=(0:K)
  • an explicit list: at=[0,3]

t[begin] and t[end] use Julia array-indexing syntax; the qualifying name t is ignored and the references resolve to 0 and the horizon value k respectively.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLParser.parse_tracking_programMethod
parse_tracking_program(block::Expr) -> TrackingProgram

Functional entry point for parsing a TrackingDSL program from a Julia Expr (as returned by Meta.quot(quote ... end)).

Returns a TrackingProgram AST without validating or resolving names. Supply numeric values via the ctx argument of resolve_tracking_program or lower_tracking_program:

prog = parse_tracking_program(quote
    agent(a1; dynamics=(A,B), period=dt)
    horizon(K)
    times(Tall = 0:K)
end)
ctx = Dict{Symbol,Any}(:K => 40, :A => [1.0 0.0; 0.0 1.0], :B => [0.0; 1.0;;], :dt => 0.05)
resolved = resolve_tracking_program(prog, ctx)

Example — indexed boundary reference

prog = parse_tracking_program(quote
    agent(a1; dynamics=(A,B), period=dt)
    horizon(K)
    boundary(:agent, a1; at=t_pin, value=x_ref[a,t_pin])
end)
ctx = Dict{Symbol,Any}(:K => 5, :A => ..., :B => ..., :dt => 0.05, :a => 1, :t_pin => 3)
set_indexed!(ctx, :x_ref, 1, 3, [0.0, 1.0, 0.0, 0.0])
resolved = resolve_tracking_program(prog, ctx)

t[begin] resolves to 0; t[end] resolves to the horizon K.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLParser.@tracking_problemMacro
@tracking_problem begin ... end

Parse a TrackingDSL program and return a TrackingProgram AST.

Statements are valid Julia expressions interpreted by their leading name. Numeric values are not embedded in the program; pass them via the ctx argument of resolve_tracking_program or lower_tracking_program.

The special time references t[begin] and t[end] are built-in: t[begin] resolves to 0; t[end] resolves to the horizon value k (from horizon(K) in the program and :K in the context dict). The qualifying name t is syntactic decoration and is ignored.

Example

prog = @tracking_problem begin
    space(X) = R^2
    map_decl(A, X, X)
    map_decl(R_y, X, X)
    agent(a1; dynamics=(A,R_y), period=dt)
    agent(a2; dynamics=(A,R_y), period=dt)
    target(t1)
    horizon(K)
    times(Tall = 0:K)
    consensus(c1; agents=(a1,a2), maps=(R_y,R_y), at=Tall)
    track(tr1; agent=a1, target=t1, maps=(R_y,R_y), at=Tall[end])
    boundary(:agent, a1; at=Tall[begin], value=x0_a1)
end

ctx = Dict{Symbol,Any}(
    :K    => 40,
    :A    => [1.0 0.0; 0.0 1.0],
    :R_y  => [1.0 0.0; 0.0 0.0],
    :dt   => 0.05,
    :x0_a1 => zeros(2),
)
result = lower_tracking_program(prog, ctx)

t[begin] resolves to 0; t[end] resolves to the value of K.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLValidatorModule
TrackingDSLValidator

Semantic validation for TrackingProgram ASTs.

validate_tracking_program checks:

  1. No duplicate declarations of the same name.
  2. All referenced names in constraints and boundaries are declared.
  3. Time sets reference declared time aliases.
  4. Agent dynamics reference declared maps.
  5. Consensus/track constraints reference declared agents/targets.
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLValidator.validate_tracking_programMethod
validate_tracking_program(prog::TrackingProgram)

Validate the TrackingProgram AST for semantic consistency.

Raises a TrackingDeclarationError, TrackingTypeError, or TrackingTimeResolutionError if validation fails. Returns prog unchanged on success so it can be used in a pipeline:

ctx = Dict{Symbol,Any}(:K => 5, :A => ..., :B => ..., :dt => 0.05)
lower_tracking_program(prog, ctx)

Validation checks:

  • No duplicate names across spaces, maps, agents, targets, time aliases, constraints.
  • All names referenced in constraints exist as declarations.
  • t[begin] and t[end] are always valid (built-in time references).
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLResolverModule
TrackingDSLResolver

Name resolution for TrackingProgram ASTs.

Resolution produces a ResolvedProgram that contains all numeric values needed by the lowering pass. The t[begin] and t[end] time references are first-class: t[begin] resolves to 0; t[end] resolves to the value of the horizon k.

All numeric values (matrices, scalars, vectors) are supplied through an external context dict passed to resolve_tracking_program. Use set_indexed! to register indexed boundary values.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLResolver.resolve_tracking_programMethod
resolve_tracking_program(prog::TrackingProgram, ctx::AbstractDict) -> ResolvedProgram

Resolve all symbolic names in prog to concrete numeric values using the supplied context dict ctx.

ctx maps Symbol keys to their values (scalars, matrices, vectors). Use set_indexed! to register indexed boundary values:

ctx = Dict{Symbol,Any}(
    :K  => 5,
    :A  => [1.0 0.0; 0.0 1.0],
    :B  => [0.0; 1.0;;],
    :dt => 0.05,
)
set_indexed!(ctx, :x_ref, 1, 3, [1.0, 2.0, 0.0])  # x_ref[a=1, t=3]
resolved = resolve_tracking_program(prog, ctx)

Resolution steps:

  1. Build value environment from ctx.
  2. Resolve the horizon k.
  3. Resolve space dimensions.
  4. Resolve maps to concrete matrices.
  5. Resolve agent dynamics.
  6. Resolve time specs to integer vectors.
  7. Resolve boundary conditions (including indexed references x[a,t]).

Raises TrackingUnboundSymbolError if any required name is missing from ctx. Raises TrackingDimensionMismatchError for inconsistent matrix dimensions.

t[begin] resolves to 0; t[end] resolves to the value of the horizon K.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLResolver.set_indexed!Method
set_indexed!(ctx, name, agent_val, time_val, vec)

Store an indexed boundary value in ctx under the canonical key (name, agent_val, time_val). Use this to supply values for boundary conditions declared with an indexed reference like boundary(:agent, a; at=t, value=x[a,t]).

Because the key is a Tuple, ctx must support non-Symbol keys. Use Dict{Any,Any} (not Dict{Symbol,Any}) when indexed values are needed:

ctx = Dict{Any,Any}(:K => 5, :A => ..., :B => ..., :dt => 0.05, :a => 1, :t_pin => 3)
set_indexed!(ctx, :x_ref, 1, 3, [0.0, 1.0, 0.0])
resolved = resolve_tracking_program(prog, ctx)
source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLLoweringModule
TrackingDSLLowering

Lowers a ResolvedProgram to the concrete TrackingProblem used by MultiAgentTracking.build_time_expanded_tracking_sheaf.

The main entry points are lower_tracking_program (returns a TrackingProblem plus a boundary dictionary) and the convenience wrapper that runs the full parse → validate → resolve → lower pipeline.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLLowering.lower_tracking_programMethod
lower_tracking_program(resolved::ResolvedProgram;
    consensus_weight = 1.0,
    tracking_weight  = 1.0,
    include_target_dynamics = false) -> LoweredTrackingProblem

Lower a ResolvedProgram to a TrackingProblem and a boundary dictionary.

The result .problem is directly usable with build_time_expanded_tracking_sheaf(result.problem).

Restrictions maps for multiple consensus constraints are combined into a single shared consensus_restriction matrix (first declared wins); use the per-constraint map from ResolvedConsensus if per-edge heterogeneity is required.

For uniform consensus with a single map pair, the map is used directly. When multiple distinct maps are present, the first is stored in TrackingProblem.consensus_restriction and applied to all consensus edges.

Example

prog = parse_tracking_program(quote
    space X = R^2
    map A : X -> X
    map B : X -> X
    map R_y : X -> X
    agent a1 dynamics (A, B) period dt
    agent a2 dynamics (A, B) period dt
    target t1
    horizon K
    times Tall = 0:K
    consensus c1 between (a1, a2) using (R_y, R_y) at Tall
    track tr1 agent a1 target t1 using (A, A) at t[end]
    boundary agent a1 at t[begin] = x0_a1
end)
ctx = Dict{Symbol,Any}(
    :K     => 5,
    :A     => [1.0 0.0; 0.0 1.0],
    :B     => [0.0; 1.0;;],
    :R_y   => [1.0 0.0; 0.0 0.0],
    :dt    => 0.05,
    :x0_a1 => [0.0, 0.0, 0.0],
)
result = lower_tracking_program(prog, ctx)

t[begin] always resolves to 0; t[end] resolves to the horizon value K.

source
CellularSheaves.ControlSheaves.TrackingDSL.TrackingDSLLowering.lower_tracking_programMethod
lower_tracking_program(prog::TrackingProgram, ctx::AbstractDict;
    consensus_weight = 1.0,
    tracking_weight  = 1.0,
    include_target_dynamics = false) -> LoweredTrackingProblem

Convenience entry point that runs the full validate → resolve → lower pipeline in one call.

prog = parse_tracking_program(quote
    space X = R^2
    map_decl(A, X, X)
    map_decl(B, X, X)
    agent a1 dynamics (A, B) period dt
    agent a2 dynamics (A, B) period dt
    horizon K
    times Tall = 0:K
    consensus c1 between (a1, a2) using (A, A) at Tall
end)
ctx = Dict{Symbol,Any}(:K => 5, :A => I(2), :B => reshape([0.0,1.0],2,1), :dt => 0.05)
result = lower_tracking_program(prog, ctx)
source