Skip to content

layout: dependency-graph extraction misses lookup-table references, so lookup tables appear as isolated nodes with no connectors #650

@bpowers

Description

@bpowers

Problem

The layout dependency-graph extraction does not record an edge from a graphical-function / lookup-table holder variable to the variables that use it. As a result, lookup tables that are genuinely referenced by other variables appear as isolated (unconnected) nodes in generated layouts: no connector is drawn between a lookup table and its caller, and the table gets classified as "isolated" by the isolated-variable parking pass.

Concrete example

In test/metasd/WRLD3-03/wrld3-03.mdl, variables like land yield multiplier from capital table are graphical-function / lookup-table holders that ARE used by other variables. For instance land yield multiplier from capital calls them Vensim-style:

Land Yield Multiplier from Capital Table(capital ratio)

which the importer converts to a LOOKUP(table, x)-style call. The layout dependency graph never records a table -> caller edge, so:

  • Consequence 1: no connector is drawn between the lookup table and the variable that uses it.
  • Consequence 2: these tables are classified as "isolated" by the isolated-variable parking pass, and get parked in rows below the diagram (tidy, but they are not actually isolated -- they have a real consumer).

WRLD3-03 has ~60 such * table variables, so this is visually significant on real Vensim models -- a meaningful amount of structural information is missing from the diagram.

Why it matters

Missing connectors mean missing information in the diagram. A modeler reading a generated layout cannot see that a lookup table feeds a particular variable, and the "isolated" classification mislabels variables that are actually wired into the model. This is a layout-quality issue, not a simulation-correctness issue (the engine simulates these references correctly).

Components affected

  • src/simlin-engine/src/layout/mod.rs:
    • extract_equation_deps (the string-heuristic fallback dep extraction, ~line 3985)
    • compute_metadata (the dep-graph build, ~line 4265; the AST-based path via crate::db::variable_direct_dependencies at ~line 4385 with the extract_equation_deps fallback at ~line 4424)
    • the isolated-variable derivation (build_full_graph / park_isolated_nodes, ~lines 2430, 2526)

Likely root cause / fix area

The dep extraction needs to treat a Vensim-style table_name(arg) call and/or a LOOKUP(table_name, arg) builtin call as creating a table_name -> caller dependency edge.

Two paths exist and should both be checked:

  1. AST-based path (compiled model): variable_direct_dependencies may already classify the table as a dependency of its caller -- if so, the issue is confined to the fallback path. This needs verification, because lookup-only tables are now treated as non-value-bearing static tables (engine: standalone lookup-only variable is lowered to gf(Time) (unit error; phantom Time-indexed series for a bare graphical function) #606) reached only through LOOKUP(table, x) call sites resolved by ident, so it is worth confirming the table ident still shows up in the caller's dt_deps/initial_deps.
  2. String-heuristic fallback path (extract_equation_deps): contains_ident does a word-boundary substring match over the lowercased equation text, so a reference like Land Yield Multiplier from Capital Table(capital ratio) should already match the table ident -- this needs confirmation against the actual canonicalized idents (spaces -> underscores, etc.). WRLD3-03 has unit errors, so it may be on the fallback path; the observed missing edges suggest something in this chain is not matching.

The reproduction below should be used to determine which path WRLD3-03 takes and where exactly the edge is dropped.

Reproduction

Generate a layout for wrld3_03 via the layout eval harness and observe that * table variables have no connectors:

LAYOUT_EVAL_MODELS=wrld3_03 cargo run --release -p simlin-engine \
  --features png_render,file_io --example layout_eval

Inspect the rendered PNG / generated view: the ~60 * table variables are parked as isolated nodes with no connectors to their callers.

Possible approaches

  • Confirm whether the AST path (variable_direct_dependencies) records the table -> caller edge for a LOOKUP/Vensim-style lookup call; if it does not, ensure lookup-table idents are emitted as dependencies of their callers.
  • If the issue is in the string-heuristic fallback, fix extract_equation_deps / contains_ident so a table_name(arg) or LOOKUP(table_name, arg) reference produces the table_name dependency (accounting for canonicalization of multi-word table names).
  • Add a regression test (e.g. in src/simlin-engine/tests/layout.rs) asserting that a model with a referenced lookup-only table produces a connector from the table to its caller and that the table is NOT classified as isolated.

How it was discovered

Identified during layout hill-climbing work on branch layout-hill-climb, while investigating why ~60 * table variables in WRLD3-03 were being parked by the new isolated-variable parking pass.

Related (distinct) issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingengineIssues with the rust-based simulation enginelayoutDiagram auto-layout engine

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions