Graph Authoring
Grid graphs are immutable, directed or undirected property multigraphs. They retain stable node and edge identity, property schemas, deterministic ordering, and explicit work bounds. A graph…
Graph Authoring
Grid graphs are immutable, directed or undirected property multigraphs. They retain stable node and edge identity, property schemas, deterministic ordering, and explicit work bounds. A graph is a named resident value rather than a workbook cell or spilled array.
Use graphs when topology is part of the problem: routes, dependencies, communities, flows, reachability, propagation, or relationships that cannot be represented honestly as independent table rows.
Declaring A Graph
Use an exact graph named binding:
frame road_rows = READ_PARQUET("roads.parquet")
graph roads = GRAPH(
road_rows AS edges,
"road_id" AS edge_id,
"origin" AS from,
"destination" AS to,
["minutes", "capacity", "build_cost"] AS edge_properties,
TRUE AS directed
)
edges, edge_id, from, and to are required. The edge source may be a
Frame, finite range, or literal row array. Stable edge IDs are mandatory.
Optional nodes and node_id bindings add isolated nodes.
node_properties and edge_properties select retained properties; when they
are omitted, Grid retains non-binding columns.
Parallel edges and self-loops are supported. Construction rejects missing endpoints, duplicate edge IDs, invalid keys, and inconsistent rows.
Node identity is exact and type-sensitive. Strings and checked integers are valid node keys. Blanks, floating-point identity, and implicit text coercion are rejected.
Graph values must use unqualified named bindings. They cannot be assigned to a
cell, range, sheet-qualified target, input, output, default, lazy
assignment, or scheduled assignment.
GRAPH also accepts one native parse_tree source. This overload derives a
directed parent-to-child property Graph at runtime from the exact tree cell
revision:
tree = PARSE_RESULT_TREE(PARSE(source_text, parser))
graph syntax = GRAPH(tree)
Parse-tree Graphs use the common resident Graph algorithms and Predicate
projection path. Their node IDs are deterministic one-based preorder ordinals
within a tree revision; child order is the explicit ordinal edge property,
not adjacency iteration order. See
Grammars And Parsers for the
complete property and provenance schema.
Relating Model Symbols
MODEL.GRAPH contains ordinary formula dependencies plus an authored
relationship layer. Use relates to for a directed relationship and
connects to for one bidirectional logical relationship:
predicate shipment(qty: number)
factory = 1200 relates to warehouse via :shipment(12)
warehouse = 800 connects to retailer via :peer
frame downstream = MODEL.GRAPH.BFS(:factory)
A relationship following an assignment inherits that assignment target as its
source; it does not relate the assigned value. Relationships may also be
standalone statements for symbols declared elsewhere. They are static model
metadata: lazy, conditional, scheduled, default, or state assignment
lifecycle does not make the edge conditional. Repeating a source, target,
direction, and kind creates distinct parallel relationships with stable opaque
IDs.
relates to creates one directed edge. connects to creates a bidirectional
relationship whose two traversal arcs share one logical relationship ID.
Predicate declarations type relationship payloads in the same way they type
unary predicate payloads.
Typed Property Schemas
Use a closed schema when algorithms and property access should be checked before execution:
frame city_rows = READ_PARQUET("cities.parquet")
graph schema Roads {
node kind City
edge kind Road: City -> City
node.kind: symbol
node.population: number
edge.kind: symbol
edge.minutes: number
edge.capacity: number
edge.open: boolean
}
graph roads: Roads = GRAPH(
city_rows AS nodes,
"city_id" AS node_id,
["kind", "population"] AS node_properties,
road_rows AS edges,
"road_id" AS edge_id,
"origin" AS from,
"destination" AS to,
["kind", "minutes", "capacity", "open"] AS edge_properties
)
Schema entries use node.<path>: <type> or edge.<path>: <type>. A trailing
? marks an optional property. Nested paths are allowed. Property types use
normal Grid tags, including semantic and dimensional tags whose values can be
represented as graph properties.
Named node and edge kinds constrain valid endpoints. A schema that declares
kinds must include total node.kind and edge.kind string or symbol
properties. Unknown kinds, missing required values, undeclared properties, and
invalid endpoint combinations are errors.
Selectors, objectives, patterns, comprehensions, rewrites, and fixed points are checked against the declared schema. Numeric objectives require non-optional numeric properties. Transformations that preserve source properties also preserve the source schema.
Bare graph bindings remain available when a closed schema is unnecessary.
Querying A Graph
Method form is preferred:
node_count = roads.NODE_COUNT()
out_degree = roads.OUT_DEGREE("Denver")
connected = roads.HAS_PATH("Denver", "Boulder")
frame fastest = roads.SHORTEST_PATH(
"Denver",
"Boulder",
"minutes" AS weight
)
frame ranks = roads.PAGE_RANK(
"capacity" AS weight,
0.85 AS damping,
1e-8 AS tolerance,
100 AS max_iterations
)
Scalar operations include NODE_COUNT, EDGE_COUNT, DENSITY, DEGREE,
IN_DEGREE, OUT_DEGREE, HAS_PATH, HAS_EULERIAN_PATH, and
TRIANGLE_COUNT.
Frame-producing operations include NODES, EDGES, NEIGHBORS,
IN_NEIGHBORS, BFS, DFS, SHORTEST_PATH, BELLMAN_FORD,
WEAK_COMPONENTS, STRONG_COMPONENTS, TOPOLOGICAL_ORDER, PAGE_RANK,
bounded DIAMETER, ARTICULATION_POINTS, BRIDGES,
CLOSENESS_CENTRALITY, BETWEENNESS_CENTRALITY, K_CORE,
CLUSTERING_COEFFICIENTS, and MAXIMUM_BIPARTITE_MATCHING.
Weighted algorithms name an edge property for each call. SHORTEST_PATH
rejects negative weights; use BELLMAN_FORD when negative weights are part of
the model. Iterative and all-sources operations require convergence or source
bounds where documented.
Projecting A Graph Into Predicate Knowledge
Use KNOWLEDGE GRAPH when a resident property graph should supply extensional
facts to Grid's Predicate engine:
graph knowledge = GRAPH(
relationship_rows AS edges,
"edge_id" AS edge_id,
"subject" AS from,
"object" AS to,
["relation", "confidence"] AS edge_properties,
TRUE AS directed
)
KNOWLEDGE GRAPH knowledge VIA edge.relation WITH PROPERTIES
predicate reaches is transitive
connected = MUST("origin" reaches "destination")
The selected text or symbol edge property names each open binary predicate.
WITH PROPERTIES also exposes typed graph_node, graph_edge,
graph_node_property, and graph_edge_property facts for ordinary Predicate
rules. The projection reads the current graph revision, preserves parallel-edge
identity and proof provenance, and never copies inferred closure back into the
source graph. See Predicates for query, evidence,
and WHY semantics.
This projection is distinct from the built-in views: MODEL.GRAPH describes
model dependencies and explicit model relationships, while
MODEL.PREDICATES describes authored Predicate state.
Declarative Paths
Use a first-class path when the intent is an objective rather than a specific
algorithm:
path fastest = path in roads from "Denver" to "Boulder"
minimize sum(edge.minutes)
limit 40 AS hops
path resilient = path in roads from "Denver" to "Boulder"
maximize min(edge.capacity)
path fewest_transfers = path in roads from origin to destination
minimize hops
Grid selects an appropriate plan from the graph properties, objective, and bounds. The selected plan may be inspected, but it is not part of source meaning.
Path values are bound to the source graph revision. Read scalar and tabular projections explicitly:
cost = fastest.cost
hops = fastest.hops
frame fastest_nodes = fastest.nodes
frame fastest_edges = fastest.edges
path.nodes contains (ordinal, node). path.edges contains (ordinal, edge, from, to) followed by retained edge properties. A missing path produces
blank scalar projections and empty Frames.
Partitions
Connectivity, core decomposition, and community detection produce the same partition value:
partition weak = partition nodes of roads by connectivity(weak AS direction)
partition cores = partition nodes of roads by core_number
partition core_four = partition nodes of roads by k_core(4 AS k)
partition regions = partition nodes of roads by community(
leiden AS method,
modularity(1 AS resolution) AS objective,
edge.capacity AS weight,
symmetrize(sum) AS direction,
0 AS seed,
all AS hierarchy,
50 AS max_passes,
16 AS max_levels,
0.000000001 AS tolerance
)
Leiden is the recommended community method. louvain is available as an
explicit compatibility choice. Directedness is never guessed: choose
directed, undirected, or an explicit symmetrization policy.
Partition projections are Frames:
frame membership = regions.members
frame summaries = regions.summary
Membership contains (node, partition). Summary contains (partition, size, first_node, label). IDs and rows use canonical deterministic order.
Bounded Pattern Matching
Graph patterns always require an output bound:
frame alternatives = match roads limit 1000:
(a)-[first where first.open = TRUE]->(b)-[second where second.open = TRUE]->(c)
return a as origin, b as via, c as destination,
first.minutes as first_minutes, second.minutes as second_minutes
Node and edge bindings are statically scoped. Predicates use typed properties, and projections require unique aliases. Repeating a binding with the same role expresses identity equality:
frame mutual = match roads limit 1000:
(a)-[out]->(b)-[back]->(a)
return a.id as origin, b.id as peer
A binding cannot change between node and edge roles. Pattern output is a Frame; use ordinary relational operations afterward for deduplication, grouping, aggregation, ordering, or windows.
Comprehensions And Rewrites
A graph comprehension creates a property-preserving filtered graph:
graph active: Roads = roads {
node city where city.population > 0
edge road where road.open = TRUE
}
An immutable rewrite applies a bounded set of graph edits and publishes either the complete revised graph or an error. The current form deletes one matched edge binding:
graph normalized: Roads = rewrite roads limit 100000:
match (a)-[road where road.open = FALSE]->(b)
delete road
limit bounds distinct stable edge IDs. If the edit set exceeds the limit,
the rewrite fails without publishing a partial graph.
Bounded Fixed Points
Use fixpoint for repeated propagation over graph topology:
frame minutes_from_origin = fixpoint in roads:
using min_plus(edge.minutes AS weight)
along AS direction
1000000000000 AS default
0 AS seed origin
until max_delta < 0.000000001
within 100 iterations
Available operators are sum_product, min_plus, max_min, and
boolean_or. Every fixed point requires at least one seed and a positive
literal iteration bound. Direction, defaults, seeds, convergence, and numeric
requirements are validated before execution.
Transformations
Graph-producing operations must be rebound as graphs:
graph reverse_roads = roads.REVERSE()
graph least_cost_network = roads.MINIMUM_SPANNING_TREE("build_cost" AS weight)
graph regional = roads.INDUCED_SUBGRAPH(region_node_ids)
graph quotient = contract roads by regions
Each result is an immutable graph revision. Contraction turns each partition into a node, removes internal edges, and preserves cross-partition edge IDs and properties. The partition must belong to the exact source revision.
Typed Domain Projections
Host APIs can project one exact resident graph revision into domain-owned Geo routing, min-cost-flow, and simulation validators and kernels. Each projection uses an explicit typed schema, rejects missing or incompatible properties, and retains the source graph handle, revision, and fingerprint with its result. There is no untyped intermediate representation and no author-side cast syntax; the host operation defines the required node and edge properties.
Use a closed graph schema when the same graph will cross a domain boundary. That lets Grid reject incompatible topology or property values during graph construction, before a host requests the projection.
Resident Updates And Durable Patches
Graph declarations and graph-producing expressions remain immutable language values. A hosting surface may apply an explicit atomic patch to a primary resident GraphHandle; derived handles are read-only and are invalidated when their primary source changes. Predicate projections and dependent graph results then recompute from the new exact revision.
Accepted hosted patches append to the model-local durable patch journal. Optimistic revision guards and optional idempotency keys make retries deterministic, while an invalid batch leaves the prior snapshot unchanged. This is a host/API mutation surface, not graph formula syntax. The model API defines the patch shapes, supported topology changes, persistence, and recovery contracts.
Results, Ordering, And Bounds
Graph values do not spill into workbook cells. Scalar projections may be bound to cells or names; tabular projections are Frames and require an explicit conversion when workbook cells are needed.
Graph operations use canonical type-sensitive node order, stable edge IDs, and source order as deterministic tie-breakers. Results do not depend on insertion order or worker timing.
Operations enforce limits on nodes, edges, resident size, result rows, work, iterations, temporary memory, deadlines, and cancellation. Approximate all-sources operations report or retain the author-supplied source bound. Exceeded limits produce diagnostics rather than silent truncation or partial edits.
DENSITY measures the simple loop-free projection: parallel edges count once
and self-loops do not count. Graphs with fewer than two nodes report zero.
For exact signatures, use the functions.md catalog. For
querying Graph-produced Frames, see
relational-authoring.md. For the complete feature
status, see features.md.