Skip to content

[CQT-461] Make CircuitAnalyzer metrics configurable - #703

Open
SoufiTNO wants to merge 2 commits into
developfrom
CQT-461-configurable-analyzer-metrics
Open

[CQT-461] Make CircuitAnalyzer metrics configurable#703
SoufiTNO wants to merge 2 commits into
developfrom
CQT-461-configurable-analyzer-metrics

Conversation

@SoufiTNO

Copy link
Copy Markdown
Contributor
  • Add metrics and exclude_metrics parameters to (de)select metrics
  • Add configurable per-metric timeout parameter
  • Add available_metrics() class method
  • Add documentation for the CircuitAnalyzer under a new Analysis section in the compilation passes docs
  • Add tests for metric selection and timeout
  • Update CHANGELOG

SoufiTNO added 2 commits July 21, 2026 10:43
- Add metrics and exclude_metrics parameters to (de)select metrics
- Add configurable per-metric timeout parameter
- Add available_metrics() class method
- Add documentation for the CircuitAnalyzer under a new Analysis
  section in the compilation passes docs
- Add tests for metric selection and timeout
- Update CHANGELOG
@elenbaasc elenbaasc changed the title CQT-461: Make CircuitAnalyzer metrics configurable [CQT-461] Make CircuitAnalyzer metrics configurable Jul 21, 2026
@elenbaasc
elenbaasc self-requested a review July 21, 2026 09:36

@elenbaasc elenbaasc left a comment

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.

Very nice implementation, thanks for the effort!

My main concern is about using thread pool timeout, as the calculation will/might continue in the background, instead of being killed.


if TYPE_CHECKING:
from collections.abc import Iterable

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

@@ -0,0 +1,7 @@
Analyzer passe in OpenSquirrel is used to compute statistics that describe the structure of a quantum 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
Analyzer passe in OpenSquirrel is used to compute statistics that describe the structure of a quantum circuit.
Analyzer passes are used in OpenSquirrel to compute statistics that describe the structure of a quantum circuit.

@@ -0,0 +1,7 @@
Analyzer passe in OpenSquirrel is used to compute statistics that describe the structure of a quantum circuit.
Unlike most other passes, an analyzer does not modify the circuit: it inspects it and returns a set of metrics.
These metrics can be used, for instance, to characterise a circuit prior to compilation, to compare circuits, or as input features for machine-learning models that predict compilation performance.

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
These metrics can be used, for instance, to characterise a circuit prior to compilation, to compare circuits, or as input features for machine-learning models that predict compilation performance.
These metrics can be used, for instance,
- to characterize a circuit prior to compilation,
- to compare circuits, or
- as input features for machine-learning models that predict compilation performance.

@@ -0,0 +1,113 @@
This pass computes a collection of structural metrics that describe a quantum circuit.
The metrics follow the circuit profiling approach proposed by Bandić _et al._[^1] and are grouped into four categories:

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
The metrics follow the circuit profiling approach proposed by Bandić _et al._[^1] and are grouped into four categories:
The metrics follow the circuit profiling approach proposed by Bandić _et al._ (2025)$^1$ and are grouped into four categories:

so it may continue in the background until it completes on its own.
A positive value is required for `timeout`; a non-positive value raises a `ValueError`.

[^1]:

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
[^1]:
$^1$

except FuturesTimeoutError:
metrics[name] = None
warnings.warn(
f"computation of metric '{name}' exceeded the time-out of {self.timeout} s; "

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
f"computation of metric '{name}' exceeded the time-out of {self.timeout} s; "
f"computation of metric '{name}' exceeded the time-out of {self.timeout} s: "

Comment on lines +65 to +66
requested = set(self._METRIC_REGISTRY) if metrics is None else set(metrics)
excluded = set(exclude_metrics) if exclude_metrics is not None else set()

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
requested = set(self._METRIC_REGISTRY) if metrics is None else set(metrics)
excluded = set(exclude_metrics) if exclude_metrics is not None else set()
requested = set(self._METRIC_REGISTRY) if not metrics else set(metrics)
excluded = set(exclude_metrics) if exclude_metrics else set()

Comment on lines +387 to +410
_METRIC_REGISTRY: ClassVar[dict[str, Callable[[CircuitAnalyzer], Any]]] = {
# Size
"n_qubits": _metric_n_qubits,
"n_gates": _metric_n_gates,
"n_two_qubit_gates": _metric_n_two_qubit_gates,
"two_qubit_pct": _metric_two_qubit_pct,
"depth": _metric_depth,
# Interaction graph
"ig_avg_shortest_path": _metric_ig_avg_shortest_path,
"ig_std_adjacency": _metric_ig_std_adjacency,
"ig_diameter": _metric_ig_diameter,
"ig_central_dominance": _metric_ig_central_dominance,
"ig_avg_degree": _metric_ig_avg_degree,
"ig_n_maximal_cliques": _metric_ig_n_maximal_cliques,
"ig_clustering_coefficient": _metric_ig_clustering_coefficient,
# Gate dependency graph
"gdg_critical_path_length": _metric_gdg_critical_path_length,
"gdg_path_length_mean": _metric_gdg_path_length_mean,
"gdg_path_length_std": _metric_gdg_path_length_std,
"gdg_pct_gates_in_critical_path": _metric_gdg_pct_gates_in_critical_path,
# Density
"density_score": _metric_density_score,
"idling_score": _metric_idling_score,
}

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.

Any particular reason why this (class variable) is not defined before the __init__?

Comment on lines 333 to 334
variance = statistics.pstdev(path_lengths)
return mean_length, math.sqrt(variance)

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.

Missed this one last time. Just to double check, doesn't pstdev already return the standard deviation (i.e. not the variance), so why is the square-root returned?

def _analyze_with_timeout(self) -> dict[str, Any]:
"""Compute each selected metric in a worker thread, bounded by the configured time-out."""
metrics: dict[str, Any] = {}
executor = ThreadPoolExecutor(max_workers=1)

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.

Python cannot kill a running thread. So if the timeout error is raised the calculation will/might still run in the background. You could consider using multiprocessing, to replace thread pool timeout with terminating a process.

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.

2 participants