Tutorial 11 — Validating a .qnet File
Objective: Intentionally introduce errors in a QNetFile to see how validate() catches them before simulation, preventing wasted compute time.
The validate() function is designed to never raise exceptions — it returns a structured dict describing all errors and warnings found in the file. This makes it safe to use on untrusted input.
Validate a Well-Formed File
from qnet_core import validate, QNetFile, save_qnet_file_wrapper
# Create a valid file first
qf_valid = QNetFile(name="valid_network")
qf_valid.add_node("A", memory_lifetime_ms=1000.0)
qf_valid.add_node("B", memory_lifetime_ms=1000.0)
qf_valid.add_link("link_1", src="A", to="B", distance_km=10.0,
base_fidelity=0.95, generation_rate_hz=1000.0)
valid_path = "examples/valid_network.qnet"
save_qnet_file_wrapper(qf_valid, valid_path)
print("=== Validating a well-formed file ===")
result = validate(valid_path)
print(f"Valid: {result['valid']}")
if result.get("warnings"):
print(f"Warnings ({len(result['warnings'])}):")
for w in result["warnings"]:
print(f" - [{w['type']}] {w['message']}")
Validate with a Self-Loop Error
# Self-loop: both src and to reference the same node
self_loop = QNetFile(name="self_loop_test")
self_loop.add_node("NodeX", memory_lifetime_ms=1000.0)
self_loop.add_link("bad_link", src="NodeX", to="NodeX", distance_km=10.0,
base_fidelity=0.95, generation_rate_hz=1000.0)
save_qnet_file_wrapper(self_loop, "examples/self_loop.qnet")
print("\n=== Validating with self-loop error ===")
result = validate("examples/self_loop.qnet")
print(f"Valid: {result['valid']}") # → False
for err in result.get("errors", []):
print(f" Error [{err['type']}]: {err['message']}")
Validate the Built-in Invalid File
The repo includes invalid_network.qnet with known structural issues:
print("\n=== Validating the repo's invalid_network.qnet ===")
result = validate("invalid_network.qnet")
print(f"Valid: {result['valid']}")
for err in result.get("errors", []):
print(f" [{err['type']}] {err['message']}")
Error Types Detected by validate()
| Error Type | Description |
|---|---|
DuplicateNodeId | Two nodes share the same ID |
UnknownNodeReference | A link references a node that doesn't exist |
SelfLoop | A link has the same source and destination node |
InvalidDistance | Distance is non-positive or unreasonably large |
InvalidFidelity | Fidelity is outside [0, 1] range |
InvalidGenerationRate | Generation rate is non-positive |
GraphDisconnected | The network graph has unreachable components |
Validate Return Format
result = validate("network.qnet")
# Always check this first:
if not result["valid"]:
for err in result["errors"]:
print(f" ERROR [{err['type']}]: {err['message']}")
# Warnings don't block simulation but flag issues:
for warn in result.get("warnings", []):
print(f" WARN: {warn['message']}")
# The validated file can be loaded normally (errors are informational, not blocking):
if result["valid"]:
engine = from_qnet_file(result["filepath"])
Key Design Principle
validate() never raises — it always returns a dict. This makes it safe to use in CI/CD pipelines, before simulation jobs, and on user-provided input. Always call
validate()before attempting resource-intensive simulations on untrusted.qnetfiles.
What's next
- Diffing Topology Versions — see what changed between two versions
- Comparing Candidate Topologies — evaluate multiple designs numerically