qcw 0.1.0__py3-none-any.whl

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.
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."
@@ -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.
@@ -0,0 +1,7 @@
1
+ qcw/plugins/frameworks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ qcw/plugins/frameworks/interfaces/RoutineWrapper.py,sha256=SgndpAsHphPq4dh_zMxg_I5jkDl-GbYR4E0Ke-ZD7pI,9838
3
+ qcw/plugins/frameworks/interfaces/__init__.py,sha256=903fAWILT2L9p_tJbCH3wI-FtE1mxJVP7CPzvNhVvKw,355
4
+ qcw/plugins/frameworks/interfaces/supports_routine_test.py,sha256=LBtRe9aBt_aNo5G5OYdsO4SUwFRQvQR64VfFWrep740,1723
5
+ qcw-0.1.0.dist-info/WHEEL,sha256=uOqnPWqgFlbov4NeTCercq7cBQ2UN7xh5fiW55lOnAg,81
6
+ qcw-0.1.0.dist-info/METADATA,sha256=2W6lv1Gy3zA3zXIPw8sk0cZvG1mGFIaHuW_PBog7VHU,1364
7
+ qcw-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.26
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any