stzGraph: The Art of Connected Thinking
A graph is an elegant yet profound concept in computer science—a structure of nodes (entities) and edges (relationships) that illuminates how systems connect, interact, and evolve. Whether modeling workflows, codebases, data schemas, or conversational logic, stzGraph transforms intricate dependencies into intuitive understanding. With syntax that mirrors human reasoning, it makes relationships not just visible but explorable and actionable—directly from your terminal.
par Mansour Ayouni · dans le dépôt depuis le 2025-10-18 · tutoriel · domaine Diagrammes et graphes
Cet article est écrit en anglais, comme la bibliothèque.
The Beauty of Graphs
stzGraph transcends being a mere tool; it's a paradigm for systems thinking, where flows of influence, chains of causality, and cycles of interdependence become transparent and navigable. Its lightweight, dependency-free design combined with powerful analytical capabilities makes it a universal foundation for reasoning about any relational system.
Quick Start: From Vision to Reality
Build, visualize, query, and annotate a graph in moments:
oGraph = new stzGraph("MyFlow")
oGraph {
AddNode(:@start, "Start")
AddNode(:@middle, "Process")
AddNode(:@end, "End")
AddEdge(:@start, :@middle, "flows")
AddEdge(:@middle, :@end, "completes")
Show()
}illustration
Output:
╭───────╮
│ Start │
╰───────╯
|
flows
|
v
╭───────────╮
│ !Process! │
╰───────────╯
|
completes
|
v
╭─────╮
│ End │
╰─────╯
Visualize horizontally with ShowH():
╭───────╮ ╭───────────╮ ╭─────╮ │ Start │--flows-->│ !Process! │--completes-->│ End │ ╰───────╯ ╰───────────╯ ╰─────╯
Query with precision:
? oGraph.NodeCount() #--> 3 ? oGraph.PathExists(:@start, :@end) #--> TRUE
illustration
Enrich nodes with contextual metadata:
oGraph.AddNodeXT(:@middle, "Process", [
:priority = "high",
:owner = "team-a"
])illustration
stzGraph feels like a dialogue with your system—declare relationships, receive insights. No boilerplate, no dependencies, just connected reasoning.
A Natural API: Code That Mirrors Intuition
The stzGraph API is architected to align with how you naturally think about systems. Every method answers a direct question:
| Your Question | Method | Returns |
|---|---|---|
| Can A reach B? | PathExists(:@a, :@b) |
TRUE / FALSE |
| What are all paths from A to B? | FindAllPaths(:@a, :@b) |
Array of paths |
| Are there loops? | CyclicDependencies() |
TRUE / FALSE |
| What does X influence? | ReachableFrom(:@x) |
Array of nodes |
| Which nodes are critical? | BottleneckNodes() |
Array of IDs |
| How connected is the graph? | NodeDensity() |
% (0–100) |
| What's the longest chain? | LongestPath() |
Edge count |
| What connects to X? | NeighborsOf(:@x) |
Array of nodes |
| What leads to X? | IncomingTo(:@x) |
Array of nodes |
| Describe the graph | Explain() |
Structured analysis |
| Does this exist? | NodeExists(:@id) / EdgeExists(:@from, :@to) |
TRUE / FALSE |
| Show vertical view | Show() / ShowV() |
ASCII diagram |
| Show horizontal view | ShowH() / ShowHorizontal() |
ASCII diagram |
| Show with annotations | ShowWithLegend() |
ASCII + legend |
The API speaks your language—thoughts translate to code with zero friction.
Connectivity: Mapping the Possible
Every system tells a story. stzGraph reveals whether critical outcomes are reachable:
oWorkflow = new stzGraph("ApprovalProcess")
oWorkflow {
AddNode(:@request, "Request")
AddNode(:@manager, "Manager")
AddNode(:@approved, "Approved")
AddEdge(:@request, :@manager, "submit")
AddEdge(:@manager, :@approved, "finalize")
Show()
}illustration
Output:
╭─────────╮
│ Request │
╰─────────╯
|
submit
|
v
╭───────────╮
│ !Manager! │
╰───────────╯
|
finalize
|
v
╭──────────╮
│ Approved │
╰──────────╯
Query with confidence:
? oWorkflow.PathExists(:@request, :@approved) #--> TRUE
illustration
Alternative Routes: Exploring Complexity
Real systems rarely follow a single path. stzGraph exposes every possibility:
oProcess = new stzGraph("MultiPathProcess")
oProcess {
AddNode(:@start, "Start")
AddNode(:@fast, "Fast Path")
AddNode(:@standard, "Standard Path")
AddNode(:@end, "Complete")
AddEdge(:@start, :@fast, "expedited")
AddEdge(:@start, :@standard, "normal")
AddEdge(:@fast, :@end, "finish")
AddEdge(:@standard, :@end, "finish")
Show()
}illustration
Output (branches clearly demarcated):
╭───────╮
│ Start │
╰───────╯
|
expedited
|
v
╭───────────╮
│ Fast Path │
╰───────────╯
|
finish
|
v
╭──────────╮
│ Complete │
╰──────────╯
////
╭───────╮ ↑
│ Start │──╯
╰───────╯
|
normal
|
v
╭───────────────╮
│ Standard Path │
╰───────────────╯
|
finish
|
v
╭──────────╮
│ Complete │
╰──────────╯
Quantify the complexity:
aPaths = oProcess.FindAllPaths(:@start, :@end) ? len(aPaths) #--> 2
illustration
Parallel Execution: Unlocking Concurrency
Identify branches that execute independently to optimize workflows:
? oProcess.ParallelizableBranches() #--> [[:@fast, :@standard]] ? oProcess.DependencyFreeNodes() #--> [:@start]
illustration
This intelligence empowers architects to unlock parallelism—from concurrent task scheduling to modular microservice design.
Cyclic Dependencies: Visualizing Feedback Loops
Cycles aren't errors; they're feedback systems. stzGraph reveals their structure with clarity:
oCyclic = new stzGraph("CyclicStructure")
oCyclic {
AddNode(:@p1, "P1")
AddNode(:@p2, "P2")
AddNode(:@p3, "P3")
AddEdge(:@p1, :@p2, "")
AddEdge(:@p2, :@p3, "")
AddEdge(:@p3, :@p1, "")
Show()
}illustration
Output (cycle highlighted with precision):
╭────╮
│ P1 │
╰────╯
|
v
╭────╮
│ P2 │
╰────╯
|
v
╭────╮
│ P3 │
╰────╯
|
<CYCLE: >
| ↑
╰──> [P1] ──╯
Programmatic detection:
? oCyclic.CyclicDependencies() #--> TRUE
illustration
Structural Constraints: Enforcing Integrity
Embed design rules directly into your model to prevent structural flaws:
oGraph = new stzGraph("DAGStructure")
oGraph {
AddNode(:@a, "A")
AddNode(:@b, "B")
AddNode(:@c, "C")
AddConstraint("ACYCLIC")
AddConstraint("CONNECTED")
AddCustomConstraint("NO_ORPHANS", func {
return len(This.DependencyFreeNodes()) = 1
})
AddEdge(:@a, :@b, "")
AddEdge(:@b, :@c, "")
}
? oGraph.ValidateConstraints() #--> TRUEillustration
Built-in constraints like ACYCLIC and CONNECTED, combined with custom rules (e.g., NO_ORPHANS), enforce your design intentions early, catching violations before they propagate.
Reachability: Measuring Influence
Identify nodes that disproportionately influence system behavior:
oHierarchy = new stzGraph("TypeSystem")
oHierarchy {
AddNode(:@entity, "Entity")
AddNode(:@person, "Person")
AddNode(:@employee, "Employee")
AddNode(:@manager, "Manager")
AddEdge(:@entity, :@person, "is_a")
AddEdge(:@person, :@employee, "is_a")
AddEdge(:@employee, :@manager, "is_a")
Show()
}illustration
Output:
╭────────╮
│ Entity │
╰────────╯
|
is_a
|
v
╭──────────╮
│ !Person! │
╰──────────╯
|
is_a
|
v
╭────────────╮
│ !Employee! │
╰────────────╯
|
is_a
|
v
╭─────────╮
│ Manager │
╰─────────╯
Extract insights:
? oHierarchy.ReachableFrom(:@person) #--> [:@person, :@employee, :@manager] ? oHierarchy.Explain()
illustration
Output:
Graph: TypeSystem Nodes: 4 | Edges: 3 Bottleneck nodes: @person, @employee No cycles - DAG Density: 25% | Longest path: 3 hops
Bottlenecks: Locating Critical Convergence
Spot nodes where multiple paths converge—often the weakest link in system resilience:
oGraph = new stzGraph("BottleneckTest")
oGraph {
AddNode(:@a, "A")
AddNode(:@b, "B")
AddNode(:@c, "C")
AddNode(:@hub, "Hub")
AddEdge(:@a, :@hub, "")
AddEdge(:@b, :@hub, "")
AddEdge(:@c, :@hub, "")
ShowWithLegend()
}illustration
The !Hub! marker signals a critical convergence point. Query it:
? oGraph.BottleneckNodes() #--> [:@hub]
illustration
Impact Analysis: Quantifying Risk Propagation
Model how failures cascade through dependencies—essential for resilience planning:
oSystem = new stzGraph("SystemDependencies")
oSystem {
AddNode(:@database, "Database")
AddNode(:@api, "API")
AddNode(:@worker1, "Worker1")
AddNode(:@worker2, "Worker2")
AddEdge(:@database, :@api, "")
AddEdge(:@api, :@worker1, "")
AddEdge(:@api, :@worker2, "")
}
? oSystem.ImpactOf(:@api) #--> 2
? oSystem.FailureScope(:@api) #--> [:@worker1, :@worker2]
? oSystem.MostCriticalNodes(2) #--> [:@api, :@database]illustration
Complexity Metrics: Assessing Architectural Health
Quantify coupling and latency risk through system-wide metrics:
? oGraph.NodeDensity() #--> % (0–100, tight vs. modular) ? oGraph.LongestPath() #--> Edge count (latency indicator)
illustration
High density reveals tight coupling; lengthy paths suggest latency bottlenecks. Use these signals to guide refactoring decisions.
Direct Dependencies: Granular Visibility
While global queries reveal system-wide patterns, local inspection provides essential precision. stzGraph exposes immediate neighbors—both outgoing (what this node influences) and incoming (what depends on it)—without traversing the entire graph. This granular view is invaluable for microservice isolation, module boundary verification, or root-cause analysis during incidents.
oGraph = new stzGraph("Services")
oGraph {
AddNode(:@api, "API")
AddNode(:@auth, "Auth")
AddNode(:@db, "DB")
AddEdge(:@api, :@auth, "")
AddEdge(:@api, :@db, "")
}
? oGraph.NeighborsOf(:@api) #--> [:@auth, :@db]
? oGraph.IncomingTo(:@auth) #--> [:@api]illustration
These queries answer: "What does API directly touch?" and "What depends directly on Auth?"—questions that scale better than full reachability analysis for operational troubleshooting or design review.
Rich Querying: Domain-Aware Discovery
Annotate nodes with metadata, then query contextually for domain-specific insights:
oGraph = new stzGraph("Codebase")
oGraph {
AddNodeXT(:@fn1, "function1", [:type = "function"])
AddNodeXT(:@fn2, "function2", [:type = "function"])
AddNodeXT(:@mod1, "module1", [:type = "module"])
AddEdge(:@fn1, :@fn2, "calls")
AddEdge(:@fn2, :@mod1, "imports")
}
? oGraph.Query([:nodeType = "function"]) #--> [:@fn1, :@fn2]
? oGraph.FindNodesWhere(func node {
return substr(node["label"], "function") > 0
}) #--> [:@fn1, :@fn2]illustration
Metadata transforms your graph into a semantic model, enabling precise queries tailored to your domain.
Inference: Extensible Reasoning Engines
stzGraph moves beyond static relationship recording to become a dynamic reasoning engine. Built-in inference rules (TRANSITIVITY, SYMMETRY, COMPOSITION) handle common patterns, but the true power emerges through custom inference logic—where you encode domain-specific reasoning as executable rules.
Consider organizational hierarchy: explicit edges capture direct reporting relationships, but organizational reality demands understanding chains of command. Rather than manually computing transitive closure, you register an inference rule once, and the graph continuously applies it to new data. This transforms static models into self-evolving systems.
Anonymous functions (deferred code blocks) enable this paradigm—you pass logic to the graph system, which executes it when needed, not when defined. The rule operates on current graph state and injects derived knowledge back, creating a feedback loop of insight.
oGraph = new stzGraph("Organization")
oGraph {
AddNode(:@alice, "Alice")
AddNode(:@bob, "Bob")
AddNode(:@carol, "Carol")
AddNode(:@david, "David")
AddEdge(:@alice, :@bob, "manages")
AddEdge(:@bob, :@carol, "manages")
AddEdge(:@carol, :@david, "manages")
RegisterInferenceRule("CHAIN_OF_COMMAND", func oGraph {
nInferred = 0
acEdges = oGraph.AllEdges()
acNewEdges = []
# Nested loop: for each pair of edges, check if one's "to" matches the other's "from"
nLen = len(acEdges)
for i = 1 to nLen
aEdge1 = acEdges[i]
cMidpoint = aEdge1["to"] # End point of first edge
for j = 1 to nLen
aEdge2 = acEdges[j]
# Found a transitive pair: A -> B -> C means A can reach C
if aEdge2["from"] = cMidpoint
cFrom = aEdge1["from"]
cTo = aEdge2["to"]
# Avoid duplicates: only add if edge doesn't already exist
if NOT oGraph.EdgeExists(cFrom, cTo)
if find(acNewEdges, [cFrom, cTo]) = 0
acNewEdges + [cFrom, cTo]
nInferred += 1
ok
ok
ok
end
end
# Add all inferred edges to the graph
nNewLen = len(acNewEdges)
for i = 1 to nNewLen
aNewEdge = acNewEdges[i]
oGraph.AddEdge(aNewEdge[1], aNewEdge[2], "(chain-inferred)")
end
return nInferred
})
? ApplyCustomInference("CHAIN_OF_COMMAND") #--> 3 new relationships inferred
? EdgeExists(:@alice, :@carol) #--> TRUE (now discoverable)
? CustomInferenceRules() #--> ["CHAIN_OF_COMMAND"]
}illustration
The rule automatically surfaces hidden authority—Alice now provably manages Carol and David, even though no explicit edge existed. Three inferred edges emerge from two inputs: the graph becomes a reasoning substrate, not a passive map. Extend with domain logic; let the system derive consequences. This pattern scales to regulatory compliance (transitive obligation chains), supply chains (implicit dependencies), and codebase analysis (inferred coupling through composition).
Temporal Evolution: Versioning Change
Capture graph states at key moments for auditing, comparison, or rollback:
oGraph = new stzGraph("DatabaseSchema")
oGraph {
AddNode(:@users, "users")
AddNode(:@orders, "orders")
AddEdge(:@users, :@orders, "has_many")
Snapshot("v1.0")
AddNode(:@payments, "payments")
AddEdge(:@orders, :@payments, "has_many")
? Snapshots() #--> ["v1.0"]
}illustration
Snapshots enable safe exploration—revisit or compare states as your system evolves.
Export and Interoperability: Building Resilient Systems Through Extensibility
Static formats suffocate complex systems. stzGraph solves this through pluggable exporters—a mechanism to serialize your graph into any format, on-demand, for any ecosystem. Standard formats (DOT, JSON, YAML) handle common needs, but the extensibility model empowers you to encode domain-specific knowledge into serialization itself.
Imagine integrating with visualization tools, compliance audits, or CI/CD pipelines—each demands different representations of the same underlying graph. Rather than maintaining parallel models or writing ad-hoc conversion scripts, you register exporters once, then invoke them dynamically. The graph becomes a unified source of truth that speaks every language.
Anonymous functions make this elegant: pass transformation logic to the graph system, which applies it to current state and returns the result. New tools, new formats, new requirements—extend without touching core logic.
oGraph.RegisterExporter("MERMAID", func {
acNodes = oGraph.AllNodes()
acEdges = oGraph.AllEdges()
cMermaid = "graph LR;" + nl
# Build node definitions
for i = 1 to len(acNodes)
aNode = acNodes[i]
cMermaid += " " + aNode["id"] + "[" + aNode["label"] + "]" + nl
end
# Build edge connections
for i = 1 to len(acEdges)
aEdge = acEdges[i]
cMermaid += " " + aEdge["from"] + " --> " + aEdge["to"] + nl
end
return cMermaid
})
? oGraph.ExportUsing("MERMAID")
#-->
# graph LR;
# input[Input]
# process[Process]
# output[Output]
# input --> process
# process --> outputillustration
Register an exporter in the morning, paste output into Mermaid Live Editor(s'ouvre dans un nouvel onglet) by afternoon, deploy interactive documentation by evening—no backend changes, no format wrestling. A single stzGraph instance now powers visualization, documentation, auditing, and system validation across teams. This is extensibility as resilience: when requirements shift, your foundation adapts, not fractures.
Visualization: ASCII as Strategic Clarity
You've encountered rich graphs throughout this document. Here's why we chose ASCII over sophisticated rendering: pragmatism over eye candy.
Sophisticated diagrams seduce—but they seduce after the work is done. In the moment of design and discovery, you need thinking speed, not presentation polish. ASCII visualization enforces abstraction: no colors to distract, no fonts to fuss over, no layout engines to configure. You see structure naked—nodes, edges, patterns—without the cognitive load of graphic rendering.
More importantly, ASCII visualization is instant and embedded. Your terminal. Your REPL. Your notebook. No external tools, no context-switching, no waiting for GraphQL rendering (stzDiagram provides that power for publication; we chose ASCII for thinking). This proximity collapses the design cycle: modify your graph, hit Enter, see consequence immediately. Rapid prototyping becomes a natural rhythm.
Layout Modes serve specific analytical lenses:
-
Vertical (
Show()/ShowV()) — Hierarchical flows cascade top-to-bottom, mirroring causal reasoning. Workflows, dependency chains, inheritance hierarchies—sequence-driven thinking. -
Horizontal (
ShowH()/ShowHorizontal()) — Parallel branches spread left-to-right. Compare alternatives, reveal concurrent paths, spot symmetric structures at a glance. -
Annotated (
ShowWithLegend()) — Semantic markers overlay structural analysis:!bottleneck!flags convergence zones,~cycle~highlights feedback,////partitions branches. What queries would surface, visualization makes immediate.
Markers encode system properties:
- !label! — bottleneck (multiple paths converge)
- ~label~ — cycle participant
- !~label~! — cyclic bottleneck (compound risk)
- <CYCLE: label> — feedback loop marker
- [Node] — cycle member
- //// — branch boundary
ASCII transforms stzGraph from a data structure into a thinking partner—tactile, responsive, uncluttered. When clarity matters more than pixels, constraints become features.
Foundation for Domain-Specific Reasoning
stzGraph serves as the architectural foundation for specialized abstractions built atop graph semantics:
- stzWorkflow — task dependencies and orchestration
- stzDecisionTree — branching logic and outcomes
- stzSemanticModel — concept relationships and meaning
- stzDataModel — schema cardinality and constraints
- stzCodeModel — module and function dependencies
- stzNaturalLanguage — semantic role networks
Each inherits stzGraph's analytical power while introducing domain-specific vocabulary. Graph structure is universal; semantics are domain-specific.
Softanza Advantage
stzGraph elevates relationships from implementation details to first-class language constructs—concise, visual, and analytically rich. It's a developer-first tool for systems reasoning without the overhead of heavy frameworks or proprietary lock-in.
| Dimension | stzGraph | NetworkX | Neo4j | GraphQL | Wolfram Language Graphs |
|---|---|---|---|---|---|
| Structure Validation | ✅ .Explain() facts: cycles, bottlenecks |
◯ Manual analysis | ◯ Query-based | ◯ Schema only | ✅ Built-in (e.g., IsAcyclicGraphQ) |
| Custom Constraints | ✅ Built-in + user-defined rules | ✗ None | ◯ Query validation | 🟠 Schema-based | ◯ Compute conditions |
| Inference Rules | ✅ Extensible (transitivity, composition, custom) | ◯ Manual computation | 🟠 Query-based only | ✗ None | ✅ Symbolic derivation |
| Development Speed | ✅ Instant—no dependencies | ◯ pip install + setup |
🟠 Server required | 🟠 Endpoint + schema | 🟠 Proprietary install + learning curve |
| Language Integration | ✅ Native Haro fluency | ◯ Python idiomatic | 🟠 Java/Cypher | 🟠 JSON/REST layer | ✅ Wolfram-native, symbolic |
| Visualization | ✅ ASCII instant (stzDiagram: Graphviz layouts) | 🟠 Matplotlib | ◯ Web UI | ✗ Results only | ✅ Rich (e.g., GraphPlot, interactive) |
| Introspection | ✅ Programmatic data arrays | ◯ Manual traversal | 🟠 Query results | ◯ Schema tools | ✅ Deep symbolic queries |
| Temporal Tracking | ✅ Snapshots + change history | ◯ Attribute-based | 🟠 Transaction logs | ✗ None | ◯ Versioning via notebooks |
| Custom Exporters | ✅ Pluggable format serialization | ◯ Format-specific | 🟠 Cypher only | ✗ Schema-bound | ◯ Export to images/formats |
| Reusability | ✅ One codebase across domains | ◯ Research focus | ◯ Graph DB specific | 🟠 API-specific | ◯ Computation-heavy apps |
| Best For | ✅ Validate and extend systems | ✅ Algorithm research | ✅ Persistent queries | ◯ Data APIs | ✅ Scientific/computational modeling |
While Wolfram Language excels at symbolic computation and scientific visualization, stzGraph wins through instant accessibility, domain agnosticism, and a deep commitment to systems validation—ideal for developers facing real-world complexity.
Conclusion
Every developer encounters that moment when systems feel alive—dependencies interweave, logic branches unexpectedly, and outcomes hinge on invisible connections. Graphs are the cartography for that terrain.
stzGraph renders invisible relationships into visible order, empowering you to think in flows, reason through dependencies, and master cycles. Its arsenal—from custom inference and constraint validation to snapshots and extensible exporters—transforms it from a visualization tool into a reasoning engine.
From the first node through the final export, stzGraph invites you to think like a graph, unlocking clarity and command in any domain.
Lectures liées
- Intelligence in Motion: Graph Planning in Softanza
- stzGraph — Modeling Knowledge Through Structure
- Introducing stzOrgChart - The Strategic Organizational Intelligence Platform
- stzGraph: Computational Foundation and Design
Citer
Mansour Ayouni. « stzGraph: The Art of Connected Thinking ». Softanza narrations, 2025. https://mayouni.github.io/stzsite/fr/narrations/stzgraphdoc.html
Non exécuté : son code touche aux fichiers, au réseau, à l'horloge ou au hasard. L'article est publié tel qu'il est écrit ; ses blocs sont des illustrations. Le code des articles est du code Haro. Là où un article a été écrit avant que la langue prenne son nom actuel, le nom a été changé et rien d'autre. Genre, domaine et série lus dans le nom du fichier, année tirée du premier commit : dérivés, jusqu'à ce que l'article porte son propre en-tête. Le fichier sur GitHub(s'ouvre dans un nouvel onglet)

