Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
run: |
python -m pip install --upgrade pip
python -m pip install '.[dev]'
- name: Generate auto-generated API documentation
- name: Generate the API reference
run: python dev/generate_api_docs.py --write-file
- name: Deploy documentation
run: mkdocs gh-deploy --force --clean
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ repos:
hooks:
- id: pyright
args: [--project, pyproject.toml]
additional_dependencies: ['networkx', 'pyyaml', 'pandas', 'pandas-stubs', 'netgraph-core']
additional_dependencies: ['networkx', 'numpy', 'pyyaml', 'jsonschema', 'netgraph-core']

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
Expand All @@ -31,5 +31,5 @@ repos:
name: Validate YAML schemas
entry: make validate
language: system
files: ^scenarios/.*\.yaml$
files: ^(scenarios/.*|tests/integration/.*)\.ya?ml$|^ngraph/schemas/scenario\.json$
pass_filenames: false
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Fixed

- Bill-of-materials counts for `exclusive: true` link ends round up as documented (a fractional count was truncated, so an exclusive half optic counted as none)
- `ngraph inspect --detail` shows enum step parameters by name (`PROPORTIONAL`, not `1`)
- `ngraph inspect` reports errors on stderr, as `run` does

### Removed

- **BREAKING**: `placement_rounds` (`MaximumSupportedDemand`, `TrafficMatrixPlacement`) and `acceptance_rule` (`MaximumSupportedDemand`)
- **BREAKING**: integer, numeric-string and blank `flow_policy` values; use the preset name
- **BREAKING**: `ngraph.dsl.selectors` re-exports; import selector types and evaluation from `ngraph.model.selectors`
- **BREAKING**: `edge_select` on `shortest_path_cost` and `k_shortest_paths`, where it had no effect
- **BREAKING**: `include_flow_summary` on `FailureManager.run_max_flow_monte_carlo`; use `include_flow_details`
- **BREAKING**: `CapacityEnvelope` flow-summary aggregation (`from_values(flow_summaries=...)`, `flow_summary_stats`), which nothing produced
- `serialize_policy_preset`, `DemandSet.get_default_set` and `FailureManager.run_single_failure_scenario`
- `enable_debug_logging`/`disable_debug_logging`; use `set_global_log_level`
- `expand_demands(default_policy_preset=...)`; unset presets use `DEFAULT_PRESET`
- Re-exports `ngraph.analysis.LARGE_CAPACITY` and `ngraph.dsl.blueprints.parser.expand_name_patterns`; `check_no_extra_keys` moved to `ngraph.utils.yaml_utils`
- The always-true third element of `resolve_link_end_components`, and `ngraph inspect --output`
- Result fields without information: workflow `active_seed` (equal to `step_seed`), MSD context `acceptance_rule`, failure-trace `expansion.risk_groups`
- The implicit `type: node` attribute on YAML-defined nodes

### Changed

Inputs that were silently ignored or replaced now raise, `ValueError` unless noted.

- Unknown keys in any scenario builder, from demands and failure policies to selectors, components and workflow steps
- `mode` on a bound `AnalysisContext` call, or on `analyze()`/`from_network()` without `source` and `sink`
- `alpha` together with `alpha_from_step` on `TrafficMatrixPlacement`; `alpha` defaults to 1.0
- A `parallelism` that is not an integer or `"auto"` (floats were truncated)
- A hardware `count` that is not a finite positive number (it became 1)
- Duplicate top-level risk-group names (the last one silently won)
- An unbound `context` passed to `max_flow_analysis` or `sensitivity_analysis`
- A `run_demand_placement_monte_carlo` input that is not a list of demand configs or a `DemandSet` (`TypeError`)
- A non-`FlowPolicyPreset` `TrafficDemand.flow_policy`, and a `to_networkx` node map missing an index (`KeyError`)
- CLI: an unknown `--keys` step, `--profile-memory` without `--profile`, and non-JSON results (were stringified)

Other changes.

- Selector contexts follow the DSL sections: `link` and `rule` replace `adjacency` and `override`
- Monte Carlo keeps `None` results from custom analysis functions in `results`
- `PerformanceProfiler.save_detailed_profile` requires `step_name`
- Dependencies: `pandas` dropped, `numpy` declared; dev extras drop pytest-benchmark, pytest-mock, pdoc and pandas-stubs
- Bundled `TrafficMatrixPlacement` scenarios use `parallelism: auto`
- Internal: removed impossible-state fallbacks, required-import guards, test-only helpers and unused test scaffolding

## [0.23.1] - 2026-09-13

### Changed
Expand Down
35 changes: 14 additions & 21 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# NetGraph Development Makefile
# This Makefile provides convenient shortcuts for common development tasks
# NetGraph development tasks. Run `make help` for the list.

.PHONY: help venv clean-venv dev install check check-ci lint format test qt build clean check-dist publish-test publish info hooks check-python docs docs-serve docs-diagrams validate perf

# Default target - show help
.DEFAULT_GOAL := help

# --------------------------------------------------------------------------
# Python interpreter detection
# --------------------------------------------------------------------------
# VENV_BIN: path to local virtualenv bin directory
VENV_BIN := $(PWD)/venv/bin

# PY_BEST: scan for newest supported Python (used when creating new venvs)
# Supports 3.11-3.14 to match requires-python >=3.11
# PY_BEST: newest of python3.14..python3.11 on PATH, else python3 or python;
# used to create venvs (requires-python >=3.11)
PY_BEST := $(shell for v in 3.14 3.13 3.12 3.11; do command -v python$$v >/dev/null 2>&1 && { echo python$$v; exit 0; }; done; command -v python3 2>/dev/null || command -v python 2>/dev/null)

# PY_PATH: active python3/python on PATH (respects CI setup-python and activated venvs)
Expand All @@ -26,7 +23,7 @@ PY_PATH := $(shell command -v python3 2>/dev/null || command -v python 2>/dev/nu
# 4. Final fallback to 'python3' literal for clear error messages
PYTHON ?= $(if $(wildcard $(VENV_BIN)/python),$(VENV_BIN)/python,$(if $(PY_PATH),$(PY_PATH),$(if $(PY_BEST),$(PY_BEST),python3)))

# Derived tool commands (always use -m to ensure correct environment)
# Run tools with -m so they come from $(PYTHON)'s environment
PIP := $(PYTHON) -m pip
PYTEST := $(PYTHON) -m pytest
RUFF := $(PYTHON) -m ruff
Expand All @@ -46,9 +43,9 @@ help:
@echo " make check-ci - Run non-mutating checks and tests (CI entrypoint)"
@echo " make lint - Run only linting (non-mutating: ruff + pyright)"
@echo " make format - Auto-format code with ruff"
@echo " make test - Run tests with coverage (includes slow and benchmark)"
@echo " make qt - Run quick tests only (excludes slow and benchmark)"
@echo " make perf - Run performance analysis with comprehensive reports and plots"
@echo " make test - Run tests with coverage (includes slow tests)"
@echo " make qt - Run quick tests only (excludes slow tests)"
@echo " make perf - Run the performance benchmarks and write reports and plots"
@echo " make validate - Validate YAML schemas"
@echo ""
@echo "Documentation:"
Expand Down Expand Up @@ -83,7 +80,7 @@ dev:
echo "❌ Error: venv creation failed - $(VENV_BIN)/python not found"; \
exit 1; \
fi; \
$(VENV_BIN)/python -m pip install -U pip setuptools wheel; \
$(VENV_BIN)/python -m pip install -U pip setuptools; \
fi
@echo "📦 Installing dev dependencies..."
@$(VENV_BIN)/python -m pip install -e .'[dev]'
Expand All @@ -103,7 +100,7 @@ venv:
echo "❌ Error: venv creation failed - $(VENV_BIN)/python not found"; \
exit 1; \
fi
@$(VENV_BIN)/python -m pip install -U pip setuptools wheel
@$(VENV_BIN)/python -m pip install -U pip setuptools
@echo "✅ venv ready. Activate with: source venv/bin/activate"

clean-venv:
Expand Down Expand Up @@ -136,24 +133,20 @@ format:
@$(RUFF) format .

test:
@echo "🧪 Running tests with coverage (includes slow and benchmark)..."
@echo "🧪 Running tests with coverage (includes slow tests)..."
@$(PYTEST)

qt:
@echo "⚡ Running quick tests only (excludes slow and benchmark)..."
@$(PYTEST) --no-cov -m "not slow and not benchmark"
@echo "⚡ Running quick tests only (excludes slow tests)..."
@$(PYTEST) --no-cov -m "not slow"

perf:
@echo "📊 Running performance analysis with tables and graphs..."
@$(PYTHON) -m dev.perf.main run || (echo "❌ Performance analysis failed."; exit 1)

validate:
@echo "📋 Validating YAML schemas..."
@if $(PYTHON) -c "import jsonschema" >/dev/null 2>&1; then \
$(PYTHON) -c "import json, yaml, jsonschema, pathlib; from importlib import resources as res; f=res.files('ngraph.schemas').joinpath('scenario.json').open('r', encoding='utf-8'); schema=json.load(f); f.close(); scenario_files=sorted(set(pathlib.Path('scenarios').rglob('*.yaml')) | set(pathlib.Path('scenarios').rglob('*.yml'))); integration_files=sorted(set(pathlib.Path('tests/integration').glob('*.yaml')) | set(pathlib.Path('tests/integration').glob('*.yml'))); all_files=scenario_files+integration_files; [jsonschema.validate(yaml.safe_load(open(fp)), schema) for fp in all_files]; print(f'✅ Validated {len(all_files)} YAML files against schema ({len(scenario_files)} scenarios, {len(integration_files)} integration tests)')"; \
else \
echo "⚠️ jsonschema not installed. Skipping schema validation"; \
fi
@$(PYTHON) -c "import json, yaml, jsonschema, pathlib; from importlib import resources as res; f=res.files('ngraph.schemas').joinpath('scenario.json').open('r', encoding='utf-8'); schema=json.load(f); f.close(); scenario_files=sorted(set(pathlib.Path('scenarios').rglob('*.yaml')) | set(pathlib.Path('scenarios').rglob('*.yml'))); integration_files=sorted(set(pathlib.Path('tests/integration').glob('*.yaml')) | set(pathlib.Path('tests/integration').glob('*.yml'))); all_files=scenario_files+integration_files; [jsonschema.validate(yaml.safe_load(open(fp)), schema) for fp in all_files]; print(f'✅ Validated {len(all_files)} YAML files against schema ({len(scenario_files)} scenarios, {len(integration_files)} integration tests)')"

# Documentation
docs:
Expand Down Expand Up @@ -197,7 +190,7 @@ build:
clean:
@echo "🧹 Cleaning build artifacts and cache files..."
@rm -rf build/ dist/ *.egg-info/
@rm -rf .pytest_cache .ruff_cache .mypy_cache htmlcov .coverage coverage.xml coverage-*.xml .benchmarks .pytest-benchmark || true
@rm -rf .pytest_cache .ruff_cache htmlcov .coverage coverage.xml coverage-*.xml || true
@find . -path "./venv" -prune -o -type f -name "*.pyc" -delete 2>/dev/null || true
@find . -path "./venv" -prune -o -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
@find . -path "./venv" -prune -o -type f -name "*.pyo" -delete 2>/dev/null || true
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Network modeling and analysis framework: Python front end, C++ graph algorithms.

## What It Does

NetGraph lets you model network topologies, traffic demands, and failure scenarios - then analyze capacity and resilience. Define networks in Python or declarative YAML, run max-flow and failure simulations, and export reproducible JSON results. Compute-intensive algorithms run in C++ with the GIL released.
NetGraph models network topologies, traffic demands and failure scenarios, and analyzes capacity and resilience. Networks are defined in Python or in YAML; max-flow and failure simulations export reproducible JSON.

## Install

Expand All @@ -19,7 +19,7 @@ pip install ngraph
```python
from ngraph import Network, Node, Link, analyze, Mode

# Build a simple network
# Three nodes in a line
network = Network()
network.add_node(Node("A"))
network.add_node(Node("B"))
Expand Down Expand Up @@ -108,25 +108,25 @@ ngraph run scenario.yml --output results/
jq '.steps.max_demand.data.alpha_star' results/scenario.results.json
```

This scenario builds a dual-site Clos fabric from blueprints, finds the maximum supportable demand, then runs 100 Monte Carlo iterations with random link failures - exporting results to JSON.
The scenario builds two Clos sites from one blueprint, finds the largest demand multiplier the network carries, then places that demand under 100 random single-link failures and writes the results to JSON.

See [DSL Reference](https://networmix.github.io/NetGraph/reference/dsl/) and [Examples](https://networmix.github.io/NetGraph/examples/clos-fabric/) for more.

## Capabilities

- **Declarative scenarios** with schema validation, reusable blueprints, and strict multigraph representation
- **Failure analysis** via policy engine with weighted modes, risk groups, and non-destructive runtime exclusions
- **Routing modes** for IP routing (cost-based) and traffic engineering (capacity-aware)
- **Flow placement** strategies for ECMP and WCMP with max-flow and capacity envelopes
- **Reproducible results** via seeded randomness and stable edge IDs
- **Declarative scenarios**: schema-validated YAML, reusable blueprints, a strict multigraph model
- **Failure analysis**: weighted failure modes, risk groups, and analysis-time exclusions that leave the base topology untouched
- **Routing models**: cost-only IP routing and capacity-aware traffic engineering
- **Flow placement**: ECMP and WCMP splits, max-flow and demand placement
- **Reproducible results**: seeded randomness and stable link ids
- **C++ algorithms** with the GIL released, via [NetGraph-Core](https://github.com/networmix/NetGraph-Core)

## Documentation

- [**Tutorial**](https://networmix.github.io/NetGraph/getting-started/tutorial/) - Getting started guide
- [**Examples**](https://networmix.github.io/NetGraph/examples/clos-fabric/) - Clos fabric, failure analysis, and more
- [**Tutorial**](https://networmix.github.io/NetGraph/getting-started/tutorial/) - Running a scenario from the CLI and from Python
- [**Examples**](https://networmix.github.io/NetGraph/examples/clos-fabric/) - Clos fabric capacity and failure analysis
- [**DSL Reference**](https://networmix.github.io/NetGraph/reference/dsl/) - YAML scenario syntax
- [**API Reference**](https://networmix.github.io/NetGraph/reference/api/) - Python API docs
- [**API Reference**](https://networmix.github.io/NetGraph/reference/api/) - Python API

## License

Expand Down
5 changes: 0 additions & 5 deletions dev/check_api_docs.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
"""Verify the committed API reference without modifying it."""

import difflib
import re
import sys
from pathlib import Path

from generate_api_docs import generate_api_documentation

expected = Path("docs/reference/api-full.md").read_text(encoding="utf-8")
generated = generate_api_documentation(output_to_file=False)
# The generator includes wall-clock time; ignore only that metadata line.
timestamp = r"(?m)^Generated from source code on: .*$"
expected = re.sub(timestamp, "Generated from source code on: <timestamp>", expected)
generated = re.sub(timestamp, "Generated from source code on: <timestamp>", generated)
if generated != expected:
sys.stdout.writelines(
difflib.unified_diff(
Expand Down
19 changes: 12 additions & 7 deletions dev/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
## Essential Commands

```bash
make dev # Complete dev environment setup
make check # Run all quality checks + tests
make dev # Create venv, install package with dev deps and hooks
make check # Pre-commit with auto-fix, schema check, tests, then lint
make check-ci # Non-mutating lint, schema check, and tests (CI)
make test # Run tests with coverage
make docs # Generate API documentation
make docs # Generate API documentation and diagram SVGs
make docs-serve # Serve docs locally
```

`make docs` renders `docs/assets/diagrams/*.dot` with Graphviz; install it
with `brew install graphviz` (macOS) or `apt-get install graphviz` (Debian/Ubuntu).
Without it the diagram step is skipped and the committed SVGs stay as they are.

## Publishing

**Manual**: `make clean && make build && make publish-test && make publish`
Expand All @@ -24,14 +29,14 @@ make docs-serve # Serve docs locally
pyproject.toml # Package config, dependencies, tool settings
Makefile # Development commands
.pre-commit-config.yaml # Code quality hooks
dev/run-checks.sh # Manual code quality checks
dev/run-checks.sh # Script behind make check
```

## Git Workflows
## GitHub Workflows

```text
.github/workflows/
├── python-test.yml # CI: tests, linting, type checking
├── docs.yml # Auto-deploy documentation
└── publish.yml # Auto-publish to PyPI on releases
├── docs.yml # Build and deploy docs on push to main
└── publish.yml # Publish to PyPI on release; Test PyPI on manual run
```
Loading
Loading