Skip to content

Cqt 455 implement analyzer pass in open squirrel#682

Open
SoufiTNO wants to merge 6 commits into
developfrom
CQT-455-Implement-analyzer-pass-in-OpenSquirrel
Open

Cqt 455 implement analyzer pass in open squirrel#682
SoufiTNO wants to merge 6 commits into
developfrom
CQT-455-Implement-analyzer-pass-in-OpenSquirrel

Conversation

@SoufiTNO
Copy link
Copy Markdown
Contributor

@SoufiTNO SoufiTNO commented May 5, 2026

Adds a new ''Analyzer'' pass type and the first concrete implementation, "'CircuitAnalyzer'', which computes structural metrics describing a quantum circuit. Metrics are organized into four categories :

  • Size: number of qubits, gates, two-qubit gates, two-qubit gate percentage, depth
  • Interaction graph (IG): average shortest path, std of adjacency, diameter, central dominance, average degree, number of maximal cliques, clustering coefficient
  • Gate dependency graph (GDG): critical path length, path length mean/std, fraction of gates on critical path
  • Density: density score, idling score

elenbaasc and others added 2 commits April 30, 2026 10:29
- Implement Analyzer ABC in opensquirrel/passes/analyzer/general_analyzer.py
- Implement CircuitAnalyzer with size, IG, GDG, and density metrics
- Update Circuit.analyze() return type to dict[str, Any]
- Add 14 tests in tests/passes/analyzer/
- Update CHANGELOG
@SoufiTNO SoufiTNO requested a review from elenbaasc May 5, 2026 12:33
Copy link
Copy Markdown
Contributor

@rares1609 rares1609 left a comment

Choose a reason for hiding this comment

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

Very cool pass! Its definitely helpful to have these metrics easily accessible.

I've only left a couple of minor comments, not approving/requesting changes specifically. Nice work!

Comment thread CHANGELOG.md Outdated
- `CNOT2CZDecomposer` decomposer pass
- `RoutingChecker` routing pass
- Restore SGMQ notation for barrier groups in cQASMv1 Exporter
- `CircuitAnalyzer` analyzer pass for computing structural circuit metrics (size, interaction graph, gate dependency graph, density)
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.

This would need to be added to the #Added section at line 15 in the file.

Comment on lines +107 to +122
def test_size_metrics_on_empty_circuit(analyzer: CircuitAnalyzer, empty_circuit: Circuit) -> None:
result = analyzer.analyze(empty_circuit)
assert result["n_qubits"] == 3
assert result["n_gates"] == 0
assert result["n_two_qubit_gates"] == 0
assert result["two_qubit_pct"] == pytest.approx(0.0, abs=1e-9)
assert result["depth"] == 0


def test_size_metrics_on_ghz(analyzer: CircuitAnalyzer, ghz_circuit: Circuit) -> None:
result = analyzer.analyze(ghz_circuit)
assert result["n_qubits"] == 3
assert result["n_gates"] == 3
assert result["n_two_qubit_gates"] == 2
assert result["two_qubit_pct"] == pytest.approx(2 / 3, abs=1e-3)
assert result["depth"] == 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.

Maybe you could group these two tests with using @pytest.mark.parametrize?

Comment on lines +125 to +136
def test_depth_with_parallel_gates(analyzer: CircuitAnalyzer, parallel_circuit: Circuit) -> None:
"""Two independent gates can run in the same time-step, so depth is 1."""
result = analyzer.analyze(parallel_circuit)
assert result["depth"] == 1
assert result["n_gates"] == 2


def test_depth_with_sequential_gates(analyzer: CircuitAnalyzer, sequential_circuit: Circuit) -> None:
"""Gates that share qubits must serialise, so depth equals gate count."""
result = analyzer.analyze(sequential_circuit)
assert result["depth"] == 3
assert result["n_gates"] == 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.

Same comment as above

@SoufiTNO
Copy link
Copy Markdown
Contributor Author

"Hey Chris, thanks for the tip! I ve grouped the size, depth, IG, and GDG tests using @pytest.mark.parametrize. I also modified the CHANGELOG.md, and made sure all the linter checks are green. Thanks for the review !"

@elenbaasc
Copy link
Copy Markdown
Collaborator

"Hey Chris, thanks for the tip! I ve grouped the size, depth, IG, and GDG tests using @pytest.mark.parametrize. I also modified the CHANGELOG.md, and made sure all the linter checks are green. Thanks for the review !"

No thanks to me! It was Rares (@rares1609) who did the review 😄! Thanks for making the changes

@SoufiTNO
Copy link
Copy Markdown
Contributor Author

Oops , my bad! Massive thanks to Rares for the review 🙌

Copy link
Copy Markdown
Collaborator

@elenbaasc elenbaasc left a comment

Choose a reason for hiding this comment

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

Nice work!

Just a few minor comments, nothing too serious :)


* Size: number of qubits, gates, two-qubit gates, two-qubit gate percentage, depth.
* Interaction graph (IG): metrics derived from the qubit interaction graph,
where nodes are qubits and edges are two-qubit gates.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
where nodes are qubits and edges are two-qubit gates.
where nodes correspond to qubits and edges correspond to two-qubit gates.

Comment on lines +79 to +80
for gate in gate_statements:
qubit_indices = list(gate.qubit_indices)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
for gate in gate_statements:
qubit_indices = list(gate.qubit_indices)
for gate_statement in gate_statements:
qubit_indices = list(gate_statement.qubit_indices)

Comment on lines +81 to +82
if not qubit_indices:
continue
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This would be strange. All gates should have qubit indices...

Suggested change
if not qubit_indices:
continue

Comment on lines +83 to +85
new_layer = max(layer[i] for i in qubit_indices) + 1
for i in qubit_indices:
layer[i] = new_layer
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
new_layer = max(layer[i] for i in qubit_indices) + 1
for i in qubit_indices:
layer[i] = new_layer
new_layer = max(layer[qubit_index] for qubit_index in qubit_indices) + 1
for qubit_index in qubit_indices:
layer[qubit_index] = new_layer

Comment on lines +93 to +105
empty: dict[str, Any] = {
"ig_avg_shortest_path": 0.0,
"ig_std_adjacency": 0.0,
"ig_diameter": 0,
"ig_central_dominance": 0.0,
"ig_avg_degree": 0.0,
"ig_n_maximal_cliques": 0,
"ig_clustering_coefficient": 0.0,
}

weighted_edges = circuit.interaction_graph
if not weighted_edges:
return empty
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
empty: dict[str, Any] = {
"ig_avg_shortest_path": 0.0,
"ig_std_adjacency": 0.0,
"ig_diameter": 0,
"ig_central_dominance": 0.0,
"ig_avg_degree": 0.0,
"ig_n_maximal_cliques": 0,
"ig_clustering_coefficient": 0.0,
}
weighted_edges = circuit.interaction_graph
if not weighted_edges:
return empty
weighted_edges = circuit.interaction_graph
if not weighted_edges:
return {
"ig_avg_shortest_path": 0.0,
"ig_std_adjacency": 0.0,
"ig_diameter": 0,
"ig_central_dominance": 0.0,
"ig_avg_degree": 0.0,
"ig_n_maximal_cliques": 0,
"ig_clustering_coefficient": 0.0,
}

@staticmethod
def _build_gate_dependency_graph(gate_statements: list[Gate]) -> nx.DiGraph:
"""Build a DAG where edge (i, j) means gate i must run before gate j."""
gdg: nx.DiGraph = nx.DiGraph()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I prefer to write this out (also in other places).

Suggested change
gdg: nx.DiGraph = nx.DiGraph()
gate_dependency_graph: nx.DiGraph = nx.DiGraph()

if longest_from[successor] + 1 > longest_from[node]:
longest_from[node] = longest_from[successor] + 1

return longest_to, longest_from
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why would you want them in this order (convention?)? Seems more intuitive to go from 'from' to 'to', and not from 'to' to 'from' 😅.

Comment on lines +236 to +237
mean_length = sum(path_lengths) / len(path_lengths)
variance = sum((x - mean_length) ** 2 for x in path_lengths) / len(path_lengths)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Surely there are built-in ways to determine the mean and variance of the path_lengths, right?

Comment on lines +279 to +280
for gate in gate_statements:
for qubit_index in gate.qubit_indices:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
for gate in gate_statements:
for qubit_index in gate.qubit_indices:
for gate_statement in gate_statements:
for qubit_index in gate_statement.qubit_indices:

Comment on lines +5 to +6
from opensquirrel import CircuitBuilder
from opensquirrel.circuit import Circuit
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
from opensquirrel import CircuitBuilder
from opensquirrel.circuit import Circuit
from opensquirrel import CircuitBuilder, Circuit

@SoufiTNO
Copy link
Copy Markdown
Contributor Author

Hey Chris , thank you for the review !
I ve implemented all of the requested changes. Please let me know if there are any other adjustments required before we merge it.

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.

3 participants