qiskit-state-evolution-recorder 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.
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Dawid Ciepiela
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.1
2
+ Name: qiskit_state_evolution_recorder
3
+ Version: 0.1.0
4
+ Summary: Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits.
5
+ Home-page: https://github.com/sarumaj/qiskit-state-evolution-recorder
6
+ Author: Dawid Ciepiela
7
+ Author-email: Dawid Ciepiela <71898979+sarumaj@users.noreply.github.com>
8
+ License: BSD-3-Clause
9
+ Project-URL: Homepage, https://github.com/sarumaj/qiskit-state-evolution-recorder
10
+ Requires-Python: >=3.6, <3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: matplotlib==3.9.2
14
+ Requires-Dist: matplotlib-inline==0.1.7
15
+ Requires-Dist: numpy==2.1.1
16
+ Requires-Dist: qiskit==1.2.4
17
+ Requires-Dist: qiskit-aer==0.15.1
18
+ Requires-Dist: pillow==11.0.0
19
+
20
+ # qiskit-state-evolution-recorder
21
+
22
+ Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits.
23
+
24
+ ## Usage
25
+
26
+ ```python
27
+ from qiskit.circuit import QuantumCircuit
28
+ from qiskit_state_evolution_recorder import StateEvolutionRecorder
29
+
30
+ qc = QuantumCircuit(4)
31
+ qc.h(range(4))
32
+ qc.cx(range(3), 3)
33
+ qc.h(range(4))
34
+ qc.measure_all()
35
+
36
+ recorder = StateEvolutionRecorder(qc, figsize=(12, 8), num_cols=4, style={'name': 'bw'})
37
+ recorder.evolve(120)
38
+ recorder.record("quantum_circuit.mp4", fps=30)
39
+ ```
@@ -0,0 +1,20 @@
1
+ # qiskit-state-evolution-recorder
2
+
3
+ Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits.
4
+
5
+ ## Usage
6
+
7
+ ```python
8
+ from qiskit.circuit import QuantumCircuit
9
+ from qiskit_state_evolution_recorder import StateEvolutionRecorder
10
+
11
+ qc = QuantumCircuit(4)
12
+ qc.h(range(4))
13
+ qc.cx(range(3), 3)
14
+ qc.h(range(4))
15
+ qc.measure_all()
16
+
17
+ recorder = StateEvolutionRecorder(qc, figsize=(12, 8), num_cols=4, style={'name': 'bw'})
18
+ recorder.evolve(120)
19
+ recorder.record("quantum_circuit.mp4", fps=30)
20
+ ```
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "qiskit_state_evolution_recorder"
3
+ version = "0.1.0"
4
+ description = "Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits."
5
+ readme = "README.md"
6
+ license = {text = "BSD-3-Clause"}
7
+ authors = [
8
+ { name = "Dawid Ciepiela", email = "71898979+sarumaj@users.noreply.github.com" }
9
+ ]
10
+ dependencies = [
11
+ "matplotlib==3.9.2",
12
+ "matplotlib-inline==0.1.7",
13
+ "numpy==2.1.1",
14
+ "qiskit==1.2.4",
15
+ "qiskit-aer==0.15.1",
16
+ "pillow==11.0.0"
17
+ ]
18
+ requires-python = ">=3.6, <3.12"
19
+
20
+ [project.urls]
21
+ "Homepage" = "https://github.com/sarumaj/qiskit-state-evolution-recorder"
@@ -0,0 +1,497 @@
1
+ from qiskit.circuit import InstructionSet, QuantumCircuit
2
+ from qiskit.quantum_info import Statevector
3
+ from qiskit.visualization import plot_bloch_vector
4
+ from qiskit.visualization.state_visualization import _bloch_multivector_data
5
+
6
+ from numpy import linspace, frombuffer, uint8, ndarray, ceil
7
+ from numpy.linalg import norm
8
+ import matplotlib.pyplot as plt
9
+ import matplotlib.gridspec as gridspec
10
+ from matplotlib.animation import FuncAnimation
11
+
12
+ from typing import Iterable, Union, Generator
13
+ from multiprocessing import Process, Queue, cpu_count
14
+ from tempfile import gettempdir
15
+ from os import remove, path as os_path
16
+ from PIL.Image import open as open_image
17
+ import time
18
+
19
+ class StateEvolutionRecorder:
20
+ """
21
+ A class to record the evolution of a quantum state through a quantum circuit
22
+
23
+ Attributes:
24
+ -----------
25
+ qc: QuantumCircuit
26
+ The quantum circuit to record
27
+
28
+ initial_state: Statevector
29
+ The initial state of the quantum circuit
30
+
31
+ Methods:
32
+ --------
33
+ evolve(intermediate_steps: int = 0)
34
+ Evolve the initial state through the circuit
35
+
36
+ record(filename: str, *, fps: int = 60, interval: int = 200, disk: bool = False)
37
+ Record the frames into a video file
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ qc: QuantumCircuit,
43
+ initial_state: Statevector = None,
44
+ figsize: tuple[float, float] = None,
45
+ dpi: float = None,
46
+ num_cols: int = 5,
47
+ select: list[int] = None,
48
+ style: dict = None,
49
+ ) -> None:
50
+ """
51
+ Initialize the StateEvolutionRecorder
52
+
53
+ Parameters:
54
+ -----------
55
+ qc: QuantumCircuit
56
+ The quantum circuit to record
57
+
58
+ initial_state: Statevector
59
+ The initial state of the quantum circuit, default is |0>^n
60
+
61
+ figsize: tuple[float, float]
62
+ The size of the figure, default is (6, 6)
63
+
64
+ dpi: float
65
+ The resolution of the figure, default is 100
66
+
67
+ select: list[int]
68
+ The qubits to select for the bloch vectors to display, default is all qubits
69
+
70
+ style: dict
71
+ The style of the quantum circuit diagram, default is {'name':'textbook'}
72
+ """
73
+
74
+ self._qc = qc
75
+ self._states = None
76
+ self._size = 1
77
+ self._initial_state = initial_state or Statevector.from_label('0'*self._qc.num_qubits)
78
+ self._fig = plt.figure(figsize=figsize, dpi=dpi)
79
+ self._selected_qubits = list(range(self._qc.num_qubits)) if not select else sorted(select)
80
+ num_cols = num_cols if 0 < num_cols <= len(self._selected_qubits) else 3
81
+ num_rows = 1 + int(ceil(len(self._selected_qubits) / num_cols))
82
+ self._gs = gridspec.GridSpec(num_rows, num_cols)
83
+ self._gs.set_height_ratios([3] + [2] * (num_rows - 1))
84
+ self._ax = [[self._fig.add_subplot(self._gs[0, :])]]
85
+ self._qubit_partitions = [
86
+ self._selected_qubits[i:i + num_cols]
87
+ for i in range(
88
+ 0,
89
+ min(
90
+ len(self._selected_qubits),
91
+ num_cols * num_rows
92
+ ),
93
+ num_cols
94
+ )
95
+ ]
96
+ self._ax.extend([
97
+ [
98
+ self._fig.add_subplot(self._gs[i + 1, j], projection="3d")
99
+ for j in range(len(partition))
100
+ ]
101
+ for i, partition in enumerate(self._qubit_partitions)
102
+ ])
103
+ for axes in self._ax[1:]:
104
+ for ax in axes:
105
+ ax.axis('off')
106
+ self._text = None
107
+ qc.draw(output='mpl', fold=-1, style=style if style else {'name':'textbook'} , ax=self._ax[0][0])
108
+
109
+
110
+
111
+ def evolve(self, intermediate_steps: int = 0):
112
+ """
113
+ Evolve the initial state through the circuit
114
+
115
+ Parameters:
116
+ -----------
117
+ intermediate_steps: int
118
+ The number of intermediate steps to interpolate between two adjacent states, default is 0
119
+ """
120
+
121
+ def group_instructions() -> list[list[InstructionSet]]:
122
+ """
123
+ Group the instructions taking place in parallel based on the qubits involved or barriers
124
+
125
+ Returns:
126
+ --------
127
+ list[list[InstructionSet]]: A list of grouped instructions taking place in parallel
128
+ """
129
+
130
+ current_group = []
131
+ active_qubits = set()
132
+ instructions = []
133
+
134
+ for instruction in self._qc.data:
135
+ if instruction.operation.name == 'barrier':
136
+ if current_group:
137
+ instructions.append(current_group)
138
+ current_group = []
139
+ active_qubits.clear()
140
+ continue
141
+
142
+ if instruction.operation.name == 'measure':
143
+ continue
144
+
145
+ # Get the qubits involved in the instruction
146
+ qubits_involved = set(f"{q._register.prefix}{q._index}" for q in instruction.qubits)
147
+
148
+ # If the qubits involved in the current instruction are not the same as the active qubits
149
+ if active_qubits & qubits_involved: # If there is an intersection
150
+ instructions.append(current_group)
151
+ current_group = []
152
+ active_qubits.clear()
153
+
154
+ current_group.append(instruction)
155
+ active_qubits.update(qubits_involved)
156
+
157
+ if current_group:
158
+ instructions.append(current_group)
159
+
160
+ return instructions
161
+
162
+ # Group the instructions taking place in parallel
163
+ grouped_instructions = group_instructions()
164
+
165
+ # The number of frames to render
166
+ self._size = len(grouped_instructions)+1
167
+
168
+ def generate_states() -> Generator[tuple[Statevector, Iterable[InstructionSet]], None, None]:
169
+ """
170
+ Generate the states of the quantum circuit at each step
171
+
172
+ Yields:
173
+ -------
174
+ tuple[Statevector, Iterable[InstructionSet]]:
175
+ A tuple containing the state of the quantum circuit and
176
+ the instructions responsible for the state transition
177
+ """
178
+
179
+ state = self._initial_state
180
+ for instructionSet in grouped_instructions:
181
+ yield (state, instructionSet)
182
+
183
+ sub_circuit = QuantumCircuit(*self._qc.qregs, *self._qc.cregs)
184
+ for instruction in instructionSet:
185
+ sub_circuit.append(instruction.operation, instruction.qubits, instruction.clbits)
186
+
187
+ state = state.evolve(sub_circuit)
188
+
189
+ yield (state, [])
190
+
191
+ # If there are no intermediate steps
192
+ if intermediate_steps <= 1:
193
+ self._states = generate_states()
194
+ return
195
+
196
+ def interpolate_states() -> Generator[tuple[Statevector, Iterable[InstructionSet]], None, None]:
197
+ """
198
+ Interpolate between two adjacent states to smooth the transition
199
+
200
+ Yields:
201
+ -------
202
+ tuple[Statevector, Iterable[InstructionSet]]:
203
+ A tuple containing the state of the quantum circuit and
204
+ the instructions responsible for the state transition
205
+ """
206
+
207
+ states = list(generate_states())
208
+ for lhs, rhs in zip(states[:-1], states[1:]):
209
+ for i in linspace(0, 1, intermediate_steps, endpoint=False):
210
+ intermediate_state = (1 - i) * lhs[0].data + i * rhs[0].data
211
+ state_norm = norm(intermediate_state)
212
+ if state_norm:
213
+ intermediate_state = intermediate_state / state_norm
214
+ yield (Statevector(intermediate_state), lhs[1])
215
+
216
+ yield states[-1]
217
+
218
+ # Interpolate between each pair of two adjacent states to smooth the transition
219
+ self._states = interpolate_states()
220
+ # Update the size to reflect the number of intermediate steps
221
+ self._size += len(grouped_instructions) * (intermediate_steps-1)
222
+
223
+ def _render_frame(
224
+ self,
225
+ index: int,
226
+ frame_data: tuple[Statevector, Iterable[InstructionSet]],
227
+ disk: bool
228
+ ) -> tuple[int, Union[str, ndarray[uint8]]]:
229
+ """
230
+ Render a frame. If disk is True, save the frames to disk at the OS-specific temporary directory.
231
+
232
+ Parameters:
233
+ -----------
234
+ index: int
235
+ The index of the frame
236
+
237
+ frame_data: tuple[Statevector, Iterable[InstructionSet]]
238
+ A tuple containing the state of the quantum circuit and
239
+ the instructions responsible for the state transition
240
+
241
+ disk: bool
242
+ Whether to save the frame to hard disk or not
243
+
244
+ Returns:
245
+ --------
246
+ tuple[int, Union[str, ndarray[uint8]]]:
247
+ A tuple containing the index of the frame and the frame data
248
+ either as a filename or as an image array
249
+ """
250
+
251
+ # Remove the text describing the operations
252
+ if self._text:
253
+ self._text.remove()
254
+
255
+ # Clear the axes for the bloch vectors
256
+ for axes in self._ax[1:]:
257
+ for ax in axes:
258
+ ax.clear()
259
+
260
+ state, operations = frame_data
261
+
262
+ # Allocate the axes for the bloch vectors
263
+ if len(self._ax) < 2:
264
+ self._ax.extend([
265
+ [
266
+ self._fig.add_subplot(self._gs[i + 1, j], projection="3d")
267
+ for j in range(len(partition))
268
+ ]
269
+ for i, partition in enumerate(self._qubit_partitions)
270
+ ])
271
+
272
+ # Plot the bloch vectors
273
+ bloch_data = _bloch_multivector_data(state)
274
+ for i, partition in enumerate(self._qubit_partitions):
275
+ for j in range(len(partition)):
276
+ plot_bloch_vector(bloch_data[partition[j]], f"q{partition[j]}", ax=self._ax[i + 1][j])
277
+
278
+
279
+ # Describe the operations taking place
280
+ if operations:
281
+ fragments = []
282
+ for gate in operations:
283
+ fragments.append("{0} -> {1}".format(gate.operation.name, [f"{q._register._name}{q._index}" for q in gate.qubits]))
284
+
285
+ self._text = self._fig.text(
286
+ 0.5, 0.95,
287
+ " | ".join(fragments),
288
+ ha='center', va='bottom',
289
+ fontsize=15,
290
+ transform=self._fig.transFigure
291
+ )
292
+
293
+ # Update the figure
294
+ self._fig.canvas.draw()
295
+
296
+ # Save the frame to disk
297
+ if disk:
298
+ filename = os_path.join(gettempdir(), f"{index}.png")
299
+ self._fig.savefig(filename, format="png")
300
+ return index, filename
301
+
302
+ # Return the frame as an image array
303
+ buf = self._fig.canvas.tostring_rgb()
304
+ dim = self._fig.canvas.get_width_height()[::-1] + (3,)
305
+ img = frombuffer(buf, dtype=uint8).reshape(dim)
306
+ return index, img
307
+
308
+ def _render_frames(self, disk: bool = False) -> Generator[Union[str, ndarray[uint8]], None, None]:
309
+ """
310
+ Render the frames
311
+
312
+ Parameters:
313
+ -----------
314
+ disk: bool
315
+ Whether to save the frames to disk or not
316
+
317
+ Yields:
318
+ -------
319
+ Union[str, ndarray[uint8]]:
320
+ The frame data either as a filename or as an image array
321
+ """
322
+
323
+ def generate_frames() -> Generator[tuple[int, Union[str, ndarray[uint8]]], None, None]:
324
+ """
325
+ Generate the frames using multiple processes
326
+
327
+ Yields:
328
+ -------
329
+ tuple[int, Union[str, ndarray[uint8]]]:
330
+ A tuple containing the index of the frame and the frame data
331
+ either as a filename or as an image array
332
+ """
333
+
334
+ task_queue = Queue()
335
+ result_queue = Queue()
336
+
337
+ def task():
338
+ """
339
+ The task to be executed by each process
340
+ """
341
+
342
+ while True:
343
+ args = task_queue.get()
344
+
345
+ # If there are no arguments left, terminate the process
346
+ if args is None:
347
+ break
348
+
349
+ index, frame_data, disk = args
350
+ result_queue.put(self._render_frame(index, frame_data, disk))
351
+
352
+ # Create a process for each CPU core
353
+ processes = [Process(target=task) for _ in range(cpu_count())]
354
+ for process in processes:
355
+ process.start()
356
+
357
+ # Distribute the tasks among the processes to render a frame for each state
358
+ for index, frame_data in enumerate(self._states):
359
+ task_queue.put((index, frame_data, disk))
360
+
361
+ # Terminate the processes
362
+ for _ in processes:
363
+ task_queue.put(None)
364
+
365
+ # Yield the frames in the order they were rendered
366
+ for i in range(self._size):
367
+ yield result_queue.get()
368
+ self._progress_callback(i, self._size, msg="Rendering frames")
369
+
370
+ # Wait for all the processes to terminate
371
+ for _ in processes:
372
+ process.join()
373
+
374
+ # Sort the frames in the order they were scheduled
375
+ return (frame for _, frame in sorted(generate_frames(), key=lambda x: x[0]))
376
+
377
+ def _update(self, image: Union[str, ndarray[uint8]], disk: bool):
378
+ """
379
+ Update the figure with the new frame
380
+
381
+ Parameters:
382
+ -----------
383
+ image: Union[str, ndarray[uint8]]
384
+ The frame data either as a filename or as an image array
385
+
386
+ disk: bool
387
+ Whether the frame data is a filename or an image array
388
+ """
389
+
390
+ # Remove the text describing the operations
391
+ if self._text:
392
+ self._text.remove()
393
+
394
+ # Remove the axes from the figure
395
+ if len(self._ax) > 1:
396
+ for axes in self._ax:
397
+ for ax in axes:
398
+ ax.clear()
399
+ ax.remove()
400
+ # Create a new axes for the image
401
+ self._ax = [[self._fig.add_axes([0, 0, 1, 1])]]
402
+
403
+ # If the frame data is a filename, open the image
404
+ if disk:
405
+ image = open_image(image)
406
+
407
+ # Display the image
408
+ self._ax[0][0].clear()
409
+ self._ax[0][0].imshow(image, aspect='auto')
410
+ self._ax[0][0].axis('off')
411
+
412
+ # Update the figure
413
+ self._fig.canvas.draw()
414
+
415
+ def _progress_callback(self, frame: int, total_frames: int, bar_length: int = 40, msg: str = "Progress:"):
416
+ """
417
+ Display a progress bar
418
+
419
+ Parameters:
420
+ -----------
421
+ frame: int
422
+ The current frame number
423
+
424
+ total_frames: int
425
+ The total number of frames to render
426
+
427
+ bar_length: int
428
+ The length of the progress bar
429
+
430
+ msg: str
431
+ The message to display along with the progress bar
432
+ """
433
+
434
+ progress = (frame + 1) / total_frames
435
+ percent = int(progress * 100)
436
+ filled_length = int(bar_length * progress)
437
+ bar = '█' * filled_length + '-' * (bar_length - filled_length)
438
+ template = f"\r{msg}: |{bar}| {percent:>3}% ({{frame:>{len(str(total_frames))}}}/{total_frames})"
439
+ print(template.format(frame=frame + 1), end=" "*10)
440
+
441
+
442
+ def record(self, filename: str, *, fps: int = 60, interval: int = 200, disk: bool = False):
443
+ """
444
+ Record the frames into a video file
445
+
446
+ Parameters:
447
+ -----------
448
+ filename: str
449
+ The name of the video file to create
450
+
451
+ fps: int
452
+ The number of frames per second
453
+
454
+ interval: int
455
+ The interval between each frame in milliseconds
456
+
457
+ disk: bool
458
+ Whether to save the frames to disk or not
459
+ """
460
+
461
+ try:
462
+ start_time = time.time()
463
+ # Create an animation object
464
+ anim = FuncAnimation(self._fig,
465
+ self._update,
466
+ frames=self._render_frames(disk),
467
+ fargs=(disk,),
468
+ save_count=self._size,
469
+ cache_frame_data=False,
470
+ interval=interval)
471
+ # Save the animation to a video file
472
+ anim.save(filename,
473
+ writer="ffmpeg",
474
+ fps=fps,
475
+ progress_callback=lambda frame, total_frames: self._progress_callback(frame, total_frames, msg="Encoding video"))
476
+
477
+ except KeyboardInterrupt:
478
+ # If the recording was interrupted, remove the video file
479
+ remove(filename)
480
+
481
+ else:
482
+ elapsed_time = time.time() - start_time
483
+ print(f"\nRecording finished. Elapsed time: {elapsed_time:.2f} seconds")
484
+
485
+ finally:
486
+ # Close the figure
487
+ plt.close(self._fig)
488
+
489
+ # Remove the frames from disk if any
490
+ if disk:
491
+ for index in range(self._size):
492
+ filename = os_path.join(gettempdir(), f"{index}.png")
493
+ try:
494
+ remove(filename)
495
+ except FileNotFoundError:
496
+ pass
497
+
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.1
2
+ Name: qiskit_state_evolution_recorder
3
+ Version: 0.1.0
4
+ Summary: Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits.
5
+ Home-page: https://github.com/sarumaj/qiskit-state-evolution-recorder
6
+ Author: Dawid Ciepiela
7
+ Author-email: Dawid Ciepiela <71898979+sarumaj@users.noreply.github.com>
8
+ License: BSD-3-Clause
9
+ Project-URL: Homepage, https://github.com/sarumaj/qiskit-state-evolution-recorder
10
+ Requires-Python: >=3.6, <3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: matplotlib==3.9.2
14
+ Requires-Dist: matplotlib-inline==0.1.7
15
+ Requires-Dist: numpy==2.1.1
16
+ Requires-Dist: qiskit==1.2.4
17
+ Requires-Dist: qiskit-aer==0.15.1
18
+ Requires-Dist: pillow==11.0.0
19
+
20
+ # qiskit-state-evolution-recorder
21
+
22
+ Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits.
23
+
24
+ ## Usage
25
+
26
+ ```python
27
+ from qiskit.circuit import QuantumCircuit
28
+ from qiskit_state_evolution_recorder import StateEvolutionRecorder
29
+
30
+ qc = QuantumCircuit(4)
31
+ qc.h(range(4))
32
+ qc.cx(range(3), 3)
33
+ qc.h(range(4))
34
+ qc.measure_all()
35
+
36
+ recorder = StateEvolutionRecorder(qc, figsize=(12, 8), num_cols=4, style={'name': 'bw'})
37
+ recorder.evolve(120)
38
+ recorder.record("quantum_circuit.mp4", fps=30)
39
+ ```
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ qiskit_state_evolution_recorder/__init__.py
6
+ qiskit_state_evolution_recorder.egg-info/PKG-INFO
7
+ qiskit_state_evolution_recorder.egg-info/SOURCES.txt
8
+ qiskit_state_evolution_recorder.egg-info/dependency_links.txt
9
+ qiskit_state_evolution_recorder.egg-info/requires.txt
10
+ qiskit_state_evolution_recorder.egg-info/top_level.txt
@@ -0,0 +1,6 @@
1
+ matplotlib==3.9.2
2
+ matplotlib-inline==0.1.7
3
+ numpy==2.1.1
4
+ qiskit==1.2.4
5
+ qiskit-aer==0.15.1
6
+ pillow==11.0.0
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,34 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="qiskit_state_evolution_recorder",
5
+ version="0.1.0",
6
+ description="Simple module allowing to record animations to trace changes in qubit states for arbitrary quantum circuits.",
7
+ long_description=open("README.md").read(),
8
+ long_description_content_type="text/markdown",
9
+ author="Dawid Ciepiela",
10
+ author_email="71898979+sarumaj@users.noreply.github.com",
11
+ url="https://github.com/sarumaj/qiskit-state-evolution-recorder",
12
+ packages=find_packages(),
13
+ install_requires=[
14
+ "matplotlib==3.9.2",
15
+ "matplotlib-inline==0.1.7",
16
+ "numpy==2.1.1",
17
+ "qiskit==1.2.4",
18
+ "qiskit-aer==0.15.1",
19
+ "pillow==11.0.0"
20
+ ],
21
+ classifiers=[
22
+ "License :: OSI Approved :: BSD License",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.6",
25
+ "Programming Language :: Python :: 3.7",
26
+ "Programming Language :: Python :: 3.8",
27
+ "Programming Language :: Python :: 3.9",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Operating System :: OS Independent",
31
+ ],
32
+ license="BSD-3-Clause",
33
+ python_requires=">=3.6, <3.12",
34
+ )