Skip to content

Add Photonic Subcircuit Compiler#1059

Draft
tobi-forster wants to merge 25 commits into
mainfrom
tobi/add-photonics-subcircuit-compiler
Draft

Add Photonic Subcircuit Compiler#1059
tobi-forster wants to merge 25 commits into
mainfrom
tobi/add-photonics-subcircuit-compiler

Conversation

@tobi-forster

@tobi-forster tobi-forster commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR introduces mqt.qmap.ph, a new Python subpackage implementing a photonic subcircuit compiler for MZI-mesh chips. The compiler routes photons to the computation zone by finding the lowest-loss path through the chip's MZI mesh, then fits the phase-shifter parameters to a target unitary via PyTorch gradient descent. The compiled circuit is evaluated using Perceval simulation, reporting coincidence rate and TVD against the ideal output distribution. A fixed-placement baseline is computed alongside every run to verify that routing improves performance under transmission loss.

Required dependencies:

  • perceval-quandela ≥ 1.1.0
  • pandas ≥ 2.3.3
  • numpy ≥ 1.26.0
  • torch ≥ 2.9.0

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • I have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@tobi-forster

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added photonics compilation support, including routing, optimization, simulation, and result export workflows.
    • Added a new notebook for collecting and analyzing compilation results.
    • Added support for optional photonics dependencies.
  • Bug Fixes

    • Notebook files are now excluded from license-tool processing to avoid unnecessary scans.
  • Tests

    • Added coverage for the new photonics workflow and related helper utilities.

Walkthrough

This PR adds a new mqt.qmap.ph photonic subcircuit compiler package implementing MZI-mesh routing-graph construction, route/phase optimization, Perceval-based simulation, baseline comparison, and batch data-collection utilities, along with an evaluation notebook, extensive test coverage, and supporting build/config changes (pyproject.toml, noxfile.py, license-tools exclude patterns).

Changes

Photonic subcircuit compiler

Layer / File(s) Summary
Package init and config wiring
python/mqt/qmap/ph/__init__.py, pyproject.toml, noxfile.py, .license-tools-config.json
New ph package initializer with docstring/__all__; adds photonics optional-dependency group and ty override rules; nox test sync now includes --extra photonics; license tooling excludes .ipynb files.
Baseline reference strategy
python/mqt/qmap/ph/baseline.py, test/python/ph/test_baseline.py
Functions to embed a target unitary into the chip, compute baseline active columns, and build baseline input-port vectors, with unit tests.
Routing graph and fidelity model
python/mqt/qmap/ph/graph.py, test/python/ph/test_graph.py
Bar/cross fidelity formulas, beam-splitter matrix generation, per-layer fidelity computation, edge-cost helpers, and construct_graph building a weighted DAG, with tests.
Route selection and movement mask
python/mqt/qmap/ph/routing.py, test/python/ph/test_routing.py
MaskState enum, DAG shortest-path search, path reconstruction, port inference, and conversion of a route into a movement mask, with tests.
Routing-to-phase conversion
python/mqt/qmap/ph/routing_to_phases.py, test/python/ph/test_phases.py
Converts movement masks and raw parameters into constrained effective phase parameters/gradient masks, and reshapes flattened parameters into a grid, with tests.
Unitary-to-phase optimization
python/mqt/qmap/ph/unitary_to_phase_compilation.py
Haar-random unitary generation, component-level unitary builders, fidelity loss, computation-zone extraction, and the Adam-based optimize_unitary_subcircuit_parameters routine.
Perceval simulation
python/mqt/qmap/ph/perceval_simulation.py
Builds MZI mesh circuits, simulates with input/output loss, and evaluates coincidence rate/TVD performance.
End-to-end orchestration
python/mqt/qmap/ph/subcircuit_compilation.py, test/python/ph/test_subcircuit_compilation.py
compile_subcircuit combines routing, phase optimization, baseline comparison, and simulation into RunResult; adds a parametrized regression test suite.
Batch data collection and notebook
python/mqt/qmap/ph/data_collection.py, test/python/ph/test_data_collection.py, test/python/ph/conftest.py, eval/ph/subcircuit_compilation_data_collection.ipynb
Setup, build_setup_grid, collect_pipeline_results sweep, and export_results_table; shared test fixtures; new notebook running the collection workflow and exporting results to CSV.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant compile_subcircuit
  participant construct_graph
  participant get_best_route
  participant optimize_unitary_subcircuit_parameters
  participant perceval_simulation

  compile_subcircuit->>construct_graph: build routing DAG (chip_dim, target_dim, transmissions)
  compile_subcircuit->>get_best_route: find optimal route
  compile_subcircuit->>optimize_unitary_subcircuit_parameters: optimize proposed phase parameters
  compile_subcircuit->>optimize_unitary_subcircuit_parameters: optimize baseline phase parameters
  compile_subcircuit->>perceval_simulation: create_mzi_chip / simulate_with_loss
  perceval_simulation-->>compile_subcircuit: raw probability distributions
  compile_subcircuit->>perceval_simulation: evaluate_chip_performance
  perceval_simulation-->>compile_subcircuit: coincidence rate, TVD
  compile_subcircuit-->>compile_subcircuit: assemble RunResult
Loading

Poem

A rabbit hopped through mirrors bright,
Splitting beams of photon light,
Routed paths through mesh and maze,
Phases tuned in gradient haze,
With CSV rows to hop and stack —
🐇✨ thump-thump, compile_subcircuit's back!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding the photonic subcircuit compiler.
Description check ✅ Passed The description includes the required summary, context, dependencies, and checklist, so it mostly matches the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch tobi/add-photonics-subcircuit-compiler

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 21

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@eval/ph/subcircuit_compilation_data_collection.ipynb`:
- Around line 46-54: The OptimizationConfig constructor in the notebook still
passes unsupported kwargs, causing a TypeError at runtime. Update the config
setup to remove num_restarts and restart_perturbation, keeping the remaining
OptimizationConfig parameters unchanged. Use the existing OptimizationConfig
call in the subcircuit_compilation_data_collection notebook as the target for
this fix.

In `@python/mqt/qmap/ph/baseline.py`:
- Around line 21-38: The nested loops in embed_target_unitary_into_chip are
performing per-element tensor writes for the top-left block copy; replace them
with a single sliced block assignment on the embedded tensor using the
target_dim region. Keep the same behavior in embed_target_unitary_into_chip, but
use direct indexing into embedded with the target_unitary block so the function
is clearer and avoids Python-level iteration overhead.
- Around line 21-38: Add an input validation guard in
embed_target_unitary_into_chip before the nested loops: if target_dim is greater
than chip_dim, raise a ValueError instead of indexing into embedded. Keep the
fix localized to this helper so the torch.eye allocation and assignment loop
only run when the target block fits within the chip-sized tensor.

In `@python/mqt/qmap/ph/data_collection.py`:
- Line 77: The collect_pipeline_results function is too complex and should be
split up to satisfy Ruff PLR0912/PLR0915. Extract the hardware-cache
construction logic and the per-repeat run loop into separate helper functions,
then have collect_pipeline_results orchestrate those helpers while keeping its
responsibilities minimal. Use the existing collect_pipeline_results entry point
and any new private helpers to preserve behavior and make the control flow
easier to follow.
- Around line 216-224: The std value returned by `_mean_std` in
`data_collection.py` is never used because each call site only keeps the mean,
so the second return is dead weight. Update the aggregation logic around
`_mean_std` and the result assembly in `data_collection` to either include the
standard deviation in the collected outputs (and any downstream consumers) or
simplify `_mean_std` to return only the mean and adjust all call sites
accordingly.
- Around line 220-223: The aggregated loss values are computed in the data
collection flow but never make it into the final grouped result. Update the
aggregation in data_collection.py, especially in the logic around the rows
assembly and the groupby(...).agg(...) call in the aggregation step, so
`mean_loss` and `mean_baseline_loss` are actually included in the aggregated
output (or remove the unused computations if they are not meant to be returned).
Keep the `rows` construction and `df_aggregated` schema consistent so the loss
fields are not silently dropped.
- Around line 72-74: The helper _mean_std currently has no Google-style
docstring, so add one directly above the function definition describing its
purpose, the values argument, and the returned mean/std tuple in Google format.
Keep the implementation unchanged and ensure the docstring follows the existing
Python docstring guidelines used elsewhere in data_collection.py.
- Around line 84-87: Make the boolean flags in data_collection.py keyword-only
to avoid positional boolean arguments. Update the affected function signature
around the parameters input_losses, output_losses, and ideal_beam_splitters
(along with custom_bs_data if needed) by inserting a keyword-only separator so
callers must pass these flags by name. Check all call sites of the function to
ensure they use the explicit keywords.
- Around line 173-178: The seed in data_collection.py’s unitary generation logic
is derived from target_dim + unitary_index, which can repeat across different
sweep points and create correlated RNG streams. Update the seeding in the loop
that calls get_haar_random_unitary so each (target_dim, unitary_index) pair maps
to a unique deterministic seed, for example by combining both values with a
collision-resistant formula or hash. Keep the change localized around
unitary_seed and target_unitary so the sweep remains reproducible but
independent across target_dim values.
- Around line 247-266: Handle the empty rows case before calling groupby in
data_collection.py: when rows is empty, df will not contain the grouping columns
and df.groupby(groupby_cols, ...) will raise a KeyError. Update the logic around
the df and df_aggregated निर्माण so that an empty setups list or
num_unitaries_per_setup=0 returns an empty DataFrame with the expected output
schema instead of grouping. Use the existing groupby_cols and aggregation fields
in the same block to define the return shape consistently.

In `@python/mqt/qmap/ph/subcircuit_compilation.py`:
- Around line 86-307: compile_subcircuit is doing too many orchestration steps
in one place, which is why it exceeds the statement-count threshold. Refactor
the flow in subcircuit_compilation.py by extracting the routing/setup,
optimization, ideal-distribution computation, chip construction, and
simulation/evaluation logic into small helpers such as
_compute_ideal_distributions and _optimize_and_build_chip, while keeping
compile_subcircuit as the top-level coordinator. Preserve the existing behavior
and data flow through unique symbols like get_best_route,
optimize_unitary_subcircuit_parameters, create_mzi_chip, simulate_with_loss, and
evaluate_chip_performance, and keep RunResult assembly in the main function.
- Around line 238-267: The ideal Perceval simulation is being run twice with
identical inputs in the ground-truth section, duplicating an expensive
deterministic step. Reuse the single result from the
`ground_truth_processor`/`algorithm.Sampler(...).probs()` computation for both
`ideal_probability_distribution` and `baseline_ideal_probability_distribution`,
and remove the redundant `baseline_ground_truth_processor` setup while keeping
the existing `pcvl_u`, `ground_truth_processor`, and sampler flow intact.

In `@python/mqt/qmap/ph/unitary_to_phase_compilation.py`:
- Around line 519-609: The optimization loop in unitary_to_phase_compilation is
tracking best_loss but never stores the matching parameter state, so the return
value currently reflects the final iterate instead of the best one. Update the
loop around best_loss/no_improve_steps to snapshot phase_shifter_params whenever
a new best loss is found, and return that saved best tensor from the final
dictionary instead of the current detached phase_shifter_params. Use the
existing symbols best_loss, phase_shifter_params, and the function’s return
block to locate the change.
- Line 586: The progress-check in unitary-to-phase compilation is a dead no-op
because it only evaluates a boolean and discards it, so the verbose flag never
triggers logging. Update the progress reporting logic in the loop that uses
index and verbose so that it actually emits a message every 100 iterations when
verbose is enabled, using the surrounding compilation method/function as the
place to hook the logging behavior.

In `@test/python/ph/conftest.py`:
- Around line 21-33: The current `conftest.py` setup in the `mqt`/`mqt.qmap`
import shim mutates `sys.modules` globally, which can leak the stubs into later
tests in the same interpreter. Scope the stub registration in the loop that
creates the `ModuleType` entries to the photonics tests by using a fixture or
adding teardown logic in `conftest.py` to remove or restore the `mqt` and
`mqt.qmap` modules after the tests finish, while keeping `sys.path.insert`
limited to the test session setup.

In `@test/python/ph/test_baseline.py`:
- Around line 23-118: Add explicit -> None return annotations to every flagged
staticmethod test in TestGetBaselineActiveCols, TestEmbedTargetUnitaryIntoChip,
and TestGetBaselineInputPorts (and any other test methods in test_baseline.py
that currently omit them) to satisfy Ruff ANN205; update the function signatures
directly on each test_* method rather than suppressing the warning.

In `@test/python/ph/test_data_collection.py`:
- Around line 55-62: The comment in test_valid_multiple_setups is inaccurate
about why (6,6) is not produced by build_setup_grid; update the test note to
reflect that target_dims_list only includes 2 and 4, so 6 is never a candidate
target dimension. Keep the assertions as-is and revise the explanatory text in
test_valid_multiple_setups to match the actual inputs and expected Cartesian
product.

In `@test/python/ph/test_graph.py`:
- Around line 26-212: Add explicit `-> None` return annotations to every
`@staticmethod` test in `TestBarFidelity`, `TestCrossFidelity`,
`TestGenerateBeamSplitterMatrix`, `TestDetermineRoutingFidelities`, and
`TestConstructGraph` in `test_graph.py`. The issue is Ruff ANN205 missing return
type annotations on these test methods. Update each test method signature to
include `-> None` so the file stays lint-clean without suppressions.

In `@test/python/ph/test_phases.py`:
- Around line 24-128: Annotate the test methods in TestPhases and
TestGetEffectiveParamsAndMask with explicit -> None return types to satisfy
ANN205, and add explicit torch.Tensor return annotations to the helper methods
_bar_mask and _cross_mask so the type warnings are cleared without suppressing
them.

In `@test/python/ph/test_routing.py`:
- Around line 30-232: The test methods in TestRouting and the related static
test classes are missing explicit return type annotations, triggering ANN205
warnings. Update each `@staticmethod` test method in this file to declare -> None,
including methods like test_straight_route_first_position,
test_first_mode_active, test_straight_route_chip4_target2, and the other test_*
functions, without changing the test logic or assertions.

In `@test/python/ph/test_subcircuit_compilation.py`:
- Around line 167-262: Add explicit `-> None` return annotations to each
`@staticmethod` test in `TestRunResultProperties` and `TestRunValueRanges` to
satisfy Ruff ANN205. Update the signatures of the test methods like
`test_returns_run_result`, `test_performance_dict_has_required_keys`, and the
other static test helpers in this file so they clearly return nothing; keep the
test logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: eb9671aa-8b70-45d2-a4c2-f01ce933bcdc

📥 Commits

Reviewing files that changed from the base of the PR and between 4db332e and ef3a439.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • .license-tools-config.json
  • eval/ph/subcircuit_compilation_data_collection.ipynb
  • noxfile.py
  • pyproject.toml
  • python/mqt/qmap/ph/__init__.py
  • python/mqt/qmap/ph/baseline.py
  • python/mqt/qmap/ph/data_collection.py
  • python/mqt/qmap/ph/graph.py
  • python/mqt/qmap/ph/perceval_simulation.py
  • python/mqt/qmap/ph/routing.py
  • python/mqt/qmap/ph/routing_to_phases.py
  • python/mqt/qmap/ph/subcircuit_compilation.py
  • python/mqt/qmap/ph/unitary_to_phase_compilation.py
  • test/python/ph/conftest.py
  • test/python/ph/test_baseline.py
  • test/python/ph/test_data_collection.py
  • test/python/ph/test_graph.py
  • test/python/ph/test_phases.py
  • test/python/ph/test_routing.py
  • test/python/ph/test_subcircuit_compilation.py

Comment on lines +46 to +54
"config = OptimizationConfig(\n",
" lr=0.01,\n",
" threshold=1e-6,\n",
" num_restarts=1,\n",
" max_iterations=1000,\n",
" restart_perturbation=0.15,\n",
" exclude_edge_phase_shifters=False,\n",
" optimize_routing_parameters=True,\n",
")\n",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "num_restarts|restart_perturbation" --type=py --type=jupyter -g '*.ipynb'

Repository: munich-quantum-toolkit/qmap

Length of output: 165


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Files matching OptimizationConfig ==\n'
git ls-files | rg 'subcircuit_compilation|OptimizationConfig|eval/ph/.+ipynb|\.ipynb$' || true

printf '\n== Definition locations ==\n'
rg -n "class OptimizationConfig|OptimizationConfig\(" --hidden --glob '!*.ipynb' .

printf '\n== Notebook text around lines 46-54 ==\n'
python3 - <<'PY'
import json, pathlib
path = pathlib.Path('eval/ph/subcircuit_compilation_data_collection.ipynb')
nb = json.loads(path.read_text())
for cell in nb.get('cells', []):
    if cell.get('cell_type') == 'code':
        src = ''.join(cell.get('source', []))
        if 'OptimizationConfig' in src:
            print('--- code cell ---')
            print(src)
PY

Repository: munich-quantum-toolkit/qmap

Length of output: 1915


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Search for stale fields in repo files that are easy to inspect ==\n'
rg -n "num_restarts|restart_perturbation" --glob '!*.ipynb' .

printf '\n== Locate subcircuit_compilation.py ==\n'
fd -a "subcircuit_compilation.py" .

Repository: munich-quantum-toolkit/qmap

Length of output: 235


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== OptimizationConfig definition ==\n'
sed -n '1,120p' python/mqt/qmap/ph/subcircuit_compilation.py

printf '\n== Nearby uses and constructor defaults ==\n'
sed -n '120,180p' python/mqt/qmap/ph/subcircuit_compilation.py

printf '\n== Exact stale-field search across repo (including notebooks) ==\n'
rg -n "num_restarts|restart_perturbation" .

Repository: munich-quantum-toolkit/qmap

Length of output: 7558


Remove unsupported OptimizationConfig kwargs

OptimizationConfig no longer accepts num_restarts or restart_perturbation, so this notebook will raise TypeError when it runs. Drop those two arguments from the constructor call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@eval/ph/subcircuit_compilation_data_collection.ipynb` around lines 46 - 54,
The OptimizationConfig constructor in the notebook still passes unsupported
kwargs, causing a TypeError at runtime. Update the config setup to remove
num_restarts and restart_perturbation, keeping the remaining OptimizationConfig
parameters unchanged. Use the existing OptimizationConfig call in the
subcircuit_compilation_data_collection notebook as the target for this fix.

Comment on lines +21 to +38
def embed_target_unitary_into_chip(target_unitary: np.ndarray, chip_dim: int, target_dim: int) -> torch.Tensor:
"""Embed a target unitary into the top-left block of a chip-sized identity matrix.

Args:
target_unitary: Complex unitary of shape ``(target_dim, target_dim)``.
chip_dim: Total number of spatial modes on the chip.
target_dim: Dimension of the target unitary.

Returns:
A ``(chip_dim, chip_dim)`` complex tensor that equals the identity
everywhere except the top-left ``(target_dim, target_dim)`` block,
which is replaced by ``target_unitary``.
"""
embedded = torch.eye(chip_dim, dtype=torch.complex128)
for i in range(target_dim):
for j in range(target_dim):
embedded[i, j] = target_unitary[i, j]
return embedded

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Vectorize the block assignment instead of a Python double loop.

The nested loop performs target_dim**2 individual scalar tensor writes; a single sliced assignment is clearer and avoids per-element Python/Torch overhead.

♻️ Proposed vectorized fix
     embedded = torch.eye(chip_dim, dtype=torch.complex128)
-    for i in range(target_dim):
-        for j in range(target_dim):
-            embedded[i, j] = target_unitary[i, j]
+    embedded[:target_dim, :target_dim] = torch.as_tensor(target_unitary, dtype=torch.complex128)
     return embedded
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def embed_target_unitary_into_chip(target_unitary: np.ndarray, chip_dim: int, target_dim: int) -> torch.Tensor:
"""Embed a target unitary into the top-left block of a chip-sized identity matrix.
Args:
target_unitary: Complex unitary of shape ``(target_dim, target_dim)``.
chip_dim: Total number of spatial modes on the chip.
target_dim: Dimension of the target unitary.
Returns:
A ``(chip_dim, chip_dim)`` complex tensor that equals the identity
everywhere except the top-left ``(target_dim, target_dim)`` block,
which is replaced by ``target_unitary``.
"""
embedded = torch.eye(chip_dim, dtype=torch.complex128)
for i in range(target_dim):
for j in range(target_dim):
embedded[i, j] = target_unitary[i, j]
return embedded
def embed_target_unitary_into_chip(target_unitary: np.ndarray, chip_dim: int, target_dim: int) -> torch.Tensor:
"""Embed a target unitary into the top-left block of a chip-sized identity matrix.
Args:
target_unitary: Complex unitary of shape ``(target_dim, target_dim)``.
chip_dim: Total number of spatial modes on the chip.
target_dim: Dimension of the target unitary.
Returns:
A ``(chip_dim, chip_dim)`` complex tensor that equals the identity
everywhere except the top-left ``(target_dim, target_dim)`` block,
which is replaced by ``target_unitary``.
"""
embedded = torch.eye(chip_dim, dtype=torch.complex128)
embedded[:target_dim, :target_dim] = torch.as_tensor(target_unitary, dtype=torch.complex128)
return embedded
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/mqt/qmap/ph/baseline.py` around lines 21 - 38, The nested loops in
embed_target_unitary_into_chip are performing per-element tensor writes for the
top-left block copy; replace them with a single sliced block assignment on the
embedded tensor using the target_dim region. Keep the same behavior in
embed_target_unitary_into_chip, but use direct indexing into embedded with the
target_unitary block so the function is clearer and avoids Python-level
iteration overhead.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== baseline.py around target function ==\n'
nl -ba python/mqt/qmap/ph/baseline.py | sed -n '1,120p'

printf '\n== search for embed_target_unitary_into_chip usages ==\n'
rg -n "embed_target_unitary_into_chip|target_dim|chip_dim" python/mqt/qmap -g '!**/__pycache__/**'

printf '\n== search for validation in related code ==\n'
rg -n "ValueError|IndexError|cannot exceed|chip_dim" python/mqt/qmap -g '!**/__pycache__/**'

printf '\n== read-only runtime probe ==\n'
python3 - <<'PY'
import numpy as np
import torch

def embed_target_unitary_into_chip(target_unitary: np.ndarray, chip_dim: int, target_dim: int) -> torch.Tensor:
    embedded = torch.eye(chip_dim, dtype=torch.complex128)
    for i in range(target_dim):
        for j in range(target_dim):
            embedded[i, j] = target_unitary[i, j]
    return embedded

for chip_dim, target_dim in [(2, 3), (3, 2)]:
    try:
        u = np.eye(target_dim, dtype=np.complex128)
        out = embed_target_unitary_into_chip(u, chip_dim, target_dim)
        print((chip_dim, target_dim), "ok", out.shape)
    except Exception as e:
        print((chip_dim, target_dim), type(e).__name__, e)
PY

Repository: munich-quantum-toolkit/qmap

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
path = Path("python/mqt/qmap/ph/baseline.py")
for idx, line in enumerate(path.read_text().splitlines(), start=1):
    if 1 <= idx <= 140:
        print(f"{idx:4}: {line}")
PY

printf '\n== search for usages ==\n'
rg -n "embed_target_unitary_into_chip|target_dim|chip_dim" python/mqt/qmap -g '!**/__pycache__/**'

printf '\n== search for validation ==\n'
rg -n "ValueError|IndexError|cannot exceed|chip_dim|target_dim" python/mqt/qmap -g '!**/__pycache__/**'

Repository: munich-quantum-toolkit/qmap

Length of output: 38804


Add a guard for target_dim > chip_dim

Otherwise the nested assignment fails with an IndexError inside the loop; raise a ValueError before indexing the tensor.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/mqt/qmap/ph/baseline.py` around lines 21 - 38, Add an input validation
guard in embed_target_unitary_into_chip before the nested loops: if target_dim
is greater than chip_dim, raise a ValueError instead of indexing into embedded.
Keep the fix localized to this helper so the torch.eye allocation and assignment
loop only run when the target block fits within the chip-sized tensor.

Comment thread python/mqt/qmap/ph/data_collection.py Outdated
Comment on lines +72 to +74
def _mean_std(values: list[float]) -> tuple[float, float]:
arr = np.asarray(values, dtype=np.float64)
return float(arr.mean()), float(arr.std(ddof=0))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add a Google-style docstring to _mean_std.

Coding guidelines require Google-style docstrings for all Python code; this helper currently has none.

As per coding guidelines, **/*.py: MUST use Google-style docstrings in Python code.

📝 Proposed docstring
 def _mean_std(values: list[float]) -> tuple[float, float]:
+    """Compute the mean and population standard deviation of a sample.
+
+    Args:
+        values: List of numeric samples.
+
+    Returns:
+        Tuple of ``(mean, std)`` with ``std`` computed using ``ddof=0``.
+    """
     arr = np.asarray(values, dtype=np.float64)
     return float(arr.mean()), float(arr.std(ddof=0))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _mean_std(values: list[float]) -> tuple[float, float]:
arr = np.asarray(values, dtype=np.float64)
return float(arr.mean()), float(arr.std(ddof=0))
def _mean_std(values: list[float]) -> tuple[float, float]:
"""Compute the mean and population standard deviation of a sample.
Args:
values: List of numeric samples.
Returns:
Tuple of ``(mean, std)`` with ``std`` computed using ``ddof=0``.
"""
arr = np.asarray(values, dtype=np.float64)
return float(arr.mean()), float(arr.std(ddof=0))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/mqt/qmap/ph/data_collection.py` around lines 72 - 74, The helper
_mean_std currently has no Google-style docstring, so add one directly above the
function definition describing its purpose, the values argument, and the
returned mean/std tuple in Google format. Keep the implementation unchanged and
ensure the docstring follows the existing Python docstring guidelines used
elsewhere in data_collection.py.

Source: Coding guidelines

return float(arr.mean()), float(arr.std(ddof=0))


def collect_pipeline_results(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

collect_pipeline_results exceeds complexity thresholds (Ruff PLR0912/PLR0915).

13 branches / 66 statements in one function. Consider extracting the hardware-cache construction and the per-repeat run loop into helper functions.

🧰 Tools
🪛 Ruff (0.15.20)

[warning] 77-77: Too many branches (13 > 12)

(PLR0912)


[warning] 77-77: Too many statements (66 > 50)

(PLR0915)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/mqt/qmap/ph/data_collection.py` at line 77, The
collect_pipeline_results function is too complex and should be split up to
satisfy Ruff PLR0912/PLR0915. Extract the hardware-cache construction logic and
the per-repeat run loop into separate helper functions, then have
collect_pipeline_results orchestrate those helpers while keeping its
responsibilities minimal. Use the existing collect_pipeline_results entry point
and any new private helpers to preserve behavior and make the control flow
easier to follow.

Source: Linters/SAST tools

Comment on lines +84 to +87
input_losses: bool = False,
output_losses: bool = False,
ideal_beam_splitters: bool = False,
custom_bs_data: dict[int, np.ndarray] | None = None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make boolean flags keyword-only.

Ruff flags input_losses, output_losses, ideal_beam_splitters as boolean positional args (FBT001/FBT002). Forcing keyword-only usage prevents accidental positional misordering at call sites.

♻️ Proposed fix
     phase_errors: Iterable[float] = (0.01,),
     base_seed: int = 0,
+    *,
     input_losses: bool = False,
     output_losses: bool = False,
     ideal_beam_splitters: bool = False,
     custom_bs_data: dict[int, np.ndarray] | None = None,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
input_losses: bool = False,
output_losses: bool = False,
ideal_beam_splitters: bool = False,
custom_bs_data: dict[int, np.ndarray] | None = None,
phase_errors: Iterable[float] = (0.01,),
base_seed: int = 0,
*,
input_losses: bool = False,
output_losses: bool = False,
ideal_beam_splitters: bool = False,
custom_bs_data: dict[int, np.ndarray] | None = None,
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 84-84: Boolean-typed positional argument in function definition

(FBT001)


[warning] 84-84: Boolean default positional argument in function definition

(FBT002)


[warning] 85-85: Boolean-typed positional argument in function definition

(FBT001)


[warning] 85-85: Boolean default positional argument in function definition

(FBT002)


[warning] 86-86: Boolean-typed positional argument in function definition

(FBT001)


[warning] 86-86: Boolean default positional argument in function definition

(FBT002)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/mqt/qmap/ph/data_collection.py` around lines 84 - 87, Make the boolean
flags in data_collection.py keyword-only to avoid positional boolean arguments.
Update the affected function signature around the parameters input_losses,
output_losses, and ideal_beam_splitters (along with custom_bs_data if needed) by
inserting a keyword-only separator so callers must pass these flags by name.
Check all call sites of the function to ensure they use the explicit keywords.

Source: Linters/SAST tools

Comment on lines +55 to +62
def test_valid_multiple_setups():
"""Test that the Cartesian product of valid inputs produces all expected setups."""
setups = build_setup_grid([4, 6], [2, 4])
# (4,2), (4,4), (6,2), (6,4) — all even; (6,6) excluded because target==chip allowed
assert Setup(num_modes=4, target_dim=2) in setups
assert Setup(num_modes=4, target_dim=4) in setups
assert Setup(num_modes=6, target_dim=2) in setups
assert Setup(num_modes=6, target_dim=4) in setups

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Comment is confusing/inaccurate.

"(6,6) excluded because target==chip allowed" doesn't reflect why (6,6) isn't produced — target_dims_list=[2, 4] never contains 6, so (6,6) was never a candidate pair in the first place.

📝 Proposed fix
-        # (4,2), (4,4), (6,2), (6,4) — all even; (6,6) excluded because target==chip allowed
+        # (4,2), (4,4), (6,2), (6,4) — all even; (6,6) is not produced since 6 is absent from target_dims_list
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_valid_multiple_setups():
"""Test that the Cartesian product of valid inputs produces all expected setups."""
setups = build_setup_grid([4, 6], [2, 4])
# (4,2), (4,4), (6,2), (6,4) — all even; (6,6) excluded because target==chip allowed
assert Setup(num_modes=4, target_dim=2) in setups
assert Setup(num_modes=4, target_dim=4) in setups
assert Setup(num_modes=6, target_dim=2) in setups
assert Setup(num_modes=6, target_dim=4) in setups
def test_valid_multiple_setups():
"""Test that the Cartesian product of valid inputs produces all expected setups."""
setups = build_setup_grid([4, 6], [2, 4])
# (4,2), (4,4), (6,2), (6,4) — all even; (6,6) is not produced since 6 is absent from target_dims_list
assert Setup(num_modes=4, target_dim=2) in setups
assert Setup(num_modes=4, target_dim=4) in setups
assert Setup(num_modes=6, target_dim=2) in setups
assert Setup(num_modes=6, target_dim=4) in setups
🧰 Tools
🪛 Ruff (0.15.20)

[warning] 55-55: Missing return type annotation for staticmethod test_valid_multiple_setups

Add return type annotation: None

(ANN205)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/ph/test_data_collection.py` around lines 55 - 62, The comment in
test_valid_multiple_setups is inaccurate about why (6,6) is not produced by
build_setup_grid; update the test note to reflect that target_dims_list only
includes 2 and 4, so 6 is never a candidate target dimension. Keep the
assertions as-is and revise the explanatory text in test_valid_multiple_setups
to match the actual inputs and expected Cartesian product.

Comment on lines +26 to +212
@staticmethod
def test_ideal_bs_gives_one():
"""Test that ideal 50/50 beam splitters yield bar fidelity 1.0."""
assert bar_fidelity([0.5, 0.5]) == pytest.approx(1.0)

@staticmethod
def test_fully_reflective_gives_one():
"""Test that fully reflective beam splitters yield bar fidelity 1.0."""
# Both BSs reflect fully → bar state transmits all light
assert bar_fidelity([1.0, 1.0]) == pytest.approx(1.0)

@staticmethod
def test_fully_transmissive_gives_one():
"""Test that fully transmissive beam splitters yield bar fidelity 1.0."""
# Both BSs transmit fully → bar state also reaches fidelity 1
assert bar_fidelity([0.0, 0.0]) == pytest.approx(1.0)

@staticmethod
def test_cross_reflectivities_gives_zero():
"""Test that cross-configured reflectivities yield bar fidelity 0.0."""
# r0=1, r1=0 → cross config → bar fidelity should be 0
assert bar_fidelity([1.0, 0.0]) == pytest.approx(0.0)

@staticmethod
def test_symmetric_value():
"""Test that bar_fidelity is symmetric under reflectivity swap."""
# bar_fidelity is symmetric: swapping r0 and r1 gives the same result
assert bar_fidelity([0.3, 0.7]) == pytest.approx(bar_fidelity([0.7, 0.3]))

@staticmethod
def test_returns_float_in_unit_interval():
"""Test that bar_fidelity returns a value in [0, 1]."""
result = bar_fidelity([0.45, 0.55])
assert 0.0 <= result <= 1.0


class TestCrossFidelity:
"""Tests for cross_fidelity."""

@staticmethod
def test_ideal_bs_gives_one():
"""Test that ideal 50/50 beam splitters yield cross fidelity 1.0."""
assert cross_fidelity([0.5, 0.5]) == pytest.approx(1.0)

@staticmethod
def test_fully_reflective_gives_zero():
"""Test that fully reflective beam splitters yield cross fidelity 0.0."""
assert cross_fidelity([1.0, 1.0]) == pytest.approx(0.0)

@staticmethod
def test_fully_transmissive_gives_zero():
"""Test that fully transmissive beam splitters yield cross fidelity 0.0."""
assert cross_fidelity([0.0, 0.0]) == pytest.approx(0.0)

@staticmethod
def test_bar_reflectivities_gives_one():
"""Test that bar-configured reflectivities yield cross fidelity 1.0."""
# r0=1, r1=0 → cross state transmits all light
assert cross_fidelity([1.0, 0.0]) == pytest.approx(1.0)

@staticmethod
def test_symmetric_value():
"""Test that cross_fidelity is symmetric under reflectivity swap."""
assert cross_fidelity([0.3, 0.7]) == pytest.approx(cross_fidelity([0.7, 0.3]))

@staticmethod
def test_complementary_with_bar_at_ideal():
"""Test that bar and cross fidelity are equal at ideal 50/50 beam splitters."""
# At ideal BS, both bar and cross fidelity are 1.0
r = [0.5, 0.5]
assert bar_fidelity(r) == pytest.approx(cross_fidelity(r))

@staticmethod
def test_returns_float_in_unit_interval():
"""Test that cross_fidelity returns a value in [0, 1]."""
result = cross_fidelity([0.45, 0.55])
assert 0.0 <= result <= 1.0


class TestGenerateBeamSplitterMatrix:
"""Tests for generate_beam_splitter_matrix."""

@staticmethod
def test_ideal_returns_all_half():
"""Test that ideal mode returns all 0.5 reflectivities."""
bs = generate_beam_splitter_matrix(chip_size=4, ideal_bs=True)
assert np.allclose(bs, 0.5)

@staticmethod
def test_ideal_correct_size_chip4():
"""Test that a 4-mode chip yields 12 beam-splitter values."""
# chip_size=4: MZIs per layer [2, 1, 2, 1] -> 6 total -> 12 BS values
bs = generate_beam_splitter_matrix(chip_size=4, ideal_bs=True)
assert len(bs) == 12

@staticmethod
def test_ideal_correct_size_chip6():
"""Test that a 6-mode chip yields 30 beam-splitter values."""
# chip_size=6: MZIs per layer [3, 2, 3, 2, 3, 2] -> 15 total -> 30 BS values
bs = generate_beam_splitter_matrix(chip_size=6, ideal_bs=True)
assert len(bs) == 30

@staticmethod
def test_random_has_correct_size_chip4():
"""Test that random mode also yields 12 values for a 4-mode chip."""
bs = generate_beam_splitter_matrix(chip_size=4, ideal_bs=False, rng=np.random.default_rng(0))
assert len(bs) == 12

@staticmethod
def test_random_values_in_unit_interval():
"""Test that randomly sampled reflectivities lie in [0, 1]."""
bs = generate_beam_splitter_matrix(chip_size=4, ideal_bs=False, rng=np.random.default_rng(0))
assert np.all(bs >= 0.0)
assert np.all(bs <= 1.0)


class TestDetermineRoutingFidelities:
"""Tests for determine_routing_fidelitites."""

@staticmethod
def test_ideal_bs_all_bar_fidelities_are_one(ideal_bs_chip4):
"""Test that ideal beam splitters yield bar fidelity 1.0 for all MZIs."""
bar_fids, _ = determine_routing_fidelitites(ideal_bs_chip4, chip_dim=4)
assert all(pytest.approx(1.0) == f for f in bar_fids)

@staticmethod
def test_ideal_bs_all_cross_fidelities_are_one(ideal_bs_chip4):
"""Test that ideal beam splitters yield cross fidelity 1.0 for all MZIs."""
_, cross_fids = determine_routing_fidelitites(ideal_bs_chip4, chip_dim=4)
assert all(pytest.approx(1.0) == f for f in cross_fids)

@staticmethod
def test_correct_number_of_fidelities_chip4(ideal_bs_chip4):
"""Test that a 4-mode chip produces 6 bar and 6 cross fidelity values."""
# chip_size=4: 4 layers -> MZIs [2, 1, 2, 1] -> 6 fidelity values each
bar_fids, cross_fids = determine_routing_fidelitites(ideal_bs_chip4, chip_dim=4)
assert len(bar_fids) == 6
assert len(cross_fids) == 6

@staticmethod
def test_nonideal_bs_bar_fidelities_match_correct_pairs(nonideal_bs_chip4):
"""Test that each bar fidelity is computed from the correct in/out pair.

The BS array has layout ``[in0, out0, in1, out1, …]``, so MZI k uses
indices ``[2k, 2k+1]``. Any wrong pairing would produce a different
fidelity value because the per-MZI reflectivities are all distinct.
"""
bar_fids, _ = determine_routing_fidelitites(nonideal_bs_chip4, chip_dim=4)
expected = [bar_fidelity([nonideal_bs_chip4[2 * k], nonideal_bs_chip4[2 * k + 1]]) for k in range(6)]
assert bar_fids == pytest.approx(expected)

@staticmethod
def test_nonideal_bs_cross_fidelities_match_correct_pairs(nonideal_bs_chip4):
"""Test that each cross fidelity is computed from the correct in/out pair."""
_, cross_fids = determine_routing_fidelitites(nonideal_bs_chip4, chip_dim=4)
expected = [cross_fidelity([nonideal_bs_chip4[2 * k], nonideal_bs_chip4[2 * k + 1]]) for k in range(6)]
assert cross_fids == pytest.approx(expected)


class TestConstructGraph:
"""Tests for construct_graph."""

@staticmethod
def test_graph_has_nodes_and_edges(ideal_bs_chip4, ones_transmissions_chip4):
"""Test that the constructed graph has at least one node and one edge."""
graph, _pos, _layers = construct_graph(
chip_dim=4,
target_dim=2,
input_transmission=ones_transmissions_chip4,
output_transmission=ones_transmissions_chip4,
beam_splitter_reflectivities=ideal_bs_chip4,
)
assert graph.num_nodes() > 0
assert graph.num_edges() > 0

@staticmethod
def test_number_of_layers_chip4_target2(ideal_bs_chip4, ones_transmissions_chip4):
"""Test that chip_dim=4, target_dim=2 yields 5 routing layers."""
# number_of_layers = chip_dim - target_dim + 3 = 5
_, _, layers = construct_graph(
chip_dim=4,
target_dim=2,
input_transmission=ones_transmissions_chip4,
output_transmission=ones_transmissions_chip4,
beam_splitter_reflectivities=ideal_bs_chip4,
)
assert len(layers) == 5

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add -> None return annotations to the test methods.

Ruff flags every static test method here with ANN205 (missing return type annotation). Since the project prefers fixing reported warnings, annotate each with -> None to keep the lint clean.

As per coding guidelines: "PREFER fixing reported warnings over suppressing them".

🧰 Tools
🪛 Ruff (0.15.20)

[warning] 27-27: Missing return type annotation for staticmethod test_ideal_bs_gives_one

Add return type annotation: None

(ANN205)


[warning] 32-32: Missing return type annotation for staticmethod test_fully_reflective_gives_one

Add return type annotation: None

(ANN205)


[warning] 38-38: Missing return type annotation for staticmethod test_fully_transmissive_gives_one

Add return type annotation: None

(ANN205)


[warning] 44-44: Missing return type annotation for staticmethod test_cross_reflectivities_gives_zero

Add return type annotation: None

(ANN205)


[warning] 50-50: Missing return type annotation for staticmethod test_symmetric_value

Add return type annotation: None

(ANN205)


[warning] 56-56: Missing return type annotation for staticmethod test_returns_float_in_unit_interval

Add return type annotation: None

(ANN205)


[warning] 66-66: Missing return type annotation for staticmethod test_ideal_bs_gives_one

Add return type annotation: None

(ANN205)


[warning] 71-71: Missing return type annotation for staticmethod test_fully_reflective_gives_zero

Add return type annotation: None

(ANN205)


[warning] 76-76: Missing return type annotation for staticmethod test_fully_transmissive_gives_zero

Add return type annotation: None

(ANN205)


[warning] 81-81: Missing return type annotation for staticmethod test_bar_reflectivities_gives_one

Add return type annotation: None

(ANN205)


[warning] 87-87: Missing return type annotation for staticmethod test_symmetric_value

Add return type annotation: None

(ANN205)


[warning] 92-92: Missing return type annotation for staticmethod test_complementary_with_bar_at_ideal

Add return type annotation: None

(ANN205)


[warning] 99-99: Missing return type annotation for staticmethod test_returns_float_in_unit_interval

Add return type annotation: None

(ANN205)


[warning] 109-109: Missing return type annotation for staticmethod test_ideal_returns_all_half

Add return type annotation: None

(ANN205)


[warning] 115-115: Missing return type annotation for staticmethod test_ideal_correct_size_chip4

Add return type annotation: None

(ANN205)


[warning] 122-122: Missing return type annotation for staticmethod test_ideal_correct_size_chip6

Add return type annotation: None

(ANN205)


[warning] 129-129: Missing return type annotation for staticmethod test_random_has_correct_size_chip4

Add return type annotation: None

(ANN205)


[warning] 135-135: Missing return type annotation for staticmethod test_random_values_in_unit_interval

Add return type annotation: None

(ANN205)


[warning] 146-146: Missing return type annotation for staticmethod test_ideal_bs_all_bar_fidelities_are_one

Add return type annotation: None

(ANN205)


[warning] 152-152: Missing return type annotation for staticmethod test_ideal_bs_all_cross_fidelities_are_one

Add return type annotation: None

(ANN205)


[warning] 158-158: Missing return type annotation for staticmethod test_correct_number_of_fidelities_chip4

Add return type annotation: None

(ANN205)


[warning] 166-166: Missing return type annotation for staticmethod test_nonideal_bs_bar_fidelities_match_correct_pairs

Add return type annotation: None

(ANN205)


[warning] 178-178: Missing return type annotation for staticmethod test_nonideal_bs_cross_fidelities_match_correct_pairs

Add return type annotation: None

(ANN205)


[warning] 189-189: Missing return type annotation for staticmethod test_graph_has_nodes_and_edges

Add return type annotation: None

(ANN205)


[warning] 202-202: Missing return type annotation for staticmethod test_number_of_layers_chip4_target2

Add return type annotation: None

(ANN205)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/ph/test_graph.py` around lines 26 - 212, Add explicit `-> None`
return annotations to every `@staticmethod` test in `TestBarFidelity`,
`TestCrossFidelity`, `TestGenerateBeamSplitterMatrix`,
`TestDetermineRoutingFidelities`, and `TestConstructGraph` in `test_graph.py`.
The issue is Ruff ANN205 missing return type annotations on these test methods.
Update each test method signature to include `-> None` so the file stays
lint-clean without suppressions.

Sources: Coding guidelines, Linters/SAST tools

Comment on lines +24 to +128
@staticmethod
def test_no_exclude_sequential_fill():
"""Test that a flat parameter vector is reshaped into an (N, N) grid without excluded corners."""
params = torch.arange(16, dtype=torch.float64)
grid = reshape_flattened_params_to_grid(params, num_modes=4, exclude_edge_phase_shifters=False)
assert grid.shape == (4, 4)
assert torch.equal(grid, params.reshape(4, 4))

@staticmethod
def test_exclude_edge_zeroes_corners():
"""Test that excluded corners are set to zero in the output grid."""
params = torch.arange(14, dtype=torch.float64)
grid = reshape_flattened_params_to_grid(params, num_modes=4, exclude_edge_phase_shifters=True)
assert grid.shape == (4, 4)
assert not grid[0, 3].item() # top-right corner
assert not grid[3, 3].item() # bottom-right corner

@staticmethod
def test_exclude_edge_fills_remaining_14_positions():
"""Test that excluding corners leaves exactly 14 active positions filled with ones."""
params = torch.ones(14, dtype=torch.float64)
grid = reshape_flattened_params_to_grid(params, num_modes=4, exclude_edge_phase_shifters=True)
# Exactly 2 zeros (the corners), 14 ones
assert (~grid.bool()).sum().item() == 2
assert grid.bool().sum().item() == 14

@staticmethod
def test_wrong_size_raises_value_error():
"""Test that an incorrectly sized parameter vector raises ValueError."""
with pytest.raises(ValueError, match="Size mismatch"):
reshape_flattened_params_to_grid(torch.zeros(10), num_modes=4)

@staticmethod
def test_wrong_size_exclude_raises_value_error():
"""Test that a 16-element vector raises ValueError when corner exclusion expects 14."""
with pytest.raises(ValueError, match="Size mismatch"):
reshape_flattened_params_to_grid(torch.zeros(16), num_modes=4, exclude_edge_phase_shifters=True)


class TestGetEffectiveParamsAndMask:
"""Tests for get_effective_params_and_mask."""

@staticmethod
def _bar_mask(chip_dim):
return torch.ones((chip_dim, chip_dim), dtype=torch.int)

@staticmethod
def _cross_mask(chip_dim):
return torch.full((chip_dim, chip_dim), MaskState.CROSS, dtype=torch.int)

def test_all_bar_mask_forces_zero_pi_no_optimize(self):
"""Test that a full bar mask sets even-layer MZI pairs to (0, pi) when routing optimization is disabled."""
chip_dim = 4
mask = self._bar_mask(chip_dim)
raw = torch.zeros((chip_dim, chip_dim), dtype=torch.float64)

eff, _grad, _ = get_effective_params_and_mask(chip_dim, mask, raw, optimize_routing_parameters=False)

# Even layers: each MZI pair → (top=0, bot=pi)
for layer in range(0, chip_dim, 2):
for top in range(0, chip_dim - 1, 2):
assert eff[top, layer].item() == pytest.approx(0.0)
assert eff[top + 1, layer].item() == pytest.approx(math.pi)

def test_all_bar_mask_zeros_gradients_no_optimize(self):
"""Test that a full bar mask zeros all gradients when routing optimization is disabled."""
chip_dim = 4
mask = self._bar_mask(chip_dim)
raw = torch.zeros((chip_dim, chip_dim), dtype=torch.float64)

_, grad, _ = get_effective_params_and_mask(chip_dim, mask, raw, optimize_routing_parameters=False)

assert not grad.any()

def test_all_cross_mask_forces_both_zero_no_optimize(self):
"""Test that a full cross mask forces effective params to zero when routing optimization is disabled."""
chip_dim = 4
mask = self._cross_mask(chip_dim)
raw = torch.zeros((chip_dim, chip_dim), dtype=torch.float64)

eff, grad, _ = get_effective_params_and_mask(chip_dim, mask, raw, optimize_routing_parameters=False)

assert not eff.any()
assert not grad.any()

@staticmethod
def test_mzi_zone_passes_through_nonzero_params():
"""Test that MZI-zone parameters pass through unchanged when routing optimization is disabled."""
chip_dim = 4
mask = torch.zeros((chip_dim, chip_dim), dtype=torch.int) # all MaskState.MZI
raw = torch.ones((chip_dim, chip_dim), dtype=torch.float64)

eff, _grad, _ = get_effective_params_and_mask(chip_dim, mask, raw, optimize_routing_parameters=False)

# Non-zero MZI params should pass through unchanged
assert torch.allclose(eff, raw)

def test_returns_three_tensors(self):
"""Test that get_effective_params_and_mask returns a tuple of three tensors."""
chip_dim = 4
mask = self._bar_mask(chip_dim)
raw = torch.zeros((chip_dim, chip_dim), dtype=torch.float64)

result = get_effective_params_and_mask(chip_dim, mask, raw)
assert len(result) == 3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add -> None (and helper) return annotations.

ANN205 is flagged on the test methods and on the _bar_mask/_cross_mask helpers (which should be annotated torch.Tensor). Annotate to clear the warnings.

As per coding guidelines: "PREFER fixing reported warnings over suppressing them".

🧰 Tools
🪛 Ruff (0.15.20)

[warning] 25-25: Missing return type annotation for staticmethod test_no_exclude_sequential_fill

Add return type annotation: None

(ANN205)


[warning] 33-33: Missing return type annotation for staticmethod test_exclude_edge_zeroes_corners

Add return type annotation: None

(ANN205)


[warning] 42-42: Missing return type annotation for staticmethod test_exclude_edge_fills_remaining_14_positions

Add return type annotation: None

(ANN205)


[warning] 51-51: Missing return type annotation for staticmethod test_wrong_size_raises_value_error

Add return type annotation: None

(ANN205)


[warning] 57-57: Missing return type annotation for staticmethod test_wrong_size_exclude_raises_value_error

Add return type annotation: None

(ANN205)


[warning] 67-67: Missing return type annotation for staticmethod _bar_mask

(ANN205)


[warning] 71-71: Missing return type annotation for staticmethod _cross_mask

(ANN205)


[warning] 110-110: Missing return type annotation for staticmethod test_mzi_zone_passes_through_nonzero_params

Add return type annotation: None

(ANN205)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/ph/test_phases.py` around lines 24 - 128, Annotate the test
methods in TestPhases and TestGetEffectiveParamsAndMask with explicit -> None
return types to satisfy ANN205, and add explicit torch.Tensor return annotations
to the helper methods _bar_mask and _cross_mask so the type warnings are cleared
without suppressing them.

Sources: Coding guidelines, Linters/SAST tools

Comment on lines +30 to +232
@staticmethod
def test_straight_route_first_position():
"""Test that a straight route through position 0 yields input port 0, output ports [0,1], and active col 0."""
# Source → input 0 → ... → compute 0 → sink
input_ports, output_ports, active_cols = infer_input_computation_and_output_ports([0, 0, 0, 0, 0], target_dim=2)
assert input_ports == [0]
assert output_ports == [0, 1]
assert active_cols == [0]

@staticmethod
def test_route_at_second_input_position():
"""Test that a route through input position 1 yields input port 1, output ports [2,3], and active col 1."""
# Source → input 1 → intermediate nodes → compute at odd index → sink
input_ports, output_ports, active_cols = infer_input_computation_and_output_ports([0, 1, 1, 1, 0], target_dim=2)
assert input_ports == [2]
assert output_ports == [0, 1]
assert active_cols == [1]

@staticmethod
def test_active_cols_even_for_even_computation_index():
"""Test that an even computation index yields only even active columns."""
_, _, active_cols = infer_input_computation_and_output_ports([0, 0, 0, 0, 0], target_dim=4)
# computation_index=0, even → active_cols=[0, 2]
assert all(c % 2 == 0 for c in active_cols)

@staticmethod
def test_active_cols_odd_for_odd_computation_index():
"""Test that an odd computation index yields only odd active columns."""
_, _, active_cols = infer_input_computation_and_output_ports([0, 0, 1, 1, 0], target_dim=4)
# computation_index=1, odd → active_cols=[1, 3]
assert all(c % 2 == 1 for c in active_cols)

@staticmethod
def test_raises_for_too_short_route():
"""Test that a route with fewer than 2 nodes raises ValueError."""
with pytest.raises(ValueError, match="at least 2 nodes"):
infer_input_computation_and_output_ports([0], target_dim=2)


class TestConvertInputPorts:
"""Tests for convert_input_ports."""

@staticmethod
def test_first_mode_active():
"""Test that active port 0 on a 4-mode chip gives [1, 0, 0, 0]."""
# input_ports=[0] on a 4-mode chip: mode 0 gets photon, mode 1 skipped
result = convert_input_ports([0], chip_dim=4)
assert result == [1, 0, 0, 0]

@staticmethod
def test_third_mode_active():
"""Test that active port 2 on a 4-mode chip gives [0, 0, 1, 0]."""
# input_ports=[2]: mode 2 gets photon, mode 3 skipped
result = convert_input_ports([2], chip_dim=4)
assert result == [0, 0, 1, 0]

@staticmethod
def test_no_active_modes():
"""Test that no active ports yields an all-zero vector."""
result = convert_input_ports([], chip_dim=4)
assert result == [0, 0, 0, 0]

@staticmethod
def test_total_length_matches_chip_dim():
"""Test that the result length equals chip_dim."""
result = convert_input_ports([0], chip_dim=6)
assert len(result) == 6


class TestConvertOutputPorts:
"""Tests for convert_output_ports."""

@staticmethod
def test_first_window():
"""Test that output ports [0, 1] on a 4-mode chip gives [1, 1, 0, 0]."""
result = convert_output_ports([0, 1], chip_dim=4)
assert result == [1, 1, 0, 0]

@staticmethod
def test_second_window():
"""Test that output ports [2, 3] on a 4-mode chip gives [0, 0, 1, 1]."""
result = convert_output_ports([2, 3], chip_dim=4)
assert result == [0, 0, 1, 1]

@staticmethod
def test_empty_output_ports():
"""Test that no output ports yields an all-zero vector."""
result = convert_output_ports([], chip_dim=4)
assert result == [0, 0, 0, 0]

@staticmethod
def test_length_matches_chip_dim():
"""Test that the result length equals chip_dim."""
result = convert_output_ports([0, 1], chip_dim=6)
assert len(result) == 6


class TestGetInputPortsForComputationZone:
"""Tests for get_input_ports_for_computation_zone."""

@staticmethod
def test_first_active_col():
"""Test that active col 0 with target_dim=2 gives [1, 0]."""
result = get_input_ports_for_computation_zone([0], target_dim=2)
assert result == [1, 0]

@staticmethod
def test_second_active_col():
"""Test that active col 1 with target_dim=2 gives [0, 1]."""
result = get_input_ports_for_computation_zone([1], target_dim=2)
assert result == [0, 1]

@staticmethod
def test_multiple_active_cols():
"""Test that active cols [0, 2] with target_dim=4 gives [1, 0, 1, 0]."""
result = get_input_ports_for_computation_zone([0, 2], target_dim=4)
assert result == [1, 0, 1, 0]


class TestRouteToMovementMask:
"""Tests for route_to_movement_mask."""

@staticmethod
def test_straight_route_chip4_target2():
"""Test that the straight route on a 4-mode chip produces the expected movement mask."""
# Straight route: all-BAR routing, compute zone at modes 0-1, layers 2-3
mask = route_to_movement_mask([0, 0, 0, 0, 0], chip_dim=4, target_dim=2)

expected = torch.tensor(
[
[MaskState.BAR, MaskState.BAR, MaskState.MZI, MaskState.MZI],
[MaskState.BAR, MaskState.BAR, MaskState.MZI, MaskState.TOP_ONLY],
[MaskState.BAR, MaskState.BAR, MaskState.BAR, MaskState.TOP_ONLY],
[MaskState.BAR, MaskState.BAR, MaskState.BAR, MaskState.BAR],
],
dtype=torch.int,
)

assert torch.equal(mask, expected)

@staticmethod
def test_mask_shape():
"""Test that the movement mask has shape (chip_dim, chip_dim)."""
mask = route_to_movement_mask([0, 0, 0, 0, 0], chip_dim=4, target_dim=2)
assert mask.shape == (4, 4)

@staticmethod
def test_empty_route_returns_all_bar():
"""Test that an empty route produces an all-BAR mask."""
mask = route_to_movement_mask([], chip_dim=4, target_dim=2)
assert torch.all(mask == MaskState.BAR)

@staticmethod
def test_compute_zone_contains_only_mzi_or_virtual_states():
"""Test that the compute zone contains only MZI, TOP_ONLY, or BOT_ONLY states."""
mask = route_to_movement_mask([0, 0, 0, 0, 0], chip_dim=4, target_dim=2)
compute_zone = mask[0:2, 2:4]
valid_compute_states = {MaskState.MZI, MaskState.TOP_ONLY, MaskState.BOT_ONLY}
assert all(v.item() in valid_compute_states for v in compute_zone.flatten())

@staticmethod
def test_routing_zone_contains_only_bar_or_cross():
"""Test that the routing zone contains only BAR or CROSS states."""
mask = route_to_movement_mask([0, 0, 0, 0, 0], chip_dim=4, target_dim=2)
routing_zone = mask[:, 0:2]
valid_routing_states = {MaskState.BAR, MaskState.CROSS}
assert all(v.item() in valid_routing_states for v in routing_zone.flatten())


class TestGetBestRoute:
"""Tests for get_best_route."""

@staticmethod
def test_ideal_bs_returns_deterministic_route(ideal_bs_chip4, ones_transmissions_chip4):
"""Test that ideal beam splitters yield a valid route with zero cost."""
graph, _, layers = construct_graph(
chip_dim=4,
target_dim=2,
input_transmission=ones_transmissions_chip4,
output_transmission=ones_transmissions_chip4,
beam_splitter_reflectivities=ideal_bs_chip4,
)
route, cost = get_best_route(graph, layers)

# With all-ideal components: all paths have equal cost (0)
assert isinstance(route, list)
assert len(route) == 5 # number_of_layers for chip4/target2
assert cost == pytest.approx(0.0)

@staticmethod
def test_route_starts_and_ends_at_zero(ideal_bs_chip4, ones_transmissions_chip4):
"""Test that the route begins and ends at node index 0 (source/sink)."""
graph, _, layers = construct_graph(
chip_dim=4,
target_dim=2,
input_transmission=ones_transmissions_chip4,
output_transmission=ones_transmissions_chip4,
beam_splitter_reflectivities=ideal_bs_chip4,
)
route, _ = get_best_route(graph, layers)

assert route[0] == 0 # source
assert route[-1] == 0 # sink

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add -> None return annotations to the test methods.

Same ANN205 warnings apply throughout this file. Annotate each static test method with -> None.

As per coding guidelines: "PREFER fixing reported warnings over suppressing them".

🧰 Tools
🪛 Ruff (0.15.20)

[warning] 31-31: Missing return type annotation for staticmethod test_straight_route_first_position

Add return type annotation: None

(ANN205)


[warning] 40-40: Missing return type annotation for staticmethod test_route_at_second_input_position

Add return type annotation: None

(ANN205)


[warning] 49-49: Missing return type annotation for staticmethod test_active_cols_even_for_even_computation_index

Add return type annotation: None

(ANN205)


[warning] 56-56: Missing return type annotation for staticmethod test_active_cols_odd_for_odd_computation_index

Add return type annotation: None

(ANN205)


[warning] 63-63: Missing return type annotation for staticmethod test_raises_for_too_short_route

Add return type annotation: None

(ANN205)


[warning] 73-73: Missing return type annotation for staticmethod test_first_mode_active

Add return type annotation: None

(ANN205)


[warning] 80-80: Missing return type annotation for staticmethod test_third_mode_active

Add return type annotation: None

(ANN205)


[warning] 87-87: Missing return type annotation for staticmethod test_no_active_modes

Add return type annotation: None

(ANN205)


[warning] 93-93: Missing return type annotation for staticmethod test_total_length_matches_chip_dim

Add return type annotation: None

(ANN205)


[warning] 103-103: Missing return type annotation for staticmethod test_first_window

Add return type annotation: None

(ANN205)


[warning] 109-109: Missing return type annotation for staticmethod test_second_window

Add return type annotation: None

(ANN205)


[warning] 115-115: Missing return type annotation for staticmethod test_empty_output_ports

Add return type annotation: None

(ANN205)


[warning] 121-121: Missing return type annotation for staticmethod test_length_matches_chip_dim

Add return type annotation: None

(ANN205)


[warning] 131-131: Missing return type annotation for staticmethod test_first_active_col

Add return type annotation: None

(ANN205)


[warning] 137-137: Missing return type annotation for staticmethod test_second_active_col

Add return type annotation: None

(ANN205)


[warning] 143-143: Missing return type annotation for staticmethod test_multiple_active_cols

Add return type annotation: None

(ANN205)


[warning] 153-153: Missing return type annotation for staticmethod test_straight_route_chip4_target2

Add return type annotation: None

(ANN205)


[warning] 171-171: Missing return type annotation for staticmethod test_mask_shape

Add return type annotation: None

(ANN205)


[warning] 177-177: Missing return type annotation for staticmethod test_empty_route_returns_all_bar

Add return type annotation: None

(ANN205)


[warning] 183-183: Missing return type annotation for staticmethod test_compute_zone_contains_only_mzi_or_virtual_states

Add return type annotation: None

(ANN205)


[warning] 191-191: Missing return type annotation for staticmethod test_routing_zone_contains_only_bar_or_cross

Add return type annotation: None

(ANN205)


[warning] 203-203: Missing return type annotation for staticmethod test_ideal_bs_returns_deterministic_route

Add return type annotation: None

(ANN205)


[warning] 220-220: Missing return type annotation for staticmethod test_route_starts_and_ends_at_zero

Add return type annotation: None

(ANN205)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/ph/test_routing.py` around lines 30 - 232, The test methods in
TestRouting and the related static test classes are missing explicit return type
annotations, triggering ANN205 warnings. Update each `@staticmethod` test method
in this file to declare -> None, including methods like
test_straight_route_first_position, test_first_mode_active,
test_straight_route_chip4_target2, and the other test_* functions, without
changing the test logic or assertions.

Sources: Coding guidelines, Linters/SAST tools

Comment on lines +167 to +262
def test_returns_run_result(scenario_result):
"""Test that compile_subcircuit returns a RunResult instance."""
assert isinstance(scenario_result.result, RunResult)

@staticmethod
def test_performance_dict_has_required_keys(scenario_result):
"""Test that the performance dict contains all required metric keys."""
assert set(scenario_result.result.performance.keys()) >= _PERF_KEYS

@staticmethod
def test_baseline_performance_dict_has_required_keys(scenario_result):
"""Test that the baseline performance dict contains all required metric keys."""
assert set(scenario_result.result.baseline_performance.keys()) >= _PERF_KEYS

@staticmethod
def test_losses_is_float(scenario_result):
"""Test that loss and baseline_loss are convertible to float."""
assert isinstance(float(scenario_result.result.loss), float)
assert isinstance(float(scenario_result.result.baseline_loss), float)

@staticmethod
def test_compute_times_are_positive(scenario_result):
"""Test that compute_time and baseline_compute_time are strictly positive."""
assert scenario_result.result.compute_time > 0
assert scenario_result.result.baseline_compute_time > 0

@staticmethod
def test_coincidence_rate_in_unit_interval(scenario_result):
"""Test that coincidence_rate values lie in [0, 1] for both compiled and baseline."""
for cr in (
float(scenario_result.result.performance["coincidence_rate"]),
float(scenario_result.result.baseline_performance["coincidence_rate"]),
):
assert cr >= 0.0
assert cr <= 1.0 or cr == pytest.approx(1.0)

@staticmethod
def test_tvd_in_unit_interval(scenario_result):
"""Test that TVD values lie in [0, 1] for both compiled and baseline."""
for tvd in (
float(scenario_result.result.performance["tvd"]),
float(scenario_result.result.baseline_performance["tvd"]),
):
assert tvd >= 0.0
assert tvd <= 1.0 or tvd == pytest.approx(1.0)

@staticmethod
def test_losses_non_negative(scenario_result):
"""Test that loss and baseline_loss are non-negative."""
assert float(scenario_result.result.loss) >= 0.0
assert float(scenario_result.result.baseline_loss) >= 0.0


class TestRunValueRanges:
"""Range-based regression checks with per-scenario bounds.

coincidence_rate_min varies with t_low (transmission loss reduces detected photons).
tvd_max is split by phase_error regime: tight (0.01) for phase_error=0,
loose (0.05) for phase_error>0. losses_max is uniform across all scenarios.

Bounds are conservative — tighten them after observing typical results.
"""

@staticmethod
def test_coincidence_rate_above_minimum(scenario_result):
"""Test that the compiled coincidence rate meets the scenario's minimum threshold."""
cr = float(scenario_result.result.performance["coincidence_rate"])
assert cr >= scenario_result.scenario.coincidence_rate_min

@staticmethod
def test_baseline_coincidence_rate_above_minimum(scenario_result):
"""Test that the baseline coincidence rate meets the baseline's minimum threshold."""
cr = float(scenario_result.result.baseline_performance["coincidence_rate"])
assert cr >= scenario_result.scenario.baseline_coincidence_rate_min

@staticmethod
def test_tvd_below_maximum(scenario_result):
"""Test that the compiled TVD is below the scenario's maximum threshold."""
tvd = float(scenario_result.result.performance["tvd"])
assert tvd <= scenario_result.scenario.tvd_max

@staticmethod
def test_baseline_tvd_below_maximum(scenario_result):
"""Test that the baseline TVD is below the baseline's maximum threshold."""
tvd = float(scenario_result.result.baseline_performance["tvd"])
assert tvd <= scenario_result.scenario.baseline_tvd_max

@staticmethod
def test_optimization_loss_below_maximum(scenario_result):
"""Test that the final optimization loss is below the convergence threshold."""
assert float(scenario_result.result.loss) <= scenario_result.scenario.losses_max

@staticmethod
def test_baseline_loss_below_maximum(scenario_result):
"""Test that the baseline loss is below the convergence threshold."""
assert float(scenario_result.result.baseline_loss) <= scenario_result.scenario.losses_max

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add -> None return type annotations to test methods.

Ruff flags all staticmethod test functions in this file (ANN205) for missing return type annotations. Per coding guidelines, warnings should be fixed rather than left unaddressed.

As per coding guidelines, "PREFER fixing reported warnings over suppressing them".

🧰 Tools
🪛 Ruff (0.15.20)

[warning] 167-167: Missing return type annotation for staticmethod test_returns_run_result

Add return type annotation: None

(ANN205)


[warning] 172-172: Missing return type annotation for staticmethod test_performance_dict_has_required_keys

Add return type annotation: None

(ANN205)


[warning] 177-177: Missing return type annotation for staticmethod test_baseline_performance_dict_has_required_keys

Add return type annotation: None

(ANN205)


[warning] 182-182: Missing return type annotation for staticmethod test_losses_is_float

Add return type annotation: None

(ANN205)


[warning] 188-188: Missing return type annotation for staticmethod test_compute_times_are_positive

Add return type annotation: None

(ANN205)


[warning] 194-194: Missing return type annotation for staticmethod test_coincidence_rate_in_unit_interval

Add return type annotation: None

(ANN205)


[warning] 204-204: Missing return type annotation for staticmethod test_tvd_in_unit_interval

Add return type annotation: None

(ANN205)


[warning] 214-214: Missing return type annotation for staticmethod test_losses_non_negative

Add return type annotation: None

(ANN205)


[warning] 231-231: Missing return type annotation for staticmethod test_coincidence_rate_above_minimum

Add return type annotation: None

(ANN205)


[warning] 237-237: Missing return type annotation for staticmethod test_baseline_coincidence_rate_above_minimum

Add return type annotation: None

(ANN205)


[warning] 243-243: Missing return type annotation for staticmethod test_tvd_below_maximum

Add return type annotation: None

(ANN205)


[warning] 249-249: Missing return type annotation for staticmethod test_baseline_tvd_below_maximum

Add return type annotation: None

(ANN205)


[warning] 255-255: Missing return type annotation for staticmethod test_optimization_loss_below_maximum

Add return type annotation: None

(ANN205)


[warning] 260-260: Missing return type annotation for staticmethod test_baseline_loss_below_maximum

Add return type annotation: None

(ANN205)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/ph/test_subcircuit_compilation.py` around lines 167 - 262, Add
explicit `-> None` return annotations to each `@staticmethod` test in
`TestRunResultProperties` and `TestRunValueRanges` to satisfy Ruff ANN205.
Update the signatures of the test methods like `test_returns_run_result`,
`test_performance_dict_has_required_keys`, and the other static test helpers in
this file so they clearly return nothing; keep the test logic unchanged.

Source: Linters/SAST tools

@tobi-forster tobi-forster force-pushed the tobi/add-photonics-subcircuit-compiler branch from ef3a439 to 0313d46 Compare July 6, 2026 14:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant