Skip to main content

zinet

Graph IR core package shared across SPL, ConceptBook, Graph-Code, Momagrid, and Solver domains.

graph.yaml is the common intermediate representation of the ZiNets ecosystem, the way LLVM IR is for compilers: front-ends emit it, back-ends consume it, and the shared validator and transforms live here once.

The format

Every graph has three layers, each a list of nodes:

version: "1.0"
imports: [./shared/tools.yaml]   # optional; resolved relative to this file
primitives:                      # leaves: must not have deps
  - id: x_units
    meta: {type: variable}
concepts:                        # composed from primitives and other concepts
  - id: capacity_constraint
    deps: [x_units, max_units]
    edge_types: {max_units: constraint}
    meta: {expression: "x_units <= max_units"}
applications:                    # capstones: nothing may depend on these
  - id: minimize_cost
    deps: [capacity_constraint]
  • deps are the ids a node depends on (incoming edges).
  • edge_types optionally types a dependency: depends_on (default), data_flow, prerequisite, constraint, inheritance.
  • meta is an opaque domain payload that the core never interprets.

Two graph formats, one family

zinet stores a static knowledge or dependency graph. Its sibling GraphLang (graf) stores an executable workflow (typed nodes with ports, control and data edges, loops) in *.graph.yaml. Both are plain YAML that persists a graph, and the file name is only a convention (a plain graph.yaml may hold either), so each tool tells the files apart by content: workflow: with nodes: and edges: is GraphLang; primitives:, concepts: or applications: as lists of nodes is zinet. Given the other kind, each tool says what the file is and which command to use, instead of a schema error. ConceptBook and graph-code write the same three layers as mappings with composed_of / needs; zinet does not read that dialect yet, but its algorithms and drawing work on any networkx graph (see below). More in docs/DEV/readme-ecosystem.md.

Install

From PyPI:

pip install zinet

From source, for development:

uv venv .venv && uv pip install -p .venv/bin/python -e ".[dev]"

CLI: zinet

zinet validate graph.yaml            # exit 1 on errors; warnings are printed but do not fail
zinet validate graph.yaml --strict   # also check that every concept reaches a primitive and the graph is one piece
zinet diff old.yaml new.yaml         # exit 1 when the graphs differ
zinet slice graph.yaml target_id -o part.yaml   # target plus its prerequisite closure
zinet render graph.yaml                              # svg to stdout (default)
zinet render graph.yaml --format mermaid             # mermaid flowchart text
zinet render graph.yaml --format html -o graph.html  # interactive page (search, zoom, click a node); works offline
zinet render graph.yaml --format ascii               # plain-text outline by depth
zinet render graph.yaml --format png -o graph.png    # png and pdf need -o
zinet prettify graph.yaml                            # diff against canonical formatting (exit 1 if it would change)
zinet prettify graph.yaml --write                    # rewrite in place; comments are kept
zinet convert graph.yaml                             # to graph.json; the comments are kept in a small database
zinet convert graph.json                             # back to graph.yaml, with the comments put back
zinet schema -o schemas/graph.schema.json            # JSON Schema for editors and other tools

render supports svg, png, pdf, mermaid, html and ascii. Mermaid, html and ascii need nothing installed. The svg, png and pdf formats need Graphviz's dot on PATH (apt install graphviz).

Exit code 2 means a usage, load, or render error. prettify exits 1 when a file is not in canonical form, so it can run in CI.

Specification

The format is specified in docs/SPECIFICATION.md. The machine-readable JSON Schema is schemas/graph.schema.json, regenerated with zinet schema -o schemas/graph.schema.json. A test fails if the committed schema is out of date.

Python API

from zinet import load_graph, validate, diff_graphs, slice_to, dump_graph

graph = load_graph("solver_pipeline.yaml")   # resolves imports
for issue in validate(graph):
    print(issue)
prereqs = slice_to(graph, "minimize_cost")

Graph algorithms

Plain-networkx functions that the apps share (graph-code, ConceptBook and others had their own copies). The input is a networkx.DiGraph with edges from a dependency to its dependent and an optional kind attribute per node (primitive, concept, application; none means "referenced but never declared"). to_networkx(graph) builds one from a zinet Graph.

import zinet
G = zinet.to_networkx(zinet.load_graph("graph.yaml"))
zinet.learning_path(G, "app", known=["a"])        # what is still needed to reach `app`, dependencies first
zinet.productivity_order(G)                       # order that unlocks the most nodes as early as possible
zinet.reducible(G)                                # does everything rest on primitives?
zinet.build_order(G), zinet.gap(G, "app", ["a"]), zinet.applications_of(G, "a"), zinet.restrict(G, nodes)
zinet.ensure_acyclic(G)                           # raises CycleError, naming the nodes on the cycle

The options select the behaviour of the code these functions replace: include_primitives, include_target, order="topological"|"alphabetical"|"reach", weighted_applications, seed_by_reach, and four reducible rules (primitive_ancestry, declared, declared_or_listed, named_primitives). Defaults follow graph-code. A missing node raises UnknownNodeError (a KeyError) and a cycle raises CycleError. What each copy did, and where they differ, is frozen in tests/characterization/ (see divergences.md). topological_order and ancestors also accept a DiGraph.

Comments

prettify --write keeps your YAML comments. Each comment is pinned to what it sits next to (a node by id, a key of that node, or a top-level section; a comment block at the top of the file stays on top) and put back into the canonical text. If any comment cannot be put back exactly, the file is not rewritten.

zinet convert goes between YAML and JSON. JSON has no comments, so the YAML's comments are stored in a small SQLite database (~/.zinet/comments.db, or $ZINET_HOME, or --store PATH) and put back when you convert the JSON to YAML again. A comment whose node was removed or renamed is written at the end of the file as # [orphaned from <id>] ... and returns if the node does. This is the approach work-graph uses for its layer files (the same code, see zinet.roundtrip); there is nothing to install.

Drawing any graph

zinet.vis draws and trims any networkx graph, not only zinet's, so other tools do not each carry their own copy (GraphLang and work-graph did). A Style is a set of small functions that read your attribute names (node label, kind, colour, shape, group; edge label and dashing), so no app has to rename anything.

from zinet.vis import render, select, zinet_style
G = zinet.to_networkx(zinet.load_graph("graph.yaml"))
render(G, "graph.html")                         # engine="cytoscape" (default) | "graphviz" | "mermaid" | "ascii" | "pyvis" | "graphify"
sub, note = select(G, node="app", hops=2)       # a readable neighbourhood of a big graph: filters, hidden kinds, hub edges, a node cap

The cytoscape engine writes one HTML file with Cytoscape.js bundled (MIT, see src/zinet/vis/data/LICENSE-cytoscape.txt), so it works offline; if Graphviz is installed it also takes the node layout from dot (it keeps a group together and picks a left-to-right or top-to-bottom shape that fits the screen). graphviz writes svg, png or pdf by file extension. mermaid writes flowchart text (-o FILE.md wraps it in a fenced block). pyvis and graphify need pip install zinet[pyvis] / zinet[graphify]. GraphLang's and work-graph's output through these engines is checked against golden files in tests/characterization/.

On the interactive page, hovering a node fades everything but its connections (and shows its dashed edges), and clicking it opens its details. Two options of the Style make the first view the main flow of a big graph: secondary_hidden=True starts the dashed edges (the data wiring) behind a "show ... edges" checkbox, and groups_collapsed=True starts every group (a LOOP) as one box (click it to open it, double-click an open group to close it; "Collapse groups" / "Expand groups" do all of them). Graphviz lays out both the open and the collapsed arrangement, so the collapsed view is compact. GraphLang turns both on; they are off by default.

Checks

Code Severity Meaning
duplicate_id error an id is defined more than once
unknown_dep error a dep names an id that does not exist
primitive_has_deps error a primitive depends on something
layer_violation error a node depends on an application
cycle error the dependency graph is not a DAG
orphan warning a node with no deps and no dependents
not_reducible error optional (--check reducible): a concept or application with no primitive ancestor
disconnected warning optional (--check connected): the graph is in more than one piece when edges are read as undirected

zinet validate --strict runs both optional checks. They are off by default, so a graph that is valid today stays valid.

Layout

src/zinet/
  schema.py       Pydantic models: Graph, Node, Layer, EdgeType
  dag.py          topological order, ancestors (domain-agnostic)
  validator.py    structural checks -> Issue list
  io.py           load_graph (with imports), dump_graph, to_yaml
  prettify.py     canonical formatting for `zinet prettify`
  roundtrip.py    comment-preserving YAML (harvest and re-attach), SQLite comment store, `zinet convert`
  transforms/     diff_graphs, slice_to
  algorithms.py   graph algorithms on a plain networkx DiGraph (learning path, order, reducible, ...)
  render.py       DOT and Mermaid generation for zinet graphs, svg/png/pdf via `dot`
  vis/            drawing for any networkx graph: engines, `select`, `Style` (see "Drawing any graph")
  cli.py          the `zinet` command
docs/SPECIFICATION.md   the graph.yaml format specification
schemas/graph.schema.json   generated JSON Schema
tests/            pytest suite; tests/fixtures holds one graph per domain

Design decisions

  • Imports merge nodes without namespacing, so ids must be unique across the whole import tree.
  • load_graph does not validate. Run validate separately for structural checks.
  • Unknown top-level and node keys are rejected, so typos fail loudly. Domain data goes in meta.
  • Only the core checks are here. Domain rules (for example, solver bounds) belong in the back-end.

Not yet implemented

  • Domain-specific JSON Schema overlays (solver, spl, cba).
  • graph.html interactive explorer (belongs to cba).
  • spl3 integration, which is the next step: SPL scripts compiling to GraphIR.

Metadata

Release files for zinet 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zinet 0.2.0
File Size Uploaded Provenance
zinet-0.2.0.tar.gz 205.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for zinet 0.2.0
File Interpreter ABI Platform
zinet-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 379.6 kB

Release files / zinet-0.2.0.tar.gz

Download URL zinet-0.2.0.tar.gz
Size 205.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2ba731df117a90b1187ddfb07b47fc58b7b764010784a356c742b69bfe0900bf
BLAKE2b-256 checksum
How to use checksums
df5fa5851f09ebadaee08ec9ceec8c81e27906f16c9d556618b125d814b06283
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.5

Release files / zinet-0.2.0-py3-none-any.whl

Download URL zinet-0.2.0-py3-none-any.whl
Size 173.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6c10be2e06d70515189c27611891af81b5ec246f49bbb45ca37fd0f89c96cf11
BLAKE2b-256 checksum
How to use checksums
66a28009d086f1f256fad960fe91409c7f9251f2e894c476256304b1058abfaa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.5

Security for zinet 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page