Skip to content

spatial_graph_algorithms.verify

One-call pipeline runner that bundles simulation, reconstruction, and all output artefacts into a single directory.

Output artefacts

File Contents
report.csv Graph properties, shortest-path stats, false-edge stats
degree_distribution.csv Per-degree node counts and proportions
run_parameters.csv The simulation_kwargs dict as a single CSV row
reconstruction_quality.csv CPD, KNN, distortion per method
{prefix}_network.png 2-D graph layout with false-edge colouring
{prefix}_edge_length_histogram.png Edge-length distribution
comparison_{method}.png Original vs reconstructed side-by-side

API Reference

spatial_graph_algorithms.verify.VerifyConfig dataclass

Configuration for :func:~spatial_graph_algorithms.verify.run_report.

Parameters:

Name Type Description Default
output_root Path

Root directory under which a subdirectory named run_id is created for all output artefacts. Default is ".planning/artifacts/verification_runs".

Path('.planning/artifacts/verification_runs')
run_id str

Identifier for this run. If None, a UTC timestamp is used (YYYYMMDD_HHMMSS).

None
prefix str

Filename prefix for generated plots. Default is "verify".

'verify'
reconstruct_methods list of str

Reconstruction methods to run. Each element must be a valid method name accepted by :func:~spatial_graph_algorithms.reconstruct.reconstruct (e.g. ["mds", "strnd"]). An empty list skips reconstruction.

list()

Examples:

>>> from spatial_graph_algorithms.verify import VerifyConfig
>>> cfg = VerifyConfig(
...     output_root="results/",
...     run_id="experiment_01",
...     reconstruct_methods=["mds", "strnd"],
... )
Source code in src/spatial_graph_algorithms/verify/config.py
@dataclass
class VerifyConfig:
    """Configuration for :func:`~spatial_graph_algorithms.verify.run_report`.

    Parameters
    ----------
    output_root : Path
        Root directory under which a subdirectory named *run_id* is created
        for all output artefacts.  Default is
        ``".planning/artifacts/verification_runs"``.
    run_id : str, optional
        Identifier for this run.  If ``None``, a UTC timestamp is used
        (``YYYYMMDD_HHMMSS``).
    prefix : str
        Filename prefix for generated plots.  Default is ``"verify"``.
    reconstruct_methods : list of str
        Reconstruction methods to run.  Each element must be a valid method
        name accepted by :func:`~spatial_graph_algorithms.reconstruct.reconstruct`
        (e.g. ``["mds", "strnd"]``).  An empty list skips reconstruction.

    Examples
    --------
    >>> from spatial_graph_algorithms.verify import VerifyConfig
    >>> cfg = VerifyConfig(
    ...     output_root="results/",
    ...     run_id="experiment_01",
    ...     reconstruct_methods=["mds", "strnd"],
    ... )
    """

    output_root: Path = Path(".planning/artifacts/verification_runs")
    run_id: str | None = None
    prefix: str = "verify"
    reconstruct_methods: list[str] = field(default_factory=list)

spatial_graph_algorithms.verify.run_report

End-to-end pipeline: simulate → visualise → reconstruct → evaluate → report.

Classes

Functions

run_report(*, simulation_kwargs, config=None)

Run a full simulation + analysis pipeline and write all artefacts to disk.

Generates a synthetic graph, computes graph properties, optionally runs reconstruction and quality evaluation, and saves everything to a timestamped run directory.

Parameters:

Name Type Description Default
simulation_kwargs dict

Keyword arguments forwarded to :func:~spatial_graph_algorithms.simulate.generate (e.g. n, mode, false_edges_fraction, seed).

required
config VerifyConfig

Pipeline configuration. If None, defaults are used.

None

Returns:

Type Description
dict

A dictionary with keys:

  • "run_id" — string identifier for this run.
  • "run_dir" — :class:pathlib.Path to the output directory.
  • "report_file" — :class:pathlib.Path to report.csv.
Notes

The following files are always written to run_dir:

  • report.csv — graph properties + shortest-path stats + false-edge stats.
  • degree_distribution.csv — per-degree counts and proportions.
  • run_parameters.csv — the simulation_kwargs dict as a single row.
  • {prefix}_network.png — 2-D graph layout plot.
  • {prefix}_edge_length_histogram.png — edge-length distribution.

When :attr:~VerifyConfig.reconstruct_methods is non-empty, these are also written:

  • reconstruction_quality.csv — CPD, KNN, distortion per method.
  • comparison_{method}.png — original vs reconstructed side-by-side.

Examples:

>>> from spatial_graph_algorithms.verify import VerifyConfig, run_report
>>> out = run_report(
...     simulation_kwargs=dict(n=300, mode="delaunay_corrected", seed=42),
...     config=VerifyConfig(
...         output_root="results/",
...         run_id="demo",
...         reconstruct_methods=["mds", "strnd"],
...     ),
... )
>>> out["run_dir"].exists()
True
Source code in src/spatial_graph_algorithms/verify/run_report.py
def run_report(*, simulation_kwargs: dict, config: VerifyConfig | None = None) -> dict:
    """Run a full simulation + analysis pipeline and write all artefacts to disk.

    Generates a synthetic graph, computes graph properties, optionally runs
    reconstruction and quality evaluation, and saves everything to a
    timestamped run directory.

    Parameters
    ----------
    simulation_kwargs : dict
        Keyword arguments forwarded to :func:`~spatial_graph_algorithms.simulate.generate`
        (e.g. ``n``, ``mode``, ``false_edges_fraction``, ``seed``).
    config : VerifyConfig, optional
        Pipeline configuration.  If ``None``, defaults are used.

    Returns
    -------
    dict
        A dictionary with keys:

        - ``"run_id"`` — string identifier for this run.
        - ``"run_dir"`` — :class:`pathlib.Path` to the output directory.
        - ``"report_file"`` — :class:`pathlib.Path` to ``report.csv``.

    Notes
    -----
    The following files are always written to ``run_dir``:

    - ``report.csv`` — graph properties + shortest-path stats + false-edge stats.
    - ``degree_distribution.csv`` — per-degree counts and proportions.
    - ``run_parameters.csv`` — the ``simulation_kwargs`` dict as a single row.
    - ``{prefix}_network.png`` — 2-D graph layout plot.
    - ``{prefix}_edge_length_histogram.png`` — edge-length distribution.

    When :attr:`~VerifyConfig.reconstruct_methods` is non-empty, these are also written:

    - ``reconstruction_quality.csv`` — CPD, KNN, distortion per method.
    - ``comparison_{method}.png`` — original vs reconstructed side-by-side.

    Examples
    --------
    >>> from spatial_graph_algorithms.verify import VerifyConfig, run_report
    >>> out = run_report(
    ...     simulation_kwargs=dict(n=300, mode="delaunay_corrected", seed=42),
    ...     config=VerifyConfig(
    ...         output_root="results/",
    ...         run_id="demo",
    ...         reconstruct_methods=["mds", "strnd"],
    ...     ),
    ... )
    >>> out["run_dir"].exists()
    True
    """
    cfg = config or VerifyConfig()
    rid = _run_id(cfg)
    run_dir = Path(cfg.output_root) / rid
    run_dir.mkdir(parents=True, exist_ok=True)

    sn = generate(**simulation_kwargs)

    viz_paths = render_simulation_visualization_bundle(sn, output_dir=run_dir, prefix=cfg.prefix)

    deg = degree_distribution(sn)
    deg_df = pd.DataFrame({
        'degree': list(deg['counts'].keys()),
        'count': list(deg['counts'].values()),
        'proportion': [deg['proportions'][k] for k in deg['counts'].keys()],
    }).sort_values('degree')
    deg_df.to_csv(run_dir / 'degree_distribution.csv', index=False)

    gsum = graph_summary(sn)
    sp = _shortest_path_stats(sn.adjacency_matrix)
    fst = _false_edge_stats(sn)

    params_path = run_dir / 'run_parameters.csv'
    pd.DataFrame([simulation_kwargs]).to_csv(params_path, index=False)

    report_row = {
        'run_id': rid,
        'params_file': str(params_path),
        **gsum,
        **sp,
        **fst,
        'network_plot': str(viz_paths.get('network', viz_paths.get('network_3d', ''))),
        'edge_length_histogram_plot': str(viz_paths['edge_length_histogram']),
    }

    recon_rows = []
    for method in cfg.reconstruct_methods:
        snr = reconstruct(
            sn, method=method,
            dim=simulation_kwargs.get('dim', 2),
            seed=simulation_kwargs.get('seed'),
        )
        if sn.positions is None or snr.reconstructed_positions is None:
            continue

        quality = evaluate(snr, compute_distortion=True)
        recon_rows.append({
            'method': method,
            'cpd': quality['cpd'],
            'knn': quality['knn'],
            'distortion': quality['distortion'],
        })

        if sn.positions.shape[1] == 2:
            comp_filename = f"comparison_{method}.png"
            try:
                fig = plot_comparison(snr, save=True, output_dir=run_dir, filename=comp_filename)
                import matplotlib.pyplot as plt
                plt.close(fig)
            except Exception:
                pass

    if recon_rows:
        recon_path = run_dir / 'reconstruction_quality.csv'
        pd.DataFrame(recon_rows).to_csv(recon_path, index=False)
        report_row['reconstruction_quality_file'] = str(recon_path)

    report_path = run_dir / 'report.csv'
    pd.DataFrame([report_row]).to_csv(report_path, index=False)

    return {'run_id': rid, 'run_dir': run_dir, 'report_file': report_path}