Build and visualize orbit graphs for discrete iterations under guarded rules.
Project description
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())
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 (ormake nativeto 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 requiredbind=), compiled on the fly withrustcand 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]") —.vitgraphfiles (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
.vitexamples organized by topic (basics/,rendering/,integration/,applications/,rust/).
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file visiter-0.20.0.tar.gz.
File metadata
- Download URL: visiter-0.20.0.tar.gz
- Upload date:
- Size: 559.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ce7e28ed5b51f10ae39ed0291defcc191ad4207d33aac6b98743c324eab30b1
|
|
| MD5 |
a864dacaad589395bf236e54e1f1662d
|
|
| BLAKE2b-256 |
83543b594b326e77ddf2e03fa8a01d8aadf06f55c1261f71bfbfcfccecab878e
|
File details
Details for the file visiter-0.20.0-py3-none-any.whl.
File metadata
- Download URL: visiter-0.20.0-py3-none-any.whl
- Upload date:
- Size: 56.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c0f175566d43fcaa7854f3f7761fe42abdc56b3cf8d07e5655c7e8181246e79
|
|
| MD5 |
41f451e2eb31b67f624a161b4e3fda78
|
|
| BLAKE2b-256 |
bf461ddb8bb3bad190ee951e25082f3652ea32ff053dd127fc983b985eca6c94
|