Skip to content

Commit 1be0dbe

Browse files
committed
Refactor all devices and implements IQM device.
1 parent 9d402f0 commit 1be0dbe

File tree

4 files changed

+98
-193
lines changed

4 files changed

+98
-193
lines changed

pennylane_scaleway/aer_device.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import numpy as np
15-
import warnings
1614

1715
from dataclasses import fields
16+
import numpy as np
1817
from tenacity import retry, stop_after_attempt, stop_after_delay
1918
from typing import Callable, Iterable, List, Sequence, Tuple
19+
import warnings
2020

2121
import pennylane as qml
2222
from pennylane.devices import ExecutionConfig
@@ -45,7 +45,7 @@
4545
@single_tape_support # add support for device.execute(tape) in addition to device.execute((tape,))
4646
class AerDevice(ScalewayDevice):
4747
"""
48-
This is Scaleway's device to run Pennylane's circuits on Aer emulators.
48+
Scaleway's device to run Pennylane circuits on Aer emulators.
4949
5050
This device:
5151
* Supports any operations with explicit PennyLane to Qiskit gate conversions defined in the plugin.
@@ -120,7 +120,6 @@ def circuit():
120120

121121
if isinstance(seed, int):
122122
kwargs.update({"seed_simulator": seed})
123-
# self._rng = np.random.default_rng(seed)
124123

125124
super().__init__(wires=wires, kwargs=kwargs, shots=shots)
126125

pennylane_scaleway/aqt_device.py

Lines changed: 7 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,19 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import numpy as np
15-
import warnings
1614

1715
from inspect import signature
18-
from typing import Callable, List, Sequence, Tuple, Union
19-
from tenacity import retry, stop_after_attempt, stop_after_delay
16+
from typing import Callable, Sequence, Tuple
17+
import warnings
2018

2119
from pennylane.devices import ExecutionConfig
2220
from pennylane.devices.modifiers import simulator_tracking, single_tape_support
23-
24-
from pennylane.tape import QuantumScriptOrBatch, QuantumScript, QuantumTape
21+
from pennylane.tape import QuantumTape
2522
from pennylane.transforms import transform
2623
from pennylane.transforms.core import TransformProgram
2724

28-
from qiskit.result import Result
29-
3025
from qiskit_scaleway.backends import AqtBackend, AerBackend
3126

32-
from pennylane_scaleway.utils import (
33-
circuit_to_qiskit,
34-
)
3527
from pennylane_scaleway.scw_device import ScalewayDevice
3628

3729

@@ -69,14 +61,13 @@ def processing_fn(results: Sequence) -> any:
6961
@single_tape_support # add support for device.execute(tape) in addition to device.execute((tape,))
7062
class AqtDevice(ScalewayDevice):
7163
"""
72-
This is Scaleway's AQT device.
73-
It allows to run quantum circuits on Scaleway's AQT emulation backends.
64+
Scaleway's device to run Pennylane circuits on AQT platforms.
7465
7566
This device:
76-
* Follows the same constraints as AerDevice, as it uses qiskit as a common interface with AQT emulators.
77-
* Has 12 wires available.
67+
* Has 12 qubits available.
7868
* Supports up to 2000 shots maximum.
79-
* Does not support more than 2000 operations AFTER decomposition, due to hardware limitation. This translates to roughly 12 wires and ~20 layers deep for a pennylane circuit.
69+
* Does not support more than 2000 operations AFTER decomposition, due to hardware limitation. This translates to roughly 12 qubits and ~20 layers deep for a pennylane circuit.
70+
* Follows the same constraints as AerDevice, as it uses qiskit as a common interface with AQT emulators.
8071
"""
8172

8273
name = "scaleway.aqt"
@@ -156,11 +147,6 @@ def circuit():
156147
"""
157148

158149
super().__init__(wires=wires, kwargs=kwargs, shots=shots, seed=seed)
159-
160-
self._default_shots = None
161-
if isinstance(shots, int):
162-
self._default_shots = shots
163-
164150
self._handle_kwargs(**kwargs)
165151

166152
def _handle_kwargs(self, **kwargs):
@@ -196,77 +182,3 @@ def preprocess(
196182
limit_aqt_shots, default_shots=self._default_shots
197183
)
198184
return transform_program, config
199-
200-
def execute(
201-
self,
202-
circuits: QuantumScriptOrBatch,
203-
execution_config: ExecutionConfig | None = None,
204-
) -> List:
205-
if not self._session_id:
206-
raise RuntimeError(
207-
"No active session. Please instanciate the device using a context manager, or call start() first. You can also attach to an existing deduplication_id."
208-
)
209-
210-
if isinstance(circuits, QuantumScript):
211-
circuits = [circuits]
212-
213-
qiskit_circuits = []
214-
for circuit in circuits:
215-
qiskit_circuits.append(
216-
circuit_to_qiskit(
217-
circuit, self.num_wires, diagonalize=True, measure=True
218-
)
219-
)
220-
221-
shots = self._default_shots
222-
if circuits[0].shots and circuits[0].shots.total_shots:
223-
shots = circuits[0].shots.total_shots
224-
225-
@retry(stop=stop_after_attempt(3) | stop_after_delay(3 * 60), reraise=True)
226-
def run() -> Union[Result, List[Result]]:
227-
return self._platform.run(
228-
qiskit_circuits,
229-
session_id=self._session_id,
230-
shots=shots,
231-
**self._run_options,
232-
).result()
233-
234-
results = run()
235-
if isinstance(results, Result):
236-
results = [results]
237-
238-
counts = []
239-
for result in results:
240-
if isinstance(result.get_counts(), dict):
241-
counts.append(result.get_counts())
242-
else:
243-
counts.extend([count for count in result.get_counts()])
244-
245-
all_results = []
246-
for original_circuit, qcirc, count in zip(circuits, qiskit_circuits, counts):
247-
# Reconstruct the list of samples from the counts dictionary
248-
samples_list = []
249-
for key, value in count.items():
250-
samples_list.extend([key] * value)
251-
252-
if not samples_list:
253-
# Handle case with no samples (e.g., 0 shots)
254-
num_clbits = len(qcirc.clbits)
255-
samples = np.empty((0, num_clbits), dtype=int)
256-
else:
257-
# Convert bitstrings to numpy array of ints, reversing for convention
258-
samples = np.vstack(
259-
[np.array([int(i) for i in s[::-1]]) for s in samples_list]
260-
)
261-
262-
# Process the samples according to the measurements in the original circuit
263-
res = [
264-
mp.process_samples(samples, wire_order=self.wires)
265-
for mp in original_circuit.measurements
266-
]
267-
268-
single_measurement = len(original_circuit.measurements) == 1
269-
res_tuple = res[0] if single_measurement else tuple(res)
270-
all_results.append(res_tuple)
271-
272-
return all_results

pennylane_scaleway/iqm_device.py

Lines changed: 2 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,24 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import numpy as np
15-
import warnings
1614

1715
from inspect import signature
18-
from typing import List, Union
19-
from tenacity import retry, stop_after_attempt, stop_after_delay
16+
import warnings
2017

2118
from pennylane.devices import ExecutionConfig
2219
from pennylane.devices.modifiers import simulator_tracking, single_tape_support
23-
from pennylane.tape import QuantumScriptOrBatch, QuantumScript
2420
from pennylane.transforms.core import TransformProgram
2521

26-
from qiskit.result import Result
27-
2822
from qiskit_scaleway.backends import IqmBackend
2923

30-
from pennylane_scaleway.utils import (
31-
circuit_to_qiskit,
32-
)
3324
from pennylane_scaleway.scw_device import ScalewayDevice
3425

3526

3627
@simulator_tracking # update device.tracker with some relevant information
3728
@single_tape_support # add support for device.execute(tape) in addition to device.execute((tape,))
3829
class IqmDevice(ScalewayDevice):
3930
"""
40-
This is Scaleway's IQM device.
31+
Scaleway's device to run Pennylane circuits on IQM platforms.
4132
"""
4233

4334
name = "scaleway.iqm"
@@ -83,77 +74,3 @@ def preprocess(
8374
) -> tuple[TransformProgram, ExecutionConfig]:
8475
transform_program, config = super().preprocess(execution_config)
8576
return transform_program, config
86-
87-
def execute(
88-
self,
89-
circuits: QuantumScriptOrBatch,
90-
execution_config: ExecutionConfig | None = None,
91-
) -> List:
92-
if not self._session_id:
93-
raise RuntimeError(
94-
"No active session. Please instanciate the device using a context manager, or call start() first. You can also attach to an existing deduplication_id."
95-
)
96-
97-
if isinstance(circuits, QuantumScript):
98-
circuits = [circuits]
99-
100-
qiskit_circuits = []
101-
for circuit in circuits:
102-
qiskit_circuits.append(
103-
circuit_to_qiskit(
104-
circuit, self.num_wires, diagonalize=True, measure=True
105-
)
106-
)
107-
108-
shots = self._default_shots
109-
if circuits[0].shots and circuits[0].shots.total_shots:
110-
shots = circuits[0].shots.total_shots
111-
112-
@retry(stop=stop_after_attempt(3) | stop_after_delay(3 * 60), reraise=True)
113-
def run() -> Union[Result, List[Result]]:
114-
return self._platform.run(
115-
qiskit_circuits,
116-
session_id=self._session_id,
117-
shots=shots,
118-
**self._run_options,
119-
).result()
120-
121-
results = run()
122-
if isinstance(results, Result):
123-
results = [results]
124-
125-
counts = []
126-
for result in results:
127-
if isinstance(result.get_counts(), dict):
128-
counts.append(result.get_counts())
129-
else:
130-
counts.extend([count for count in result.get_counts()])
131-
132-
all_results = []
133-
for original_circuit, qcirc, count in zip(circuits, qiskit_circuits, counts):
134-
# Reconstruct the list of samples from the counts dictionary
135-
samples_list = []
136-
for key, value in count.items():
137-
samples_list.extend([key] * value)
138-
139-
if not samples_list:
140-
# Handle case with no samples (e.g., 0 shots)
141-
num_clbits = len(qcirc.clbits)
142-
samples = np.empty((0, num_clbits), dtype=int)
143-
else:
144-
# Convert bitstrings to numpy array of ints, reversing for convention
145-
samples = np.vstack(
146-
[np.array([int(i) for i in s[::-1]]) for s in samples_list]
147-
)
148-
149-
# Process the samples according to the measurements in the original circuit
150-
res = [
151-
mp.process_samples(samples, wire_order=self.wires)
152-
for mp in original_circuit.measurements
153-
]
154-
155-
single_measurement = len(original_circuit.measurements) == 1
156-
res_tuple = res[0] if single_measurement else tuple(res)
157-
all_results.append(res_tuple)
158-
159-
return all_results

0 commit comments

Comments
 (0)