qcw 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qcw-0.1.0/PKG-INFO +24 -0
- qcw-0.1.0/README.md +5 -0
- qcw-0.1.0/pyproject.toml +34 -0
- qcw-0.1.0/src/qcw/plugins/frameworks/__init__.py +0 -0
- qcw-0.1.0/src/qcw/plugins/frameworks/interfaces/RoutineWrapper.py +299 -0
- qcw-0.1.0/src/qcw/plugins/frameworks/interfaces/__init__.py +21 -0
- qcw-0.1.0/src/qcw/plugins/frameworks/interfaces/supports_routine_test.py +39 -0
qcw-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: qcw
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Universal and consistant interface to access information from quantum circuits implemented using different framework
|
|
5
|
+
Author: Adrien Suau
|
|
6
|
+
Author-email: Adrien Suau <12374487+nelimee@users.noreply.github.com>
|
|
7
|
+
Requires-Dist: qcw-qiskit ; extra == 'all'
|
|
8
|
+
Requires-Dist: qcw-openqasm2 ; extra == 'all'
|
|
9
|
+
Requires-Dist: qcw-myqlm ; extra == 'all'
|
|
10
|
+
Requires-Dist: qcw-myqlm ; extra == 'myqlm'
|
|
11
|
+
Requires-Dist: qcw-openqasm2 ; extra == 'openqasm2'
|
|
12
|
+
Requires-Dist: qcw-qiskit ; extra == 'qiskit'
|
|
13
|
+
Requires-Python: >=3.10, <3.12
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Provides-Extra: myqlm
|
|
16
|
+
Provides-Extra: openqasm2
|
|
17
|
+
Provides-Extra: qiskit
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
`qcw` stands for **q**uantum **c**ircuit **w**rapper and aims at providing a unique interface to access quantum circuit information from different frameworks.
|
|
21
|
+
|
|
22
|
+
This package has been written to improve the modularity of [`qprof`](https://github.com/nelimee/qprof), a tool I wrote during my PhD at CERFACS. It is current quite outdated, but should be installable with old Python versions (see [`pyproject.toml`](./pyproject.toml) for compatibility).
|
|
23
|
+
|
|
24
|
+
The main package is located in [`src/qcw`](./src/qcw/) and defines the interface that children `qcw-*` namespace packages should implement as well as wrappers to automatically discover the `qcw-*` packages installed.
|
qcw-0.1.0/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
`qcw` stands for **q**uantum **c**ircuit **w**rapper and aims at providing a unique interface to access quantum circuit information from different frameworks.
|
|
2
|
+
|
|
3
|
+
This package has been written to improve the modularity of [`qprof`](https://github.com/nelimee/qprof), a tool I wrote during my PhD at CERFACS. It is current quite outdated, but should be installable with old Python versions (see [`pyproject.toml`](./pyproject.toml) for compatibility).
|
|
4
|
+
|
|
5
|
+
The main package is located in [`src/qcw`](./src/qcw/) and defines the interface that children `qcw-*` namespace packages should implement as well as wrappers to automatically discover the `qcw-*` packages installed.
|
qcw-0.1.0/pyproject.toml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "qcw"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Universal and consistant interface to access information from quantum circuits implemented using different framework"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{ name = "Adrien Suau", email = "12374487+nelimee@users.noreply.github.com" }]
|
|
7
|
+
requires-python = ">=3.10,<3.12"
|
|
8
|
+
dependencies = []
|
|
9
|
+
|
|
10
|
+
[project.optional-dependencies]
|
|
11
|
+
qiskit = ["qcw-qiskit"]
|
|
12
|
+
openqasm2 = ["qcw-openqasm2"]
|
|
13
|
+
myqlm = ["qcw-myqlm"]
|
|
14
|
+
all = ["qcw-qiskit", "qcw-openqasm2", "qcw-myqlm"]
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["uv_build>=0.11.26,<0.12.0"]
|
|
18
|
+
build-backend = "uv_build"
|
|
19
|
+
|
|
20
|
+
[tool.uv.sources]
|
|
21
|
+
qcw = { workspace = true }
|
|
22
|
+
qcw-myqlm = { workspace = true }
|
|
23
|
+
qcw-openqasm2 = { workspace = true }
|
|
24
|
+
qcw-qiskit = { workspace = true }
|
|
25
|
+
|
|
26
|
+
[tool.uv.workspace]
|
|
27
|
+
members = ["plugins/*"]
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
lint = ["ruff==0.15.20", "ty==0.0.56"]
|
|
31
|
+
dev = ["ipython", "qcw[all]", { include-group = "lint" }]
|
|
32
|
+
|
|
33
|
+
[tool.uv.build-backend]
|
|
34
|
+
module-name = "qcw.plugins.frameworks"
|
|
File without changes
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Declaration of the interface for the main RoutineWrapper class."""
|
|
2
|
+
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# Copyright: CERFACS, LIRMM, Total S.A. - the quantum computing team (March
|
|
6
|
+
# 2021).
|
|
7
|
+
# Contributor: Adrien Suau (adrien.suau@cerfacs.fr)
|
|
8
|
+
#
|
|
9
|
+
# This program is free software: you can redistribute it and/or modify it under
|
|
10
|
+
# the terms of the GNU Lesser General Public License as published by the Free
|
|
11
|
+
# Software Foundation, either version 3 of the License, or (at your discretion)
|
|
12
|
+
# any later version.
|
|
13
|
+
#
|
|
14
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
15
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
16
|
+
# FOR A PARTICULAR PURPOSE.
|
|
17
|
+
#
|
|
18
|
+
# See the GNU Lesser General Public License for more details. You should have
|
|
19
|
+
# received a copy of the GNU Lesser General Public License along with this
|
|
20
|
+
# program. If not, see https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
21
|
+
#
|
|
22
|
+
# =============================================================================
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import abc
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
from typing import Any, Iterable, Iterator
|
|
28
|
+
|
|
29
|
+
from qcw.exceptions import UnsupportedEqualityTesting
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Register:
|
|
34
|
+
"""Base class representing a generic register.
|
|
35
|
+
|
|
36
|
+
A register is characterised by a name and its size.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __hash__(self) -> int:
|
|
40
|
+
"""Return a hash of self."""
|
|
41
|
+
return hash((self.name, self.size))
|
|
42
|
+
|
|
43
|
+
name: str
|
|
44
|
+
size: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class QuantumRegister(Register):
|
|
49
|
+
"""Representation of a quantum register.
|
|
50
|
+
|
|
51
|
+
A quantum register is characterised by a name and its size.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __hash__(self) -> int:
|
|
55
|
+
"""Return a hash of self."""
|
|
56
|
+
return super().__hash__()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class ClassicalRegister(Register):
|
|
61
|
+
"""Representation of a classical register.
|
|
62
|
+
|
|
63
|
+
A classical register is characterised by a name and its size.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __hash__(self) -> int:
|
|
67
|
+
"""Return a hash of self."""
|
|
68
|
+
return super().__hash__()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class Bit:
|
|
73
|
+
"""Base class representing a generic bit.
|
|
74
|
+
|
|
75
|
+
A generic bit is characterised by the register it belongs to and its index
|
|
76
|
+
in this register.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __hash__(self) -> int:
|
|
80
|
+
"""Return a hash of self."""
|
|
81
|
+
return hash((self.reg, self.index))
|
|
82
|
+
|
|
83
|
+
def __repr__(self) -> str:
|
|
84
|
+
"""Return a textual representation of a bit."""
|
|
85
|
+
return f"{self.reg.name}[{self.reg.size}]({self.index})"
|
|
86
|
+
|
|
87
|
+
def __str__(self) -> str:
|
|
88
|
+
"""Return a user-readable representation of a bit."""
|
|
89
|
+
return self.__repr__()
|
|
90
|
+
|
|
91
|
+
reg: Register
|
|
92
|
+
index: int
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class Qubit(Bit):
|
|
97
|
+
"""Base class representing a quantum bit.
|
|
98
|
+
|
|
99
|
+
A quantum bit is characterised by the register it belongs to and its index
|
|
100
|
+
in this register.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __hash__(self) -> int:
|
|
104
|
+
"""Return a hash of self."""
|
|
105
|
+
return super().__hash__()
|
|
106
|
+
|
|
107
|
+
def __repr__(self) -> str:
|
|
108
|
+
"""Return a textual representation of a qubit."""
|
|
109
|
+
return super().__repr__()
|
|
110
|
+
|
|
111
|
+
def __str__(self) -> str:
|
|
112
|
+
"""Return a user-readable representation of a qubit."""
|
|
113
|
+
return super().__str__()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass
|
|
117
|
+
class Clbit(Bit):
|
|
118
|
+
"""Base class representing a classical bit.
|
|
119
|
+
|
|
120
|
+
A classical bit is characterised by the register it belongs to and its
|
|
121
|
+
index in this register.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def __hash__(self) -> int:
|
|
125
|
+
"""Return a hash of self."""
|
|
126
|
+
return super().__hash__()
|
|
127
|
+
|
|
128
|
+
def __repr__(self) -> str:
|
|
129
|
+
"""Return a user-readable representation of a classical bit."""
|
|
130
|
+
return super().__repr__()
|
|
131
|
+
|
|
132
|
+
def __str__(self) -> str:
|
|
133
|
+
"""Return a user-readable representation of a classical bit."""
|
|
134
|
+
return super().__str__()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class RoutineWrapper(abc.ABC, Iterable["RoutineWrapper"]):
|
|
138
|
+
"""Wrapper around the framework-specific routine type.
|
|
139
|
+
|
|
140
|
+
This class should be subclassed and implemented for each framework. It
|
|
141
|
+
helps providing a unique API for all the frameworks that can then be used
|
|
142
|
+
by other libraries.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
_main_bit_suffix: str = "main"
|
|
146
|
+
_hardware_bit_suffix: str = "hardware"
|
|
147
|
+
|
|
148
|
+
@abc.abstractmethod
|
|
149
|
+
def __init__(self, routine: Any, parent: Any | None):
|
|
150
|
+
"""Initialise the wrapper with the given routine.
|
|
151
|
+
|
|
152
|
+
:param routine: a framework-specific routine that will be wrapped.
|
|
153
|
+
"""
|
|
154
|
+
self._routine = routine
|
|
155
|
+
self._parent = parent
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def routine(self) -> Any:
|
|
159
|
+
"""Return the framework-specific routine given at initialisation."""
|
|
160
|
+
return self._routine
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def parent(self) -> Any | None:
|
|
164
|
+
"""Return the framework-specific parent given at initialisation."""
|
|
165
|
+
return self._parent
|
|
166
|
+
|
|
167
|
+
@abc.abstractmethod
|
|
168
|
+
def __iter__(self) -> Iterator[RoutineWrapper]:
|
|
169
|
+
"""Magic Python method to make the RoutineWrapper object iterable.
|
|
170
|
+
|
|
171
|
+
:return: an iterable over all the subroutines called by the wrapped
|
|
172
|
+
routine. The subroutines should be wrapped by the RoutineWrapper.
|
|
173
|
+
"""
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def ops(self) -> tuple[RoutineWrapper, ...]:
|
|
178
|
+
"""Return a list of the subroutines called by self."""
|
|
179
|
+
return tuple(self)
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
@abc.abstractmethod
|
|
183
|
+
def is_base(self) -> bool:
|
|
184
|
+
"""Check if the wrapped routine is a "base" routine.
|
|
185
|
+
|
|
186
|
+
Base routines are routines that are considered as primitive, i.e. that
|
|
187
|
+
do not call any subroutine.
|
|
188
|
+
The concept of base routine is essential for qprof as only base
|
|
189
|
+
routines should have an entry in the "gate_times" dictionary provided
|
|
190
|
+
to the "profile" method and base routines are used to stop the
|
|
191
|
+
recursion into the call-tree.
|
|
192
|
+
|
|
193
|
+
:return: True if the wrapped routine is considered as a "base" routine,
|
|
194
|
+
else False.
|
|
195
|
+
"""
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
@abc.abstractmethod
|
|
200
|
+
def name(self) -> str:
|
|
201
|
+
"""Return the name of the wrapped subroutine."""
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
def __repr__(self) -> str:
|
|
205
|
+
return f"{type(self).__name__}('{self.name}')"
|
|
206
|
+
|
|
207
|
+
def __hash__(self) -> int:
|
|
208
|
+
"""Compute the hash of the wrapped routine.
|
|
209
|
+
|
|
210
|
+
__hash__ and __eq__ methods are used by qprof to cache routines and
|
|
211
|
+
re-use already computed data. This optimisation gives impressive
|
|
212
|
+
results on some circuits and is expected to improve the runtime of
|
|
213
|
+
qprof on nearly all quantum circuits, because routines are most of the
|
|
214
|
+
time re-used.
|
|
215
|
+
|
|
216
|
+
By default the hash function will return the id of the stored routine,
|
|
217
|
+
effectively disabling the caching mechanism. It is up to the plugin
|
|
218
|
+
developers to implement an efficient hash for the framework-specific
|
|
219
|
+
routine type if they want such a caching mechanism to be used.
|
|
220
|
+
|
|
221
|
+
:return: int representing a hash of the wrapper routine.
|
|
222
|
+
"""
|
|
223
|
+
return id(self._routine)
|
|
224
|
+
|
|
225
|
+
def __eq__(self, other) -> bool:
|
|
226
|
+
"""Equality testing for wrapped routines.
|
|
227
|
+
|
|
228
|
+
__hash__ and __eq__ methods are used by qprof to cache routines and
|
|
229
|
+
re-use already computed data. This optimisation gives impressive
|
|
230
|
+
results on some circuits and is expected to improve the runtime of
|
|
231
|
+
qprof on nearly all quantum circuits, because routines are most of the
|
|
232
|
+
time re-used.
|
|
233
|
+
|
|
234
|
+
Two routines should be considered equal if and only if they generate
|
|
235
|
+
exactly the same circuit.
|
|
236
|
+
|
|
237
|
+
Comparing the generated circuits might be a costly task, but other
|
|
238
|
+
methods can be used. For example, routines with the same name and the
|
|
239
|
+
same parameters might be considered as equal (may be
|
|
240
|
+
framework-dependent).
|
|
241
|
+
|
|
242
|
+
By default the equality test will test for ids equality of the stored
|
|
243
|
+
routines, effectively disabling the caching mechanism. It is up to the
|
|
244
|
+
plugin developers to implement an efficient equality test for the
|
|
245
|
+
framework-specific routine type if they want such a caching mechanism
|
|
246
|
+
to be used.
|
|
247
|
+
|
|
248
|
+
:param other: instance of RoutineWrapper to test for equality with
|
|
249
|
+
self.
|
|
250
|
+
:return: True if self and other are equal (i.e. generate the exact same
|
|
251
|
+
quantum circuit) else False.
|
|
252
|
+
"""
|
|
253
|
+
if not isinstance(other, RoutineWrapper):
|
|
254
|
+
raise UnsupportedEqualityTesting(type(self), type(other))
|
|
255
|
+
return id(self._routine) == id(other._routine)
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
@abc.abstractmethod
|
|
259
|
+
def qubits(self) -> Iterable[Qubit]:
|
|
260
|
+
"""Return the qubits self is applied on."""
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
@abc.abstractmethod
|
|
265
|
+
def clbits(self) -> Iterable[Clbit]:
|
|
266
|
+
"""Return the classical bits self is applied on."""
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def bits(self) -> Iterable[Bit]:
|
|
271
|
+
"""Return the bits self is applied on."""
|
|
272
|
+
yield from self.qubits
|
|
273
|
+
yield from self.clbits
|
|
274
|
+
|
|
275
|
+
@abc.abstractmethod
|
|
276
|
+
def get_parent_bit(self, bit: Bit) -> Bit:
|
|
277
|
+
"""Return the bit of the calling subroutine bound to the given bit.
|
|
278
|
+
|
|
279
|
+
:param bit: a bit this routine is called on. "bit in self.bits" should
|
|
280
|
+
be True.
|
|
281
|
+
:returns: the corresponding bit in the calling subroutine.
|
|
282
|
+
"""
|
|
283
|
+
pass
|
|
284
|
+
|
|
285
|
+
def get_bit_global_index(self, bit: Bit) -> int:
|
|
286
|
+
"""Return the global index of the given bit.
|
|
287
|
+
|
|
288
|
+
The global index is the index of the main routine bit bound to the
|
|
289
|
+
given bit.
|
|
290
|
+
|
|
291
|
+
:param bit: one of the bits self is applied on.
|
|
292
|
+
:returns: the global index of the given bit.
|
|
293
|
+
"""
|
|
294
|
+
current_bit: Bit = bit
|
|
295
|
+
current_routine: RoutineWrapper = self
|
|
296
|
+
while current_routine.parent is not None:
|
|
297
|
+
current_bit = current_routine.get_parent_bit(current_bit)
|
|
298
|
+
current_routine = current_routine.parent
|
|
299
|
+
return current_bit.index
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .RoutineWrapper import (
|
|
2
|
+
Bit,
|
|
3
|
+
ClassicalRegister,
|
|
4
|
+
Clbit,
|
|
5
|
+
QuantumRegister,
|
|
6
|
+
Qubit,
|
|
7
|
+
Register,
|
|
8
|
+
RoutineWrapper,
|
|
9
|
+
)
|
|
10
|
+
from .supports_routine_test import supports_routine
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"RoutineWrapper",
|
|
14
|
+
"Bit",
|
|
15
|
+
"Qubit",
|
|
16
|
+
"Clbit",
|
|
17
|
+
"Register",
|
|
18
|
+
"QuantumRegister",
|
|
19
|
+
"ClassicalRegister",
|
|
20
|
+
"supports_routine",
|
|
21
|
+
]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Main interface to test if the plugin supports a given routine type.
|
|
2
|
+
|
|
3
|
+
This module declares the interface that should be implemented by all the qcw
|
|
4
|
+
plugins in order to check if a given routine is supported by the plugin.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# =============================================================================
|
|
8
|
+
#
|
|
9
|
+
# Copyright: CERFACS, LIRMM, Total S.A. - the quantum computing team (March
|
|
10
|
+
# 2021).
|
|
11
|
+
# Contributor: Adrien Suau (adrien.suau@cerfacs.fr)
|
|
12
|
+
#
|
|
13
|
+
# This program is free software: you can redistribute it and/or modify it under
|
|
14
|
+
# the terms of the GNU Lesser General Public License as published by the Free
|
|
15
|
+
# Software Foundation, either version 3 of the License, or (at your discretion)
|
|
16
|
+
# any later version.
|
|
17
|
+
#
|
|
18
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
19
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
20
|
+
# FOR A PARTICULAR PURPOSE.
|
|
21
|
+
#
|
|
22
|
+
# See the GNU Lesser General Public License for more details. You should have
|
|
23
|
+
# received a copy of the GNU Lesser General Public License along with this
|
|
24
|
+
# program. If not, see https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
25
|
+
#
|
|
26
|
+
# =============================================================================
|
|
27
|
+
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def supports_routine(routine: Any) -> tuple[bool, str | None]:
|
|
32
|
+
"""Check plugin compatibility with the given routine.
|
|
33
|
+
|
|
34
|
+
:param routine: framework-specific routine instance.
|
|
35
|
+
:return: True if the plugin supports this type of routine, else False. If
|
|
36
|
+
False, the second entry in the returned tuple is not None and explains
|
|
37
|
+
why this plugin does not support the given routine type.
|
|
38
|
+
"""
|
|
39
|
+
return False, "'qprof_interface' module does not support any routine type."
|