Skip to main content

VisIter

See what a discrete iteration actually does — as a graph.

The simplest case

Integers 1–9. Case: divisible by 3 → divide by 3. Default (everything else) → add 2. Where does each value end up?

#!/usr/bin/env viter
(viter(range(1, 10))
 .case(lambda x: x % 3 == 0, lambda x: x // 3)
 .default(lambda x: x + 2)
 .render())

descent graph, range 1–9

Save as descent.vit, run with viter descent.vit > out.svg. The #!/usr/bin/env viter shebang also lets you chmod +x descent.vit and execute the file directly.

.render() is the shortcut terminal — it builds the graph, converts to Graphviz, and writes SVG to stdout in one call.

Install

pip install visiter

Graphviz must be available on PATH (brew install graphviz / apt install graphviz).

Optional extras: pip install "visiter[native]" adds the native engine (prebuilt wheel, no toolchain needed), [storage] the columnar .vitgraph format, [validate] the JSON-Schema validator, [analytics] the NetworkX bridge. See Performance & storage below.

Going further

.render() is convenient for the common case. For anything more — cropping, custom colors, side-effects, filters — materialize the Graph explicitly via .build() and keep chaining:

#!/usr/bin/env viter
(viter(range(1, 10))
 .case(lambda x: x % 3 == 0, lambda x: x // 3)
 .default(lambda x: x + 2)
 .build()
 .to_dot(anchor=1, radius=8, direction="backward")
 .render())

Save intermediate results with .tap():

(viter(...).case(...).default(...).build()
 .tap(write(file="graph.json"))
 .to_dot()
 .render(file="out.svg"))

Use NetworkX for graph analysis via .filter():

import networkx as nx
(viter(...).case(...).default(...).build()
 .filter(NxFilter(nx.condensation))
 .to_dot()
 .render())

If-elif-else semantics (first matching case wins) via match=Match.FIRST:

(viter(range(1, 17), match=Match.FIRST)
 .case(lambda x: x % 2 == 0, lambda x: x // 2)
 .case(lambda x: x % 3 == 0, lambda x: x // 3)
 .default(lambda x: x * 5 + 7)
 .render())

Use the Python API directly (outside .vit files):

from visiter import viter

graph = (viter(range(1, 10))
         .case(lambda x: x % 3 == 0, lambda x: x // 3)
         .default(lambda x: x + 2)
         .build())
graph.to_dot().render(file="descent.svg")

Per-call edge labels via OpResult — for when the static label= is not enough and each step's edge should carry its own annotation:

from visiter import OpResult, viter

def odd_step(x):
    increased = 3 * x + 1
    div = (increased & -increased).bit_length() - 1
    return OpResult(increased >> div, label=f"3x+1, ÷2×{div}")

(viter([27])
 .case(lambda x: x % 2 == 0, lambda x: x // 2, label="÷2")
 .default(odd_step, label="3x+1")
 .render())

Performance & storage (optional)

Pure Python with JSON is the default and needs no toolchain. For large state spaces, three optional, additive features kick in — all producing the same graph, byte-for-byte:

  • Native engine (engine="auto", the default once present). pip install "visiter[native]" pulls a prebuilt wheel that runs the BFS bookkeeping natively — for every build, bounded or not — while keeping your Python callbacks (or make native to build it locally). Falls back to pure Python when absent.

  • Inline Rust callbacks (lang="rust") — for when the callbacks are the bottleneck. .case() takes Rust expression strings (value bound to the name you pass as the required bind=), compiled on the fly with rustc and run natively:

    (viter(10, lang="rust", bind="n")
     .case("n >= 1", "n - 1", label="take 1")
     .case("n >= 2", "n - 2", label="take 2")
     .render())
    
  • Columnar storage (pip install "visiter[storage]") — .vitgraph files (graph.to_vitgraph(...) / Graph.from_vitgraph(...)) are ~10–25× smaller than JSON and far faster to load for large graphs.

Details in the manual.

Why VisIter?

Free, scriptable, Graphviz-native orbit-graph rendering for discrete iterations under guarded rules — with cutoff boundaries (bounds, depth limits, render crops) as a first-class visual primitive, not silent truncation.

Full honest comparison against NetworkX, NestGraph (Mathematica), Maude, LoLA, and continuous-dynamics tooling: docs/comparison.md.

Documentation

  • docs/tutorial.md — gentle introduction: what problem the tool solves, smallest example, what each piece does, what the dashed arrows mean. Start here.
  • docs/manual.md — reference: every parameter, every data field, the rendering model in full, design decisions.
  • docs/comparison.md — how VisIter relates to other tools in the ecosystem, and when to pick something else.
  • demos/ — runnable .vit examples organized by topic (basics/, rendering/, integration/, applications/, rust/).

License

MIT

Metadata

Release files for visiter 0.20.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 visiter 0.20.0
File Size Uploaded Provenance
visiter-0.20.0.tar.gz 559.7 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 616.0 kB

Release files / visiter-0.20.0.tar.gz

Download URL visiter-0.20.0.tar.gz
Size 559.7 kB
Tags Source
SHA-256 checksum
How to use checksums
7ce7e28ed5b51f10ae39ed0291defcc191ad4207d33aac6b98743c324eab30b1
BLAKE2b-256 checksum
How to use checksums
83543b594b326e77ddf2e03fa8a01d8aadf06f55c1261f71bfbfcfccecab878e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / visiter-0.20.0-py3-none-any.whl

Download URL visiter-0.20.0-py3-none-any.whl
Size 56.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2c0f175566d43fcaa7854f3f7761fe42abdc56b3cf8d07e5655c7e8181246e79
BLAKE2b-256 checksum
How to use checksums
bf461ddb8bb3bad190ee951e25082f3652ea32ff053dd127fc983b985eca6c94
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Security for visiter 0.20.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