Source code for qugrad.systems._systems
1"""
2Defines classes for quantum systems
3"""
4
5from typing import Callable, Optional, Union
6
7import numpy as np
8import tensorflow as tf
9
10from py_ste.evolvers import UnitaryEvolver
11
12from .._hilbert_space import HilbertSpace
13
14def generate_channel_couplings(number_channels: list[int]) -> np.ndarray[bool]:
15 """Generates a boolean array indicating which channels couple to which
16 drives.
17
18 Parameters
19 ----------
20 number_chanels : list[int]
21 A list with the nth entry indicating the number of channels that
22 correspond the nth drive.
23
24 Returns
25 -------
26 NDArray[Shape[``len(number_channels)``, total_number_channels], bool]
27 An entry is `True` if the channel corresponds to the drive.
28 """
29 ...
30
31# Defining classes
32class ExpValCustom:
33 """
34 A class implimenting :meth:`QuantumSystem.evolved_expectation_value()` with
35 a `TensorFlow <https://www.tensorflow.org>`__ gradient.
36 """
37
38 system: "QuantumSystem"
39 "The system in which to take the expectation value"
40
41 initial_state: np.ndarray[complex]
42 "The initial state for the integrator"
43
44 dt: float
45 "The integrator time step"
46
47 observable: np.ndarray[complex]
48 "The observable to take the expectation value of"
49
50 def __init__(self,
51 system: "QuantumSystem",
52 initial_state: np.ndarray[complex],
53 dt: float,
54 observable: np.ndarray[complex]):
55 """
56 Initialises the class with the `system` in which to take the expectation
57 value and the `observable` in the `system` to take the expectation value
58 of. Additionally, the initial state before evolution and
59 the integrator time step are specified.
60
61 Parameters
62 ----------
63 system : QuantumSystem
64 The system in which to take the expectation value
65 initial_state : NDArray[Shape[``system.state_shape``], complex]
66 The initial state for the integrator
67 dt : float
68 The integrator time step
69 observable : NDArray[Shape[``system.dim``, ``system.dim``], complex])
70 The observable to take the expectation value of
71 """
72 ...
73 @tf.custom_gradient
74 def run(self, ctrl_amp):
75 """
76 Computes the expectation value of the :attr:`observable` in the
77 :attr:`system` with respect to the :attr:`initial_state` evolved under
78 the Hamiltonian generated by the specified control amplitudes.
79
80 Parameters
81 ----------
82 ctrl_amp : tf.Tensor[Shape[n_time_steps, ``system.n_ctrl``], tf.complex128]
83 The control amplitudes
84
85 Returns
86 -------
87 tf.Tensor[Shape[], tf.complex128]
88 The expectation value
89
90 Note
91 ----
92 This function is differentiable using TensorFlow's ``tf.GradientTape``.
93 """
94 ...
95
[docs]
96class QuantumSystem:
97 """
98 A class storing the properties of a quantum system.
99 """
100 _evolver: Optional[UnitaryEvolver] = None
101 "The integrator used for time evolutions of the system."
102
103 _hilbert_space: HilbertSpace
104 "The Hilbert space of the system"
105
106 _H0: np.ndarray
107 "The systems drift Hamiltonian as a :attr:`dim` x :attr:`dim` matrix."
108
109 _Hs: np.ndarray
110 """
111 An array of the system's control Hamiltonians with shape
112 (:attr:`n_ctrl`, :attr:`dim`, :attr:`dim`).
113 """
114
115 _graph_processing: Callable[..., tuple]
116 "A Tensorflow graph of :attr:`_processing()`"
117
118 _processing: Callable[..., tuple]
119 """
120 Executes :meth:`_pre_processing()` followed by
121 :meth:`_envolope_processing()` eagerly (i.e. without using a TensorFlow
122 graph). Nonetheless, :meth:`_eager_processing()` is still auto
123 differentiable.
124
125 Parameters
126 ----------
127 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
128 The envolope control amplitudes
129 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
130 The initial state for the integrator
131 dt : float
132 The itegration time step
133 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
134 The frequencies to modulate the control amplitudes with
135 number_channels : list[int]
136 The number of channels associated with each control Hamiltonian
137
138 Warning
139 -------
140 This must be a ``list`` and not an ``NDArray`` or a
141 `TensorFlow <https://www.tensorflow.org>`__ tensor.
142
143 Returns
144 -------
145 tuple[tf.Tensor[Shape[n_time_steps, :attr:`n_ctrl`], complex], tf.Tensor[Shape[:attr:`state_shape`], complex], tf.Tensor[Shape[], float]]
146 A tuple of:
147 1. Control amplitudes
148 2. Initial state
149 3. Integrator time step
150 """
151
152 _using_graph: bool
153 """
154 Whether to use TensorFlow graphs during computation. Using a TensorFlow
155 graph will increase the speed of computation. However, you have to be
156 careful that function parameters have not been baked into the graph leading
157 to unexpected behaviour.
158 """
159
[docs]
160 def __init__(self,
161 H0: np.ndarray[complex],
162 Hs: np.ndarray[complex],
163 hilbert_space: HilbertSpace,
164 use_graph: bool = True):
165 """
166 Initialises a new :class:`QuantumSystem`.
167
168 Parameters
169 ----------
170 H0 : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
171 The systems drift Hamiltonian
172 Hs : NDArray[Shape[:attr:`n_ctrl`, :attr:`dim`, :attr:`dim`], complex] | NDArray[Shape[:attr:`n_ctrl` * :attr:`dim`, :attr:`dim`], complex]
173 The systems control Hamiltonians either as an array of control
174 Hamiltonians or the control Hamiltonians stacked along the first
175 axis.
176 hilbert_space : HilbertSpace
177 The Hilbert space of the system
178 use_graph : bool = True
179 Whether to use TensorFlow graphs during computation.
180 """
181 ...
182
183 def __del__(self):
184 ...
185 @property
186 def using_graph(self) -> bool:
187 """
188 Whether to use `TensorFlow <https://www.tensorflow.org>`__ graphs during
189 computation. Using a `TensorFlow <https://www.tensorflow.org>`__ graph
190 will increase the speed of computation. However, you have to be careful
191 that function parameters have not been baked into the graph leading to
192 unexpected behaviour.
193 """
194 ...
195 @using_graph.setter
196 def using_graph(self, value: bool):
197 ...
198 @property
199 def hilbert_space(self) -> HilbertSpace:
200 "The Hilbert space of the system"
201 ...
202 @property
203 def H0(self) -> np.ndarray[complex]:
204 """
205 The systems drift Hamiltonian as a :attr:`dim` x :attr:`dim` matrix.
206
207 See Also
208 --------
209 :attr:`Hs`
210 """
211 ...
212 @property
213 def Hs(self) -> np.ndarray[complex]:
214 """
215 An array of the system's control Hamiltonians with shape
216 (:attr:`n_ctrl`, :attr:`dim`, :attr:`dim`).
217
218 See Also
219 --------
220 :attr:`H0`
221 """
222 ...
223 @property
224 def dim(self) -> int:
225 """
226 The dimension of states in the quantum system.
227
228 See Also
229 --------
230 :attr:`state_shape`
231 """
232 ...
233 @property
234 def state_shape(self) -> tuple[int]:
235 """
236 The shape of the states in the system.
237
238 See Also
239 --------
240 :attr:`dim`
241 """
242 ...
243 @property
244 def n_ctrl(self) -> int:
245 """
246 The number of control Hamiltonians.
247 """
248 ...
249 @property
250 def evolver(self) -> UnitaryEvolver:
251 """
252 The integrator used for time evolutions of the system.
253
254 Note
255 ----
256 The `evolver` can take a while to initialise and so is not initialised
257 until `evolver` is is first used or when :meth:`initialise_evolver()` is
258 called. Using `evolver` before calling :meth:`initialise_evolver()`
259 initialises the `evolver` with the default parameters of
260 :meth:`initialise_evolver()`.
261 """
262 ...
[docs]
263 def initialise_evolver(self,
264 sparse: bool = False,
265 force_dynamic: bool = False):
266 """
267 Initialises :attr:`evolver` with an evolver from
268 `PySTE <https://PySTE.readthedocs.io>`__.
269 `PySTE <https://PySTE.readthedocs.io>`__ is Python
270 wrapper around the C++ header-only library
271 `Suzuki-Trotter-Evolver <https://Suzuki-Trotter-Evolver.readthedocs.io>`__:
272 a fast Schrödinger solver utilising the first-order Suzuki-Trotter
273 expansion.
274
275 Warning
276 -------
277 This can take a very long time to execute, especially for large Hilbert
278 space dimensions. If you plan to evolve the same quantum system many
279 times we recommended pickling the :attr:`evolver`.
280
281 Parameters
282 ----------
283 sparse : bool
284 Whether to use sparse or dense matrices during integration.
285 To make a decision on whether sparse or dense matrices are likely to
286 lead to faster integration you can consult the benchmarks at
287 https://PySTE.readthedocs.io/en/latest/benchmarks.
288 force_dynamic : bool
289 Whether to force `PySTE <https://PySTE.readthedocs.io>`__ to use a
290 dynamic evolver.
291
292 Note
293 ----
294 `PySTE <https://PySTE.readthedocs.io>`__ has precompiled evolvers
295 for specific Hilbert space dimensions and numbers of control
296 Hamiltonians. When these cannot be found
297 `PySTE <https://PySTE.readthedocs.io>`__ uses less efficient
298 evolvers with the Hilbert space dimension and the number of controls
299 determined dynamically at runtime.
300 """
301 ...
302 def _H(self, ctrl_amp: np.ndarray[float]) -> np.ndarray[complex]:
303 """
304 Computes the system Hamiltonian for the specified control amplitudes.
305
306 Parameters
307 ----------
308 ctrl_amp : NDArray[Shape[s := Any_Shape, :attr:`n_ctrl`], float]
309 The control amplitudes (stored in the last axis). The prior axes
310 allow for multiple sets of control amplitudes to be passed and the
311 Hamiltonian for each computed.
312
313 Returns
314 -------
315 NDArray[Shape[s, :attr:`dim`, :attr:`dim`], complex]
316 The system's Hamiltonian (stored in the last two axes).
317 """
318 ...
[docs]
319 def H(self,
320 ctrl_amp: Union[np.ndarray[float], np.ndarray[Callable[[float], np.ndarray[float]]]]
321 ) -> Union[np.ndarray[complex], Callable[[float], np.ndarray[complex]]]:
322 """
323 Computes the system Hamiltonian for the specified control amplitudes.
324
325 Parameters
326 ----------
327 ctrl_amp : NDArray[Shape[s := Any_Shape, :attr:`n_ctrl`], float | Callable[[float], np.ndarray[float]]]
328 The control amplitudes (stored in the last axis). The prior axes
329 allow for multiple sets of control amplitudes to be passed and the
330 Hamiltonian for each computed. The control amplitudes can be passed
331 as ``np.ndarray[float]`` to compute the system Hamiltonian for a
332 specific value of the control ampltiudes. Alternatively, the control
333 amplitudes can be passed as
334 ``np.ndarray[Callable[[float], np.ndarray[float]]]`` where each
335 element is a function of time. This will generate a time-dependent
336 Hamiltonian: a function that takes a single parameter (time) and
337 returns the Hamiltonian at this time.
338
339 Returns
340 -------
341 NDArray[Shape[s, :attr:`dim`, :attr:`dim`], complex] | NDArray[Shape[s], Callable[[float], np.ndarray[complex]]]]
342 Either the systems Hamiltonian stored in the last two axes (if
343 specific control amplitudes were passed) or a collection of
344 time-dependent Hamiltonians (if time-dependent controls were
345 passed).
346 """
347 ...
[docs]
348 def _pre_processing(self,
349 ctrl_amp : np.ndarray[complex],
350 initial_state : np.ndarray[complex],
351 dt: float,
352 frequencies: np.ndarray[complex],
353 number_channels: list[int]
354 ) -> tuple:
355 """
356 When calling any evolution method (listed in the
357 :ref:`See also section <pre_processing_see_also>`) :meth:`_pre_processing()` is
358 executed on the arguements before the control amplitudes are modulated
359 by the frequencies (during :meth:`_envolope_processing()`) and then finally
360 the modulated control amplitudes are used by the evolution method.
361
362 :meth:`_pre_processing()` should be overridden to produce desired pulse
363 shapes. You can either override :meth:`_pre_processing()` directly by
364 creating a child class, or you can use :meth:`pulse_form()`.
365
366 For :meth:`gradient()` to function correctly :meth:`_pre_processing()`
367 should be written in `TensorFlow <https://www.tensorflow.org>`__.
368
369 Parameters
370 ----------
371 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
372 The envolope control amplitudes
373 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
374 The initial state for the integrator
375 dt : float
376 The itegration time step
377 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
378 The frequencies to modulate the control amplitudes with
379 number_channels : list[int]
380 The number of channels associated with each control Hamiltonian
381
382 Warning
383 -------
384 This must be a ``list`` and not an ``NDArray`` or a
385 `TensorFlow <https://www.tensorflow.org>`__ tensor.
386
387 Returns
388 -------
389 tuple[tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], tf.Tensor[Shape[:attr:`state_shape`], tf.complex128], float, tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], list[int]]
390 A tuple of
391 1. The control amplitude envolopes
392 2. The initial state
393 3. The integrator time step
394 4. The frequencies to modulate the control amplitude envolopes with
395 5. A list of the number of channels for each control Hamiltonian
396
397 Warning
398 -------
399 The number of channels for each control Hamiltonian must be stored
400 as a ``list`` and not an ``NDArray`` or a
401 `TensorFlow <https://www.tensorflow.org>`__ tensor.
402
403 Warning
404 -------
405 Keyword arguments are not supported.
406
407 .. _pre_processing_see_also:
408
409 See Also
410 --------
411 * :meth:`propagate()`
412 * :meth:`propagate_collection()`
413 * :meth:`propagate_all()`
414 * :meth:`evolved_expectation_value()`
415 * :meth:`evolved_expectation_value_all()`
416 * :meth:`get_driving_pulses()`
417 * :meth:`gradient()`
418 """
419 ...
[docs]
420 def _envolope_processing(self,
421 ctrl_amp,
422 dt: float,
423 frequencies,
424 number_channels: list[int]
425 ) -> tuple:
426 """
427 When calling any evolution method (listed in the
428 :ref:`See also section <envolope_processing_see_also>` section)
429 :meth:`_pre_processing()` is executed on the arguements before the
430 control amplitudes are modulated by the frequencies during
431 :meth:`_envolope_processing()` and then finally the modulated control
432 amplitudes are used by the evolution method.
433
434 Parameters
435 ----------
436 ctrl_amp : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
437 The envolope control amplitudes
438 dt : float
439 The itegration time step
440 frequencies : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
441 The frequencies to modulate the control amplitudes with
442 number_channels : list[int]
443 The number of channels associated with each control Hamiltonian
444
445 Warning
446 -------
447 This must be a ``list`` and not an ``NDArray`` or a
448 `TensorFlow <https://www.tensorflow.org>`__ tensor.
449
450 Returns
451 -------
452 tf.Tensor[Shape[n_time_steps, :attr:`n_ctrl`], tf.complex128]
453 The modulated control amplitudes
454
455
456 .. _envolope_processing_see_also:
457
458 See Also
459 --------
460 * :meth:`propagate()`
461 * :meth:`propagate_collection()`
462 * :meth:`propagate_all()`
463 * :meth:`evolved_expectation_value()`
464 * :meth:`evolved_expectation_value_all()`
465 * :meth:`get_driving_pulses()`
466 * :meth:`gradient()`
467 """
468 ...
[docs]
469 def propagate(self,
470 ctrl_amp : np.ndarray[complex],
471 initial_state : np.ndarray[complex],
472 dt: float,
473 frequencies: np.ndarray[complex],
474 number_channels: list[int]
475 ) -> np.ndarray[complex]:
476 """
477 Evolves a state vector under the time-dependent Hamiltonian defined by
478 the control amplitudes using
479 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate()`
480 from `PySTE <https://PySTE.readthedocs.io>`__.
481
482 Parameters
483 ----------
484 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
485 The envolope control amplitudes
486 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
487 The initial state for the integrator
488 dt : float
489 The itegration time step
490 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
491 The frequencies to modulate the control amplitudes with
492 number_channels : list[int]
493 The number of channels associated with each control Hamiltonian
494
495 Warning
496 -------
497 This must be a ``list`` and not an ``NDArray`` or a
498 `TensorFlow <https://www.tensorflow.org>`__ tensor.
499
500 Warning
501 -------
502 Keyword arguments are not supported.
503
504 Returns
505 -------
506 NDArray[Shape[:attr:`state_shape`], complex]
507 The final state
508
509 See Also
510 --------
511 * :meth:`propagate_collection()`
512 * :meth:`propagate_all()`
513 """
514 ...
[docs]
515 def propagate_collection(self,
516 ctrl_amp : np.ndarray[complex],
517 initial_states : np.ndarray[complex],
518 dt: float,
519 frequencies: np.ndarray[complex],
520 number_channels: list[int]
521 ) -> np.ndarray[complex]:
522 """
523 Evolves a collection of state vectors under the time-dependent
524 Hamiltonian defined by the control amplitudes using
525 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate_collection()`
526 from `PySTE <https://PySTE.readthedocs.io>`__.
527
528 Parameters
529 ----------
530 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
531 The envolope control amplitudes
532 initial_states : NDArray[Shape[n_states, :attr:`state_shape`], complex]
533 The initial state for the integrator
534 dt : float
535 The itegration time step
536 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
537 The frequencies to modulate the control amplitudes with
538 number_channels : list[int]
539 The number of channels associated with each control Hamiltonian
540
541 Warning
542 -------
543 This must be a ``list`` and not an ``NDArray`` or a
544 `TensorFlow <https://www.tensorflow.org>`__ tensor.
545
546 Warning
547 -------
548 Keyword arguments are not supported.
549
550 Returns
551 -------
552 NDArray[Shape[n_states, :attr:`state_shape`], complex]
553 The final state
554
555 See Also
556 --------
557 * :meth:`propagate()`
558 * :meth:`propagate_all()`
559 """
560 ...
[docs]
561 def propagate_all(self,
562 ctrl_amp : np.ndarray[complex],
563 initial_states : np.ndarray[complex],
564 dt: float,
565 frequencies: np.ndarray[complex],
566 number_channels: list[int]
567 ) -> np.ndarray[complex]:
568 """
569 Evolves a state vector under the time-dependent Hamiltonian defined by
570 the control amplitudes using
571 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate_all()`
572 from `PySTE <https://PySTE.readthedocs.io>`__ and returns the state at
573 each time-step.
574
575 Parameters
576 ----------
577 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
578 The envolope control amplitudes
579 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
580 The initial state for the integrator
581 dt : float
582 The itegration time step
583 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
584 The frequencies to modulate the control amplitudes with
585 number_channels : list[int]
586 The number of channels associated with each control Hamiltonian
587
588 Warning
589 -------
590 This must be a ``list`` and not an ``NDArray`` or a
591 `TensorFlow <https://www.tensorflow.org>`__ tensor.
592
593 Warning
594 -------
595 Keyword arguments are not supported.
596
597 Returns
598 -------
599 NDArray[Shape[:attr:`state_shape`, n_time_steps+1], complex]
600 The state at each integrator time step (including the initial
601 state).
602
603 See Also
604 --------
605 * :meth:`propagate()`
606 * :meth:`propagate_collection()`
607 """
608 ...
[docs]
609 def evolved_expectation_value(self,
610 ctrl_amp : np.ndarray[complex],
611 initial_state : np.ndarray[complex],
612 dt: float,
613 frequencies: np.ndarray[complex],
614 number_channels: list[int],
615 observable : np.ndarray[complex]
616 ) -> complex:
617 """
618 Evolves a state vector under the time-dependent Hamiltonian defined by
619 the control amplitudes and computes the expectation value of a specified
620 observable with respect to the final state using
621 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.evolved_expectation_value()`
622 from `PySTE <https://PySTE.readthedocs.io>`__.
623
624 Parameters
625 ----------
626 ctrl_amp : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
627 The envolope control amplitudes
628 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
629 The initial state for the integrator
630 dt : float
631 The itegration time step
632 frequencies : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
633 The frequencies to modulate the control amplitudes with
634 number_channels : list[int]
635 The number of channels associated with each control Hamiltonian
636
637 Warning
638 -------
639 This must be a ``list`` and not an ``NDArray`` or a
640 `TensorFlow <https://www.tensorflow.org>`__ tensor.
641 observable : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
642 The observable to take the expectation value of.
643
644 Warning
645 -------
646 Keyword arguments are not supported.
647
648 Returns
649 -------
650 complex
651 The expectation value.
652
653 See Also
654 --------
655 * :meth:`evolved_expectation_value_all()`
656 * :meth:`gradient()`
657 """
658 ...
[docs]
659 def evolved_expectation_value_all(self,
660 ctrl_amp : np.ndarray[complex],
661 initial_state : np.ndarray[complex],
662 dt: float,
663 frequencies: np.ndarray[complex],
664 number_channels: list[int],
665 observable : np.ndarray[complex]
666 ) -> np.ndarray[complex]:
667 """
668 Evolves a state vector under the time-dependent Hamiltonian defined by
669 the control amplitudes and computes the expectation value of a specified
670 observable with respect to the state at each time-step using
671 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.evolved_expectation_value_all()`
672 from `PySTE <https://PySTE.readthedocs.io>`__.
673
674 Parameters
675 ----------
676 ctrl_amp : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
677 The envolope control amplitudes
678 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
679 The initial state for the integrator
680 dt : float
681 The itegration time step
682 frequencies : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
683 The frequencies to modulate the control amplitudes with
684 number_channels : list[int]
685 The number of channels associated with each control Hamiltonian
686
687 Warning
688 -------
689 This must be a ``list`` and not an ``NDArray`` or a
690 `TensorFlow <https://www.tensorflow.org>`__ tensor.
691 observable : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
692 The observable to take the expectation value of.
693
694 Warning
695 -------
696 Keyword arguments are not supported.
697
698 Returns
699 -------
700 NDArray[Shape[n_time_steps+1], complex]
701 The state at each integrator time step (including the initial
702 state).
703
704 See Also
705 --------
706 * :meth:`evolved_expectation_value()`
707 * :meth:`gradient()`
708 """
709 ...
[docs]
710 def get_driving_pulses(self,
711 ctrl_amp : np.ndarray[complex],
712 initial_states : np.ndarray[complex],
713 dt: float,
714 frequencies: np.ndarray[complex],
715 number_channels: list[int]
716 ) -> tuple[np.ndarray[complex], np.ndarray[complex], float]:
717 """
718 When calling any evolution method (listed in the
719 :ref:`See also section <get_driving_pulses_see_also>`) :meth:`get_driving_pulses()`
720 is executed on the arguements before the evolution method.
721
722 Parameters
723 ----------
724 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
725 The envolope control amplitudes
726 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
727 The initial state for the integrator
728 dt : float
729 The itegration time step
730 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
731 The frequencies to modulate the control amplitudes with
732 number_channels : list[int]
733 The number of channels associated with each control Hamiltonian
734
735 Warning
736 -------
737 This must be a ``list`` and not an ``NDArray`` or a
738 `TensorFlow <https://www.tensorflow.org>`__ tensor.
739
740 Warning
741 -------
742 Keyword arguments are not supported.
743
744 Returns
745 -------
746 tuple[NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex], NDArray[Shape[:attr:`state_shape`], complex], float]
747 A tuple of:
748 1. Control amplitudes
749 2. Initial state
750 3. Integrator time step
751
752
753 .. _get_driving_pulses_see_also:
754
755 See Also
756 --------
757 * :meth:`propagate()`
758 * :meth:`propagate_collection()`
759 * :meth:`propagate_all()`
760 * :meth:`evolved_expectation_value()`
761 * :meth:`evolved_expectation_value_all()`
762 * :meth:`gradient()`
763 """
764 ...
765 def _eager_processing(self,
766 ctrl_amp : np.ndarray[complex],
767 initial_states : np.ndarray[complex],
768 dt: float,
769 frequencies: np.ndarray[complex],
770 number_channels: list[int]
771 ) -> tuple:
772 """
773 Executes :meth:`_pre_processing()` followed by
774 :meth:`_envolope_processing()` eagerly (i.e. without using a
775 `TensorFlow <https://www.tensorflow.org>`__ graph). Nonetheless,
776 :meth:`_eager_processing()` is still auto differentiable.
777
778 Parameters
779 ----------
780 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
781 The envolope control amplitudes
782 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
783 The initial state for the integrator
784 dt : float
785 The itegration time step
786 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
787 The frequencies to modulate the control amplitudes with
788 number_channels : list[int]
789 The number of channels associated with each control Hamiltonian
790
791 Warning
792 -------
793 This must be a ``list`` and not an ``NDArray`` or a
794 `TensorFlow <https://www.tensorflow.org>`__ tensor.
795
796 Warning
797 -------
798 Keyword arguments are not supported.
799
800 Returns
801 -------
802 tuple[tf.Tensor[Shape[n_time_steps, :attr:`n_ctrl`], complex], tf.Tensor[Shape[:attr:`state_shape`], complex], tf.Tensor[Shape[], float]]
803 A tuple of:
804 1. Control amplitudes
805 2. Initial state
806 3. Integrator time step
807 """
808 ...
809 def _traceable_eager_processing(self,
810 ctrl_amp : np.ndarray[complex],
811 initial_states : np.ndarray[complex],
812 dt: float,
813 frequencies: np.ndarray[complex],
814 number_channels: list[int]
815 ) -> tuple:
816 """
817 A function that will be traced by
818 `TensorFlow <https://www.tensorflow.org>`__ to produce a graph of
819 :meth:`_pre_processing()` followed by :meth:`_envolope_processing()`.
820
821 Parameters
822 ----------
823 ctrl_amp : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
824 The envolope control amplitudes
825 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
826 The initial state for the integrator
827 dt : float
828 The itegration time step
829 frequencies : NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex]
830 The frequencies to modulate the control amplitudes with
831 number_channels : list[int]
832 The number of channels associated with each control Hamiltonian
833
834 Warning
835 -------
836 This must be a ``list`` and not an ``NDArray`` or a
837 `TensorFlow <https://www.tensorflow.org>`__ tensor.
838
839 Warning
840 -------
841 Keyword arguments are not supported.
842
843 Returns
844 -------
845 tuple[tf.Tensor[Shape[n_time_steps, :attr:`n_ctrl`], complex], tf.Tensor[Shape[:attr:`state_shape`], complex], tf.Tensor[Shape[], float]]
846 A tuple of:
847 1. Control amplitudes
848 2. Initial state
849 3. Integrator time step
850 """
851 ...
[docs]
852 def gradient(self,
853 ctrl_amp : np.ndarray[complex],
854 initial_state : np.ndarray[complex],
855 dt: float,
856 frequencies: np.ndarray[complex],
857 number_channels: list[int],
858 observable : np.ndarray[complex]
859 ) -> tuple[float, np.ndarray[float]]:
860 """
861 Evolves a state vector under the time-dependent Hamiltonian defined by
862 the control amplitudes and computes the expectation value of a specified
863 observable with respect to the final state and then computes the
864 gradient of the final state with respect to the first argument
865 (``args[0]``) using
866 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.switching_function()`
867 from `PySTE <https://PySTE.readthedocs.io>`__.
868
869 Parameters
870 ----------
871 ctrl_amp : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
872 The envolope control amplitudes
873 initial_state : NDArray[Shape[:attr:`state_shape`], complex]
874 The initial state for the integrator
875 dt : float
876 The itegration time step
877 frequencies : tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128]
878 The frequencies to modulate the control amplitudes with
879 number_channels : list[int]
880 The number of channels associated with each control Hamiltonian
881
882 Warning
883 -------
884 This must be a ``list`` and not an ``NDArray`` or a
885 `TensorFlow <https://www.tensorflow.org>`__ tensor.
886 observable : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
887 The observable to take the expectation value of.
888
889 Warning
890 -------
891 Keyword arguments are not supported.
892
893 Returns
894 -------
895 tuple[complex, NDArray[Shape[n_parameters], float]]
896 A tuple of the expectation value and the gradient.
897
898 See Also
899 --------
900 * :meth:`evolved_expectation_value()`
901 * :meth:`evolved_expectation_value_all()`
902 """
903 ...
[docs]
904 def pulse_form(self,
905 pulse_function: Callable,
906 path: Optional[str] = None
907 ) -> "PulseForm":
908 """
909 Initialises a new :class:`QuantumSystem` in which
910 :meth:`_pre_processing()` corresponds to executing ``pulse_function()``
911 and piping the output into the previous definition of
912 :meth:`_pre_processing()`.
913
914 Parameters
915 ----------
916 pulse_function : Callable
917 The function to compose with :meth:`_pre_processing()`.
918
919 Returns
920 -------
921 PulseForm
922 The new :class:`QuantumSystem`
923 """
924 ...
925
[docs]
926class TransformedSystem(QuantumSystem):
927 """
928 A base class for representing a transformation on a :class:`qugrad.QuantumSystem`.
929 """
930
931 _original_system: QuantumSystem
932 "The system that was transformed into this system"
933
934 _base_system: QuantumSystem
935 """
936 The system before any transformations were applied. That is `_base_system`
937 is the recursive :attr:`original_system`
938 (``original_system.original_system.original_system....``) until
939 :attr:`original_system` is no longer a :class:`TransformedSystem`.
940 """
941
[docs]
942 def __init__(self,
943 original_system: QuantumSystem,
944 H0: np.ndarray[complex],
945 Hs: Union[np.ndarray[complex], np.ndarray[complex]],
946 hilbert_space: HilbertSpace):
947 """
948 Performs a transformation on a :class:`qugrad.QuantumSystem`.
949
950 Parameters
951 ----------
952 original_system: QuantumSystem
953 The system to be transformed into this system
954 H0: NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
955 The new drift Hamiltonian
956 Hs: NDArray[Shape[":attr:`n_ctrl`, :attr:`dim`, :attr:`dim`"], complex] | NDArray[Shape[:attr:`n_ctrl` * :attr:`dim`, :attr:`dim`], complex]
957 The new control Hamiltonians either as an array of control
958 Hamiltonians or the control Hamiltonians stacked along the first
959 axis.
960 hilbert_space: HilbertSpace
961 The new Hilbert space of the system
962 """
963 ...
964 @property
965 def original_system(self) -> QuantumSystem:
966 "The system that was transformed into this system"
967 ...
968 @property
969 def base_system(self) -> QuantumSystem:
970 """
971 The system before any transformations were applied. That is
972 :attr:`base_system` is the recursive :attr:`original_system`
973 (``original_system.original_system.original_system....``) until
974 :attr:`original_system` is no longer a :class:`TransformedSystem`.
975 """
976 ...
[docs]
977 def _pre_processing(self, *args):
978 """
979 When calling any evolution method (listed in the
980 :ref:`See also section <TransformedSystem_pre_processing_see_also>`
981 section) :meth:`_pre_processing()` is executed on the arguements before
982 the control amplitudes are modulated by the frequencies (during
983 :meth:`_envolope_processing()`) and then finally the modulated control
984 amplitudes are used by the evolution method.
985
986 This is a placeholder for ``original_system._pre_processing()``.
987
988 Parameters
989 ----------
990 *args
991 The placeholder parameters. The actual parameters will be the same
992 as ``original_system._pre_processing()``.
993
994 Returns
995 -------
996 tuple[tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], tf.Tensor[Shape[:attr:`state_shape`], tf.complex128], float, tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], list[int]]
997 A tuple of
998 1. The control amplitude envolopes
999 2. The initial state
1000 3. The integrator time step
1001 4. The frequencies to modulate the control amplitude envolopes with
1002 5. A list of the number of channels for each control Hamiltonian
1003
1004 Warning
1005 -------
1006 The number of channels for each control Hamiltonian must be stored
1007 as a ``list`` and not an ``NDArray`` or a
1008 `TensorFlow <https://www.tensorflow.org>`__ tensor.
1009
1010
1011 .. _TransformedSystem_pre_processing_see_also:
1012
1013 See Also
1014 --------
1015 * :meth:`propagate()`
1016 * :meth:`propagate_collection()`
1017 * :meth:`propagate_all()`
1018 * :meth:`evolved_expectation_value()`
1019 * :meth:`evolved_expectation_value_all()`
1020 * :meth:`get_driving_pulses()`
1021 * :meth:`gradient()`
1022 """
1023 ...
[docs]
1024 def propagate(self, *args) -> np.ndarray[complex]:
1025 """
1026 Evolves a state vector under the time-dependent Hamiltonian defined by
1027 the control amplitudes using
1028 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate()`
1029 from `PySTE <https://PySTE.readthedocs.io>`__.
1030
1031 Parameters
1032 ----------
1033 *args
1034 The placeholder parameters. The actual parameters will be the same
1035 as ``original_system._pre_processing()``.
1036
1037 Returns
1038 -------
1039 NDArray[Shape[:attr:`state_shape`], complex]
1040 The final state
1041
1042 See Also
1043 --------
1044 * :meth:`propagate_collection()`
1045 * :meth:`propagate_all()`
1046 """
1047 ...
[docs]
1048 def propagate_collection(self, *args) -> np.ndarray[complex]:
1049 """
1050 Evolves a collection of state vectors under the time-dependent
1051 Hamiltonian defined by the control amplitudes using
1052 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate_collection()`
1053 from `PySTE <https://PySTE.readthedocs.io>`__.
1054
1055 Parameters
1056 ----------
1057 *args
1058 The placeholder parameters. The actual parameters will be the same
1059 as ``original_system._pre_processing()``.
1060
1061 Returns
1062 -------
1063 NDArray[Shape[n_states, :attr:`state_shape`], complex]
1064 The final state
1065
1066 See Also
1067 --------
1068 * :meth:`propagate()`
1069 * :meth:`propagate_all()`
1070 """
1071 ...
[docs]
1072 def propagate_all(self, *args) -> np.ndarray[complex]:
1073 """
1074 Evolves a state vector under the time-dependent Hamiltonian defined by
1075 the control amplitudes using
1076 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate_all()`
1077 from `PySTE <https://PySTE.readthedocs.io>`__ and returns the state at
1078 each time-step.
1079
1080 Parameters
1081 ----------
1082 *args
1083 The placeholder parameters. The actual parameters will be the same
1084 as ``original_system._pre_processing()``.
1085
1086 Returns
1087 -------
1088 NDArray[Shape[n_time_steps+1, :attr:`state_shape`], complex]
1089 The state at each integrator time step (including the initial
1090 state).
1091
1092 See Also
1093 --------
1094 * :meth:`propagate()`
1095 * :meth:`propagate_collection()`
1096 """
1097 ...
[docs]
1098 def evolved_expectation_value(self, *args) -> complex:
1099 """
1100 Evolves a state vector under the time-dependent Hamiltonian defined by
1101 the control amplitudes and computes the expectation value of a specified
1102 observable with respect to the final state using
1103 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.evolved_expectation_value()`
1104 from `PySTE <https://PySTE.readthedocs.io>`__.
1105
1106 Parameters
1107 ----------
1108 ``*args[:-1]``
1109 The placeholder parameters. The actual parameters will be the same
1110 as ``original_system._pre_processing()``.
1111 ``args[-1]`` : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
1112 The observable to take the expectation value of.
1113
1114
1115 Returns
1116 -------
1117 complex
1118 The expectation value.
1119
1120 See Also
1121 --------
1122 * :meth:`evolved_expectation_value_all()`
1123 * :meth:`gradient()`
1124 """
1125 ...
[docs]
1126 def evolved_expectation_value_all(self, *args) -> np.ndarray[complex]:
1127 """
1128 Evolves a state vector under the time-dependent Hamiltonian defined by
1129 the control amplitudes and computes the expectation value of a specified
1130 observable with respect to the state at each time-step using
1131 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.evolved_expectation_value_all()`
1132 from `PySTE <https://PySTE.readthedocs.io>`__.
1133
1134 Parameters
1135 ----------
1136 ``*args[:-1]``
1137 The placeholder parameters. The actual parameters will be the same
1138 as ``original_system._pre_processing()``.
1139 ``args[-1]`` : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
1140 The observable to take the expectation value of.
1141
1142 Returns
1143 -------
1144 NDArray[Shape[n_time_steps+1], complex]
1145 The state at each integrator time step (including the initial
1146 state).
1147
1148 See Also
1149 --------
1150 * :meth:`evolved_expectation_value()`
1151 * :meth:`gradient()`
1152 """
1153 ...
[docs]
1154 def get_driving_pulses(self, *args) -> tuple[np.ndarray[complex], np.ndarray[complex], float]:
1155 """
1156 When calling any evolution method (listed in the
1157 :ref:`See also section <get_driving_pulses_see_also>`) :meth:`get_driving_pulses()`
1158 is executed on the arguements before the evolution method.
1159
1160 Parameters
1161 ----------
1162 *args
1163 The placeholder parameters. The actual parameters will be the same
1164 as ``original_system._pre_processing()``.
1165
1166 Returns
1167 -------
1168 tuple[NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex], NDArray[Shape[:attr:`state_shape`], complex], float]
1169 A tuple of:
1170 1. Control amplitudes
1171 2. Initial state
1172 3. Integrator time step
1173
1174
1175 .. _get_driving_pulses_see_also:
1176
1177 See Also
1178 --------
1179 * :meth:`propagate()`
1180 * :meth:`propagate_collection()`
1181 * :meth:`propagate_all()`
1182 * :meth:`evolved_expectation_value()`
1183 * :meth:`evolved_expectation_value_all()`
1184 * :meth:`gradient()`
1185 """
1186 ...
[docs]
1187 def gradient(self, *args) -> tuple[float, np.ndarray[float]]:
1188 """
1189 Evolves a state vector under the time-dependent Hamiltonian defined by
1190 the control amplitudes and computes the expectation value of a specified
1191 observable with respect to the final state and then computes the
1192 gradient of the final state with respect to the first argument
1193 (``args[0]``) using
1194 :meth:`~py_ste.evolvers.DenseUnitaryEvoler.switching_function()`
1195 from `PySTE <https://PySTE.readthedocs.io>`__.
1196
1197 Parameters
1198 ----------
1199 ``*args[:-1]``
1200 The placeholder parameters. The actual parameters will be the same
1201 as ``original_system._pre_processing()``.
1202 ``args[-1]`` : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
1203 The observable to take the expectation value of.
1204
1205 Returns
1206 -------
1207 tuple[complex, NDArray[Shape[n_parameters], float]]
1208 A tuple of the expectation value and the gradient.
1209
1210 See Also
1211 --------
1212 * :meth:`evolved_expectation_value()`
1213 * :meth:`evolved_expectation_value_all()`
1214 """
1215 ...
1216
[docs]
1217class PulseForm(TransformedSystem):
1218 """
1219 A transformed :class:`qugrad.QuantumSystem` in which :meth:`_pre_processing()`
1220 has been composed with another pre processing function.
1221 """
[docs]
1222 def __init__(self,
1223 original_system: QuantumSystem,
1224 pulse_function: Callable,
1225 append: bool = False):
1226 """
1227 Initialises a new :class:`qugrad.QuantumSystem` in which :meth:`_pre_processing()`
1228 corresponds to running ``pulse_function()`` and piping the output into
1229 ``original_system._pre_processing()``.
1230
1231 Parameters
1232 ----------
1233 original_system : QuantumSystem
1234 The system that was transformed into this system
1235 pulse_function : Callable
1236 The function to compose with :meth:`_pre_processing()`.
1237 append : bool
1238 Whether to prepend
1239 (``original_system._pre_processing(*pulse_function())``)
1240 or append (``pulse_function(*original_system._pre_processing())``)
1241 ``pulse_function``.
1242 """
1243 ...
[docs]
1244 def _pre_processing(self, *args):
1245 """
1246 When calling any evolution method (listed in the
1247 :ref:`See also section <PulseForm_pre_processing_see_also>`
1248 section) :meth:`_pre_processing()` is executed on the arguements before
1249 the control amplitudes are modulated by the frequencies (during
1250 :meth:`_envolope_processing()`) and then finally the modulated control
1251 amplitudes are used by the evolution method.
1252
1253 This is a placeholder for
1254 ``original_system._pre_processing(*pulse_function())``
1255 (or ``pulse_function(*original_system._pre_processing())`` if
1256 attr:`appended` is ``True``).
1257
1258 Parameters
1259 ----------
1260 *args
1261 The placeholder parameters. The actual parameters will be the same
1262 as :attr:`pulse_function` if ``append`` is ``False`` or
1263 ``original_system._pre_processing()`` if ``append`` is ``True``.
1264
1265 Returns
1266 -------
1267 tuple[tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], tf.Tensor[Shape[:attr:`state_shape`], tf.complex128], float, tf.Tensor[Shape[n_time_steps, total_n_channels], tf.complex128], list[int]]
1268 A tuple of
1269 1. The control amplitude envolopes
1270 2. The initial state
1271 3. The integrator time step
1272 4. The frequencies to modulate the control amplitude envolopes with
1273 5. A list of the number of channels for each control Hamiltonian
1274
1275 Warning
1276 -------
1277 The number of channels for each control Hamiltonian must be stored
1278 as a ``list`` and not an ``NDArray`` or a
1279 `TensorFlow <https://www.tensorflow.org>`__ tensor.
1280
1281
1282 .. _PulseForm_pre_processing_see_also:
1283
1284 See Also
1285 --------
1286 * :meth:`propagate()`
1287 * :meth:`propagate_collection()`
1288 * :meth:`propagate_all()`
1289 * :meth:`evolved_expectation_value()`
1290 * :meth:`evolved_expectation_value_all()`
1291 * :meth:`get_driving_pulses()`
1292 * :meth:`gradient()`
1293 """
1294 ...
[docs]
1295 def propagate(self, *args) -> np.ndarray[complex]:
1296 """
1297 Evolves a state vector under the time-dependent Hamiltonian defined by
1298 the control amplitudes using
1299 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate()`
1300 from `PySTE <https://PySTE.readthedocs.io>`__.
1301
1302 Parameters
1303 ----------
1304 *args
1305 The placeholder parameters. The actual parameters will be the same
1306 as :attr:`pulse_function` if ``append`` is ``False`` or
1307 ``original_system._pre_processing()`` if ``append`` is ``True``.
1308
1309 Returns
1310 -------
1311 NDArray[Shape[:attr:`state_shape`], complex]
1312 The final state
1313
1314 See Also
1315 --------
1316 * :meth:`propagate_collection()`
1317 * :meth:`propagate_all()`
1318 """
1319 ...
[docs]
1320 def propagate_collection(self, *args) -> np.ndarray[complex]:
1321 """
1322 Evolves a collection of state vectors under the time-dependent
1323 Hamiltonian defined by the control amplitudes using
1324 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate_collection()`
1325 from `PySTE <https://PySTE.readthedocs.io>`__.
1326
1327 Parameters
1328 ----------
1329 *args
1330 The placeholder parameters. The actual parameters will be the same
1331 as :attr:`pulse_function` if ``append`` is ``False`` or
1332 ``original_system._pre_processing()`` if ``append`` is ``True``.
1333
1334 Returns
1335 -------
1336 NDArray[Shape[n_states, :attr:`state_shape`], complex]
1337 The final state
1338
1339 See Also
1340 --------
1341 * :meth:`propagate()`
1342 * :meth:`propagate_all()`
1343 """
1344 ...
[docs]
1345 def propagate_all(self, *args) -> np.ndarray[complex]:
1346 """
1347 Evolves a state vector under the time-dependent Hamiltonian defined by
1348 the control amplitudes using
1349 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.propagate_all()`
1350 from `PySTE <https://PySTE.readthedocs.io>`__ and returns the state at
1351 each time-step.
1352
1353 Parameters
1354 ----------
1355 *args
1356 The placeholder parameters. The actual parameters will be the same
1357 as :attr:`pulse_function` if ``append`` is ``False`` or
1358 ``original_system._pre_processing()`` if ``append`` is ``True``.
1359
1360 Returns
1361 -------
1362 NDArray[Shape[n_time_steps+1, :attr:`state_shape`], complex]
1363 The state at each integrator time step (including the initial
1364 state).
1365
1366 See Also
1367 --------
1368 * :meth:`propagate()`
1369 * :meth:`propagate_collection()`
1370 """
1371 ...
[docs]
1372 def evolved_expectation_value(self, *args) -> complex:
1373 """
1374 Evolves a state vector under the time-dependent Hamiltonian defined by
1375 the control amplitudes and computes the expectation value of a specified
1376 observable with respect to the final state using
1377 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.evolved_expectation_value()`
1378 from `PySTE <https://PySTE.readthedocs.io>`__.
1379
1380 Parameters
1381 ----------
1382 ``*args[:-1]``
1383 The placeholder parameters. The actual parameters will be the same
1384 as :attr:`pulse_function` if ``append`` is ``False`` or
1385 ``original_system._pre_processing()`` if ``append`` is ``True``.
1386 ``args[-1]`` : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
1387 The observable to take the expectation value of.
1388
1389
1390 Returns
1391 -------
1392 complex
1393 The expectation value.
1394
1395 See Also
1396 --------
1397 * :meth:`evolved_expectation_value_all()`
1398 * :meth:`gradient()`
1399 """
1400 ...
[docs]
1401 def evolved_expectation_value_all(self, *args) -> np.ndarray[complex]:
1402 """
1403 Evolves a state vector under the time-dependent Hamiltonian defined by
1404 the control amplitudes and computes the expectation value of a specified
1405 observable with respect to the state at each time-step using
1406 :meth:`~py_ste.evolvers.DenseUnitaryEvolver.evolved_expectation_value_all()`
1407 from `PySTE <https://PySTE.readthedocs.io>`__.
1408
1409 Parameters
1410 ----------
1411 ``*args[:-1]``
1412 The placeholder parameters. The actual parameters will be the same
1413 as :attr:`pulse_function` if ``append`` is ``False`` or
1414 ``original_system._pre_processing()`` if ``append`` is ``True``.
1415 ``args[-1]`` : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
1416 The observable to take the expectation value of.
1417
1418 Returns
1419 -------
1420 NDArray[Shape[n_time_steps+1], complex]
1421 The state at each integrator time step (including the initial
1422 state).
1423
1424 See Also
1425 --------
1426 * :meth:`evolved_expectation_value()`
1427 * :meth:`gradient()`
1428 """
1429 ...
[docs]
1430 def get_driving_pulses(self, *args) -> tuple[np.ndarray[complex], np.ndarray[complex], float]:
1431 """
1432 When calling any evolution method (listed in the
1433 :ref:`See also section <get_driving_pulses_see_also>`) :meth:`get_driving_pulses()`
1434 is executed on the arguements before the evolution method.
1435
1436 Parameters
1437 ----------
1438 *args
1439 The placeholder parameters. The actual parameters will be the same
1440 as :attr:`pulse_function` if ``append`` is ``False`` or
1441 ``original_system._pre_processing()`` if ``append`` is ``True``.
1442
1443 Returns
1444 -------
1445 tuple[NDArray[Shape[n_time_steps, :attr:`n_ctrl`], complex], NDArray[Shape[:attr:`state_shape`], complex], float]
1446 A tuple of:
1447 1. Control amplitudes
1448 2. Initial state
1449 3. Integrator time step
1450
1451
1452 .. _get_driving_pulses_see_also:
1453
1454 See Also
1455 --------
1456 * :meth:`propagate()`
1457 * :meth:`propagate_collection()`
1458 * :meth:`propagate_all()`
1459 * :meth:`evolved_expectation_value()`
1460 * :meth:`evolved_expectation_value_all()`
1461 * :meth:`gradient()`
1462 """
1463 ...
[docs]
1464 def gradient(self, *args) -> tuple[float, np.ndarray[float]]:
1465 """
1466 Evolves a state vector under the time-dependent Hamiltonian defined by
1467 the control amplitudes and computes the expectation value of a specified
1468 observable with respect to the final state and then computes the
1469 gradient of the final state with respect to the first argument
1470 (``args[0]``) using
1471 :meth:`~py_ste.evolvers.DenseUnitaryEvoler.switching_function()`
1472 from `PySTE <https://PySTE.readthedocs.io>`__.
1473
1474 Parameters
1475 ----------
1476 ``*args[:-1]``
1477 The placeholder parameters. The actual parameters will be the same
1478 as :attr:`pulse_function` if ``append`` is ``False`` or
1479 ``original_system._pre_processing()`` if ``append`` is ``True``.
1480 ``args[-1]`` : NDArray[Shape[:attr:`dim`, :attr:`dim`], complex]
1481 The observable to take the expectation value of.
1482
1483 Returns
1484 -------
1485 tuple[complex, NDArray[Shape[n_parameters], float]]
1486 A tuple of the expectation value and the gradient.
1487
1488 See Also
1489 --------
1490 * :meth:`evolved_expectation_value()`
1491 * :meth:`evolved_expectation_value_all()`
1492 """
1493 ...