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