Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #97

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Develop #97

wants to merge 9 commits into from

Conversation

S-Linde
Copy link
Collaborator

@S-Linde S-Linde commented Jan 7, 2025

This pull request contains following:

  • Benchmarking API
  • Improved styling (ruff)
  • Faster github actions (uv and a precompiled cpu version of torch)

S-Linde and others added 9 commits August 12, 2024 11:16
* some first benchmarking metrics for InitialMapping and Routing + tests (still failing)

* progress, still some fixes needed.

* some metrics

* more routing metrics + isort + black

* Cleanup

* Improved performance

* First shot at benchmarking workflow

* 'fix' pytest

* addd tests and docstrings

* Happify mypy

* Initial idea qiskit MapperWrapper

* Documentation

* Docstrings

* Add tests for qiskit_utils

* bugfix

* Add tests for MaxCutQAOA generator

* Happify pylint

* Update qgym/generators/qiskit_circuit.py

Co-authored-by: Roberto Turrado Camblor <rturrado@gmail.com>

* Update qgym/generators/qiskit_circuit.py

Co-authored-by: Roberto Turrado Camblor <rturrado@gmail.com>

* Apply suggestions

* Implement suggestions

* Implement suggestions

* Happify mypy and pylint

* Implement suggestions

---------

Co-authored-by: Willem de kok <willem.dekok@tno.nl>
Co-authored-by: Roberto Turrado Camblor <rturrado@gmail.com>
* Refactor

* Add AgentMapperWrapper

* Tests for AgentMapperWrapper

* Drop support for python38

* Happify pylint

* black & isort

* Happify mypy

* Update workflow

* Test uv workflow

* Bugfix workflow

* test

* bugfix

* Dufferent mypy call

* test

* test

* Change order

* Add venv

* Reset workflow

* Update qgym/benchmarks/metrics/initial_mapping_metrics.py

Co-authored-by: Willem de Kok <102517616+Willem-de-kok@users.noreply.github.com>

* Update qgym/wrappers/initial_mapping.py

Co-authored-by: Willem de Kok <102517616+Willem-de-kok@users.noreply.github.com>

* Hapify linters

* Isort

* Update mypy workflow

* Happify mypy

* HAPPIFY MYPY

---------

Co-authored-by: Willem de Kok <102517616+Willem-de-kok@users.noreply.github.com>
* Add get_interaction_circuit

* First attempt swap adder

* Add tests for swap instertion method

* Add tests for routing wrappers

* Replace qreg_to_int with qiskit Layout

* Test uv for mypy action

* version 312

* Workflow

* UV pylint

* Bugfix

* No venv for uv

* UV for pytest

* Add python313 to compatibility

* Use CPU wheel for torch

* Remove 313 compaitibility

* Style

* Add wrapper templates

* Style

* fix test

* fix black

* fix test

* Remove breakpoint()

* Add support for python 313

* Use unsage index strategy

* fix black

* Finish routing metrics

* Add tests

* Add RoutingBenchmaker

* Linters

* Make Circuit and CircuitLike as benchmark input

* Pylint

* mypy?

* mypy?

* Happify mypy?

* mypy?

* mypy
* Add get_interaction_circuit

* First attempt swap adder

* Add tests for swap instertion method

* Add tests for routing wrappers

* Replace qreg_to_int with qiskit Layout

* Test uv for mypy action

* version 312

* Workflow

* UV pylint

* Bugfix

* No venv for uv

* UV for pytest

* Add python313 to compatibility

* Use CPU wheel for torch

* Remove 313 compaitibility

* Style

* Add wrapper templates

* Style

* fix test

* fix black

* fix test

* Remove breakpoint()

* Add support for python 313

* Use unsage index strategy

* fix black

* Finish routing metrics

* Add tests

* Add RoutingBenchmaker

* Linters

* Make Circuit and CircuitLike as benchmark input

* Pylint

* mypy?

* mypy?

* Happify mypy?

* mypy?

* mypy

* Add safe fixes

* Happify ruff

* pydocstyle

* pylint

* Fix mypy issues (numpy bug)

* Happify MYPY

* Update github actions

* Happify ruff
* First attempt swap adder

* Add tests for swap instertion method

* Remove 313 compaitibility

* Add support for python 313

* Add RoutingBenchmaker

* Linters

* Make Circuit and CircuitLike as benchmark input

* Pylint

* mypy?

* Happify mypy?

* mypy?

* mypy

* Add safe fixes

* Happify ruff

* pydocstyle

* pylint

* Fix mypy issues (numpy bug)

* Happify MYPY

* Update github actions

* Happify ruff

* format

* tst

* Update workflow

* mypy?

* mypy
@S-Linde S-Linde requested a review from Willem-de-kok January 7, 2025 14:23
@S-Linde S-Linde self-assigned this Jan 28, 2025
Copy link
Collaborator

@Willem-de-kok Willem-de-kok left a comment

Choose a reason for hiding this comment

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

Great work!

Mainly found some spelling corrections. And some questions about a couple of choices.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The supported python versions needs to be updated in the ReadMe

computing domain. It's main purpose is to easily create RL environments for the
different passes of the OpenQL framework, by simply initializing an environment class.
This abstraction of the environment allows RL developers to develop RL agents to improve
the OpenQL framework, without requiring prior knowledge of OpenQL.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we still want to explicitly mention the OpenQL framework? Maybe we should leave that out...

@@ -131,8 +135,8 @@ def reset(
Args:
seed: Seed for the random number generator, should only be provided
(optionally) on the first reset call, i.e., before any learning is done.
circuit: Optional list of tuples of ints that the interaction gates via the
qubits the gates are acting on.
interaction_circuit: Optional 2D-Array of ints that the interaction gates
Copy link
Collaborator

@Willem-de-kok Willem-de-kok Jan 29, 2025

Choose a reason for hiding this comment

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

quite hard-to-read docstring.

Comment on lines +1 to +126
@runtime_checkable
class RoutingMetric(Protocol):
"""Protocol that a metric for qubit routing should follow."""

@abstractmethod
def compute(self, input_circuit: CircuitLike, routed_circuit: CircuitLike) -> float:
"""Compute the metric for the provided `input_circuit` and `routed_circuit`."""


class InteractionRatioLoss(RoutingMetric):
"""The :class:`InteractionRatioLoss` class."""

def __init__(self, swap_penalty: SupportsInt = 3) -> None:
"""Init of :class:`InteractionRatioLoss`.

Args:
swap_penalty: Number of gates to use to decompose the SWAP gate. Since a
SWAP gate is often decomposed using 3 CNOT gates, the default is 3.
"""
self.swap_penalty = check_int(swap_penalty, "swap_penalty", l_bound=1)

def compute(self, input_circuit: CircuitLike, routed_circuit: CircuitLike) -> float:
"""Method to calculate the ratio of the input and output circuit.

The ratio loss is defined by the number of 2 qubit gates in the `output_circuit`
devided by the number of 2 qubit gates in the `input_circut`. A score of 1 is
thus a perfect score and the lowest value that can be returned.

Args:
input_circuit: Input circuit before routing was performed.
routed_circuit: Routed version of the input circuit.

Returns:
The routing solution quality ratio.
"""
input_n_iteractions = self.get_num_interactions(input_circuit)
routed_n_iteractions = self.get_num_interactions(routed_circuit)
try:
return float(routed_n_iteractions / input_n_iteractions)
except ZeroDivisionError:
if not routed_n_iteractions:
return 1.0
return float("inf")

def get_num_interactions(self, circuit: CircuitLike) -> int:
"""Get the number of interactions from the `circuit`.

Args:
circuit: Circuit to count the number of interactions from.

Returns:
Number of 2 qubit interactions, where each SWAP gate is counted
`swap_penalty` times.
"""
circuit = Circuit(circuit)
return sum(
self.swap_penalty if gate.name == "swap" else 1
for gate in circuit.dag.two_qubit_ops()
)


class RoutingBenchmarker:
"""The :class:`RoutingBenchmarker` class."""

def __init__(
self,
generator: Iterator[CircuitLike],
metrics: Iterable[RoutingMetric],
) -> None:
"""Init of the :class:`RoutingBenchmarker` class.

Args:
generator: Circuit generator. Currently only the ``MaxCutQAOAGenerator`` is
supported.
metrics: Metrics to compute.
"""
self.generator = generator
self.metrics = tuple(metrics)

def run(self, router: Router, max_iter: int = 1000) -> BenchmarkResult:
"""Run the benchmark.

Args:
router: Router to benchmark.
max_iter: Maximum number of iterations to benchmark.

Returns:
:class:`~qgym.benchmarks.metrics.BenchmarkResult` containing the results
from the benchmark.
"""
results: list[deque[float]] = [deque() for _ in self.metrics]
for i, circuit in enumerate(self.generator, start=1):
routed_circuit = router.compute_routing(circuit)
for metric, result_que in zip(self.metrics, results):
result_que.append(metric.compute(circuit, routed_circuit))

if i >= max_iter:
break

return BenchmarkResult(results)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like the with a RoutingMetric class and any RoutingMetric inheriting from that class and the RoutingBenchmarker outputting based on a number of RoutingMetrics.

@@ -32,9 +36,11 @@ def __init__(self) -> None:
self._decoding_dct: dict[int, str] = {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is this decoding_dct and not decoding_dict? Same holds for encoding_dct furtheron in the file.

"""Insert the provided swap gates in the quantum circuit.

Args:
swaps_inserted: Swap gated to insert. Iterable of tuples (g_idx, q1, q2).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
swaps_inserted: Swap gated to insert. Iterable of tuples (g_idx, q1, q2).
swaps_inserted: Swap gates to insert. Iterable of tuples (g_idx, q1, q2).

):
"""Wrap a trained stable baselines 3 agent.

The agent shoul inherit from
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
The agent shoul inherit from
The agent should inherit from

Args:
agent: agent trained on the initial mapping environment.
env: environment the agent was trained on.
max_steps: maximum number steps the `agent` can take to compute the mapping.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
max_steps: maximum number steps the `agent` can take to compute the mapping.
max_steps: maximum number of steps the `agent` can take to compute the mapping.

"""Wrap any qiskit mapper (Layout algorithm).

This wrapper ensures that the mapper becomes compatible with the qgym framework.
This class wraps the qiskit mapper, such that it is compatible with the qgym
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
This class wraps the qiskit mapper, such that it is compatible with the qgym
This class wraps the qiskit mapper, such that it is compatible with the qgym

qiskit_mapper = SabreLayout(coupling_map)
mapper = QiskitMapperWrapper(qiskit_mapper)
mapper.compute_mapping(circuit)

Copy link
Collaborator

Choose a reason for hiding this comment

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

no assertions here?

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