1"A collection of evolver classes."
2
3import numpy as np
4
5class pybind11_object: pass
6
7def _set_threads(threads: int):
8 """
9 Sets the number of threads to be used during multithreading.
10
11 Parameters
12 ----------
13 threads : int
14 The number of threads to be used during mutlithreading
15
16 Warning
17 -------
18 The number of threads should be less than or equal to the number of
19 physical cores and you should not attempt to make use of hyperthreading
20 by using twice the number of physical cores. PySTE uses Eigen for
21 multithreading which provides the following warning:
22
23 Admonition
24 ----------
25 On most OS it is very important to limit the number of threads to
26 the number of physical cores, otherwise significant slowdowns are
27 expected
28
29 For more details see
30 https://eigen.tuxfamily.org/dox/TopicMultiThreading.html.
31
32 See Also
33 --------
34 :func:`get_threads()`
35 """
36 pass
37
38def _get_threads() -> int:
39 """
40 Gets the number of threads that will be used during multithreading.
41
42 Returns
43 -------
44 int
45 The number of threads that will be used during multithreading.
46
47 See Also
48 --------
49 :func:`set_threads()`
50 """
51 pass
52
53def _unitary_gate_infidelity(gate: np.ndarray,
54 target:np.ndarray
55 ) -> float:
56 r"""
57 Computes the gate infidelity between a gate and a target gate:
58 $$
59 \mathcal I(\texttt{gate}, \texttt{target})
60 \coloneqq 1-\frac{
61 \left|\Tr\left[\texttt{target}^\dagger \cdot \texttt{gate}\right]\right|^2
62 +\texttt{dim}}{\texttt{dim}(\texttt{dim}+1)},
63 $$
64 where $\texttt{dim}$ is the dimension of the Hilbert space the gates act
65 upon.
66
67 Parameters
68 ----------
69 gate : NDArray[Shape[dim, dim], scalar]
70 The gate to compute the infidelity of.
71 target : NDArray[Shape[dim, dim], scalar]
72 The target gate to compute the infidelity with respect to.
73
74 Returns
75 -------
76 float
77 The gate infidelity, $\mathcal I(\texttt{gate}, \texttt{target})$.
78
79 Note
80 ----
81 This function is a wrapper around the C++ function
82 :cpp:func:`Suzuki_Trotter_Evolver::unitary_gate_infidelity()`.
83 """
84 pass
85
[docs]
86class UnitaryEvolver(pybind11_object):
87 """
88 A base class for
89 :class:`DenseUnitaryEvolver`
90 and
91 :class:`SparseUnitaryEvolver`
92 with no implemented functionality.
93
94 The purpose of ``UnitaryEvolver`` is to allow for checks such as::
95
96 isinstance(evolver, evolvers.UnitaryEvolver)
97
98 See Also
99 --------
100 * :class:`DenseUnitaryEvolver`
101 * :class:`SparseUnitaryEvolver`
102
103
104 ---
105 """
106 pass
107
[docs]
108class DenseUnitaryEvolver(UnitaryEvolver):
109 r"""
110 A class to store the diagonalised drift and control Hamiltonians with dense
111 matrices and dynamic values of ``n_ctrl`` and ``dim``. On initialisation the
112 Hamiltonians are diagonalised and the eigenvectors and values stored as
113 dense matrices. This initial diagonalisation may be slow and takes
114 $O(\textrm{dim}^3)$
115 time for a
116 $\textrm{dim}\times \textrm{dim}$
117 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion
118 to be implimented in
119 $O(\textrm{dim}^2)$
120 time with matrix multiplication and only scalar exponentiation opposed to
121 matrix exponentiation which takes
122 $O(\textrm{dim}^3)$
123 time.
124
125 Note
126 ----
127 This class is a Python wrapper around the C++ struct:
128
129 .. code-block:: cpp
130
131 Suzuki_Trotter_Evolver::UnitaryEvolver<Dynamic, Dynamic, DMatrix<Dynamic, Dynamic>>
132
133 from
134 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__.
135
136 Note
137 ----
138 Unlike :class:`DenseUnitaryEvolver_nctrl_dim`, :attr:`n_ctrl` and :attr:`dim`
139 dynamically determined at runtime. :class:`DenseUnitaryEvolver_nctrl_dim` is
140 typically more efficient as the values for :attr:`n_ctrl` and :attr:`dim`
141 are baked in at compile time.
142
143 See Also
144 --------
145 * :class:`DenseUnitaryEvolver_nctrl_dim`
146 * :class:`SparseUnitaryEvolver`
147
148
149 ---
150 """
151
152 n_ctrl: int = -1
153 r"""
154 The number of control Hamiltonians used to compile the C++ backend. A value
155 of ``-1`` implies the value is not precompiled in the C++. This was the
156 value at compile time while :attr:`length` is the value at runtime time.
157 """
158
159 dim: int = -1
160 r"""
161 The dimension of the vector space the Hamiltonians act upon used to compile
162 the C++ backend. A value of ``-1`` implies the value is not precompiled in
163 the C++.
164 """
165
166 dim_x_n_ctrl: int = -1
167 r"""
168 The dimension of rows in each control Hamiltonian multiplied by the
169 number of control Hamiltonians upon used to compile the C++ backend. This is
170 the number of rows for the ``control_hamiltonians`` argument for :meth:`__init__()`. A value
171 of ``-1`` implies the value is not precompiled in the C++.
172
173 Note
174 ----
175 This is a wrapper around the C++ member
176 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`.
177 """
178
179 length: int
180 r"""
181 The number of control Hamiltonians. This is the value at runtime while
182 :attr:`n_ctrl` was the value at compile time. These two values will be equal
183 unless ``n_ctrl==-1``. If ``n_ctrl==-1`` then `length` is the actual number
184 of control Hamiltonians.
185
186 Note
187 ----
188 This is a wrapper around the C++ member
189 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::length`.
190 """
191
192 d0: np.ndarray
193 r"""
194 The eigenvalues,
195 $\operatorname{diag}(D_0)$,
196 of the drift Hamiltonian:
197 $H_0=U_0D_0U_0^\dagger$.
198
199 Note
200 ----
201 This is a wrapper around the C++ member
202 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::d0`.
203
204 See Also
205 --------
206 * :attr:`ds`
207 * :attr:`u0`
208 * :attr:`u0_inverse`
209 """
210
211 ds: list[np.ndarray]
212 r"""
213 The eigenvalues,
214 $\left(\operatorname{diag}(D_i)\right)_{i=1}^{\textrm{length}}$,
215 of the control Hamiltonians:
216 $H_i=U_iD_iU_i^\dagger$
217 for all
218 $i\in\left[\textrm{length}\right]$.
219
220 Note
221 ----
222 This is a wrapper around the C++ member
223 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::ds`.
224
225 See Also
226 --------
227 * :attr:`d0`
228 * :attr:`us_individual`
229 * :attr:`us_inverse_individual`
230 * :attr:`hs`
231 """
232
233 u0: np.ndarray
234 r"""
235 The unitary transformation,
236 $U_0$,
237 that diagonalises the drift Hamiltonian:
238 $H_0=U_0D_0U_0^\dagger$.
239
240 Note
241 ----
242 This is a wrapper around the C++ member
243 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0`.
244
245 See Also
246 --------
247 * :attr:`us_individual`
248 * :attr:`d0`
249 * :attr:`u0_inverse`
250 """
251
252 u0_inverse: np.ndarray
253 r"""
254 The inverse of the unitary transformation,
255 $U_0^\dagger$,
256 that diagonalises the drift Hamiltonian:
257 $H_0=U_0D_0U_0^\dagger$.
258
259 Note
260 ----
261 This is a wrapper around the C++ member
262 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse`.
263
264 See Also
265 --------
266 * :attr:`us_inverse_individual`
267 * :attr:`u0`
268 * :attr:`d0`
269 """
270
271 us: list[np.ndarray]
272 r"""
273 The unitary transformations,
274 $(U_i^\dagger U_{i-1})_{i=1}^{\textrm{length}}$,
275 from the eigen basis of
276 $H_{i-1}$
277 to the eigen basis of
278 $H_i$.
279
280 Note
281 ----
282 This is a wrapper around the C++ member
283 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us`.
284
285 See Also
286 --------
287 * :attr:`us_individual`
288 * :attr:`us_inverse_individual`
289 """
290
291 us_individual: list[np.ndarray]
292 r"""
293 The unitary transformations,
294 $\left(U_i\right)_{i=1}^{\textrm{length}}$,
295 that diagonalise the control Hamiltonians:
296 $H_i=U_iD_iU_i^\dagger$
297 for all
298 $i\in\left[\textrm{length}\right]$.
299
300 Note
301 ----
302 This is a wrapper around the C++ member
303 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_individual`.
304
305 See Also
306 --------
307 * :attr:`us_inverse_individual`
308 * :attr:`us`
309 * :attr:`ds`
310 * :attr:`hs`
311 """
312
313 us_inverse_individual: list[np.ndarray]
314 r"""
315 The inverse of the unitary transformations,
316 $(U_i^\dagger)_{i=1}^{\textrm{length}}$,
317 that diagonalise the control Hamiltonians:
318 $H_i=U_iD_iU_i^\dagger$
319 for all
320 $i\in\left[\textrm{length}\right]$.
321
322 Note
323 ----
324 This is a wrapper around the C++ member
325 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_inverse_individual`.
326
327 See Also
328 --------
329 * :attr:`us_individual`
330 * :attr:`us`
331 * :attr:`ds`
332 * :attr:`hs`
333 """
334
335 hs: list[np.ndarray]
336 r"""
337 The control Hamiltonians:
338 $H_i$
339 for all
340 $i\in\left[\textrm{length}\right]$.
341
342 Note
343 ----
344 This is a wrapper around the C++ member
345 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::hs`.
346
347 See Also
348 --------
349 * :attr:`us_individual`
350 * :attr:`us_inverse_individual`
351 * :attr:`ds`
352 """
353
354 u0_inverse_u_last: list[np.ndarray]
355 r"""
356 The unitary transformation,
357 $U_0^\dagger U_{\textrm{length}}$,
358 from the eigen basis of
359 $H_{\textrm{length}}$
360 to the eigen basis of
361 $H_0$.
362
363 Note
364 ----
365 This is a wrapper around the C++ member
366 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse_u_last`.
367
368 See Also
369 --------
370 * :attr:`us_individual`
371 * :attr:`us`
372 * :attr:`u0_inverse`
373 """
374
[docs]
375 def __init__(self,
376 drift_hamiltonian: np.ndarray[np.complex128],
377 control_hamiltonians: np.ndarray[np.complex128]):
378 r"""
379 Initialises a new unitary evolver with the Hamiltonian
380 $$
381 H(t)=H_0+\sum_{j=1}^{\textrm{length}}a_j(t)H_j,
382 $$
383 where $H_0$ is the drift Hamiltonian and $H_j$ are the control
384 Hamiltonians modulated by control amplitudes $a_j(t)$ which need
385 not be specified during initialisation.
386
387 Parameters
388 ----------
389 drift_hamiltonian : NDArray[Shape[runtime_dim, runtime_dim], complex128]
390 The drift Hamiltonian.
391 control_hamiltonians : NDArray[Shape[runtime_dim * :attr:`length`, runtime_dim], complex128]
392 The control Hamiltonians.
393 """
394 pass
[docs]
395 def propagate(self,
396 ctrl_amp: np.ndarray[np.complex128],
397 state: np.ndarray[np.complex128],
398 dt: float
399 ) -> np.ndarray[np.complex128]:
400 r"""
401 Propagates the state vector using the first-order Suzuki-Trotter
402 expansion. More precisely, a state vector,
403 $\psi(0)$,
404 is evolved under the differential equation
405 $$
406 \dot\psi=-iH\psi
407 $$
408 using the first-order Suzuki-Trotter expansion:
409 $$
410 \begin{align}
411 \psi(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
412 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E\\
413 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
414 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E.
415 \end{align}
416 $$
417 where
418 $a_{nj}\coloneqq a(n\Delta t)$,
419 we set
420 $a_{n0}=1$
421 for notational ease, and the additive error
422 $\mathcal E$
423 is
424 $$
425 \begin{align}
426 \mathcal E&=\mathcal O\left(
427 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
428 \norm{H_j}
429 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
430 \norm{[H_j,H_k]}\right]
431 \right)\\
432 &=\mathcal O\left(
433 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
434 \right)
435 \end{align}
436 $$
437 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
438 $$
439 \begin{align}
440 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
441 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
442 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
443 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
444 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
445 \end{align}
446 $$
447 Note the error is quadratic in $\Delta t$ but linear in $N$.
448 We can also view this as being linear in $\Delta t$ and linear in
449 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem
450 this asymptotic error scaling will not be achieved until the time step
451 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where
452 $\Omega$ is the largest energy or frequency in the system.
453
454 Parameters
455 ----------
456 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
457 $\left(a_{ij}\right)$ The control amplitudes at each time step
458 expressed as an $N\times\textrm{length}$ matrix where the element
459 $a_{ij}$ corresponds to the control amplitude of the $j$th control
460 Hamiltonian at the $i$th time step.
461 state : NDArray[Shape[runtime_dim], complex128]
462 $\left[\psi(0)\right]$ The state vector to propagate.
463 dt : float
464 ($\Delta t$) The time step to propagate by.
465
466 Returns
467 -------
468 NDArray[Shape[runtime_dim], complex128]
469 The propagated state vector, $\psi(N\Delta t)$.
470
471 Note
472 ----
473 This function is a wrapper around the C++ function
474 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate()`.
475
476 See Also
477 --------
478 * :meth:`propagate_collection()`
479 * :meth:`propagate_all()`
480 * :meth:`get_evolution()`
481 """
482 pass
[docs]
483 def propagate_collection(self,
484 ctrl_amp: np.ndarray[np.complex128],
485 states: np.ndarray[np.complex128],
486 dt: float
487 ) -> np.ndarray[np.complex128]:
488 r"""
489 Propagates a collection of state vectors using the first-order
490 Suzuki-Trotter expansion. More precisely, a collection of state vectors,
491 $\left(\psi_k(0)\right)_{k}$,
492 are evolved under the differential equation
493 $$
494 \dot\psi_k=-iH\psi_k
495 $$
496 using the first-order Suzuki-Trotter expansion:
497 $$
498 \begin{align}
499 \psi_k(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
500 e^{-ia_{ij}H_j\Delta t}\psi_k(0)+\mathcal E\\
501 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
502 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi_k(0)+\mathcal E.
503 \end{align}
504 $$
505 where
506 $a_{nj}\coloneqq a(n\Delta t)$,
507 we set
508 $a_{n0}=1$
509 for notational ease, and the addative error
510 $\mathcal E$
511 is
512 $$
513 \begin{align}
514 \mathcal E&=\mathcal O\left(
515 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
516 \norm{H_j}
517 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
518 \norm{[H_j,H_k]}\right]
519 \right)\\
520 &=\mathcal O\left(
521 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
522 \right)
523 \end{align}
524 $$
525 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
526 $$
527 \begin{align}
528 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
529 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
530 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
531 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
532 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
533 \end{align}
534 $$
535 Note the error is quadratic in $\Delta t$ but linear in $N$.
536 We can also view this as being linear in $\Delta t$ and linear in
537 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem
538 this asymptotic error scaling will not be achieved until the time step
539 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where
540 $\Omega$ is the largest energy or frequency in the system.
541
542 Parameters
543 ----------
544 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
545 $\left(a_{ij}\right)$ The control amplitudes at each time step
546 expressed as an $N\times\textrm{length}$ matrix where the element
547 $a_{ij}$ corresponds to the control amplitude of the $j$th control
548 Hamiltonian at the $i$th time step.
549 states : NDArray[Shape[runtime_dim, number_of_states], complex128]
550 $\left[\left(\psi(0)\right)_{k}\right]$ A collection of state
551 vectors to propagate expressed as a matrix with each column
552 corresponding to a state vector.
553 dt : float
554 ($\Delta t$) The time step to propagate by.
555
556 Returns
557 -------
558 NDArray[Shape[runtime_dim, number_of_states], complex128]
559 The propagated state vectors, $\left(\psi_k(N\Delta t)\right)_k$.
560
561 Note
562 ----
563 This function is a wrapper around the C++ function
564 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_collection()`.
565
566 See Also
567 --------
568 * :meth:`propagate()`
569 * :meth:`propagate_all()`
570 * :meth:`get_evolution()`
571 """
572 pass
[docs]
573 def propagate_all(self,
574 ctrl_amp: np.ndarray[np.complex128],
575 state: np.ndarray[np.complex128],
576 dt: float
577 ) -> np.ndarray[np.complex128]:
578 r"""
579 Propagates the state vector using the first-order Suzuki-Trotter
580 expansion and returns the resulting state vector at every time step.
581 More precisely, a state vector,
582 $\psi(0)$,
583 is evolved under the differential equation
584 $$
585 \dot\psi=-iH\psi
586 $$
587 using the first-order Suzuki-Trotter expansion:
588 $$
589 \begin{align}
590 \psi(n\Delta t)&=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}}
591 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E
592 \quad\forall n\in\left[0, N\right]\\
593 &=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}}
594 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E
595 \quad\forall n\in\left[0, N\right].
596 \end{align}
597 $$
598 where
599 $a_{nj}\coloneqq a(n\Delta t)$,
600 we set
601 $a_{n0}=1$
602 for notational ease, and the additive error
603 $\mathcal E$
604 is
605 $$
606 \begin{align}
607 \mathcal E&=\mathcal O\left(
608 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
609 \norm{H_j}
610 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
611 \norm{[H_j,H_k]}\right]
612 \right)\\
613 &=\mathcal O\left(
614 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
615 \right)
616 \end{align}
617 $$
618 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
619 $$
620 \begin{align}
621 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
622 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
623 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
624 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
625 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
626 \end{align}
627 $$
628 Note the error is quadratic in $\Delta t$ but linear in $N$.
629 We can also view this as being linear in $\Delta t$ and linear in
630 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem
631 this asymptotic error scaling will not be achieved until the time step
632 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where
633 $\Omega$ is the largest energy or frequency in the system.
634
635 Parameters
636 ----------
637 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
638 $\left(a_{ij}\right)$ The control amplitudes at each time step
639 expressed as an $N\times\textrm{length}$ matrix where the element
640 $a_{ij}$ corresponds to the control amplitude of the $j$th control
641 Hamiltonian at the $i$th time step.
642 state : NDArray[Shape[runtime_dim], complex128]
643 $\left[\psi(0)\right]$ The state vector to propagate.
644 dt : float
645 ($\Delta t$) The time step to propagate by.
646
647 Returns
648 -------
649 NDArray[Shape[runtime_dim, time_steps + 1], complex128]
650 The propagated state vector at each time step,
651 $\left(\psi(n\Delta t)\right)_{n=0}^N$.
652
653 Note
654 ----
655 This function is a wrapper around the C++ function
656 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_all()`.
657
658 See Also
659 --------
660 * :meth:`propagate()`
661 * :meth:`propagate_collection()`
662 * :meth:`get_evolution()`
663 """
664 pass
[docs]
665 def evolved_expectation_value(self,
666 ctrl_amp: np.ndarray[np.complex128],
667 state: np.ndarray[np.complex128],
668 dt: float,
669 observable: np.ndarray[np.complex128]
670 ) -> complex:
671 r"""
672 Calculates the expectation value with respect to an observable of an
673 evolved state vector evolved under a control Hamiltonian modulated by
674 the control amplitudes. The integration is performed using
675 :meth:`propagate()`.
676
677 Parameters
678 ----------
679 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
680 $\left(a_{ij}\right)$ The control amplitudes at each time step
681 expressed as an $N\times\textrm{length}$ matrix where the element
682 $a_{ij}$ corresponds to the control amplitude of the $j$th control
683 Hamiltonian at the $i$th time step.
684 state : NDArray[Shape[runtime_dim], complex128]
685 $\left[\psi(0)\right]$ The state vector to propagate.
686 dt : float
687 ($\Delta t$) The time step to propagate by.
688 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128]
689 $(\hat O)$ The observable to calculate the expectation value of.
690
691 Returns
692 -------
693 complex
694 The expectation value of the observable, $\langle\hat O\rangle
695 \equiv\psi^\dagger(N\Delta t)\hat O\psi(N\Delta t)$.
696
697 Note
698 ----
699 This function is a wrapper around the C++ function
700 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value()`.
701
702 See Also
703 --------
704 * :meth:`evolved_expectation_value_all()`
705 """
706 pass
[docs]
707 def evolved_expectation_value_all(self,
708 ctrl_amp: np.ndarray[np.complex128],
709 state: np.ndarray[np.complex128],
710 dt: float,
711 observable: np.ndarray[np.complex128]
712 ) -> np.ndarray[np.complex128]:
713 r"""
714 Calculates the expectation values with respect to an observable of a
715 time series of state vectors evolved under a control Hamiltonian
716 modulated by the control amplitudes. The integration is performed using
717 :meth:`propagate_all()`.
718
719 Parameters
720 ----------
721 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
722 $\left(a_{ij}\right)$ The control amplitudes at each time step
723 expressed as an $N\times\textrm{length}$ matrix where the element
724 $a_{ij}$ corresponds to the control amplitude of the $j$th control
725 Hamiltonian at the $i$th time step.
726 state : NDArray[Shape[runtime_dim], complex128]
727 $\left[\psi(0)\right]$ The state vector to propagate.
728 dt : float
729 ($\Delta t$) The time step to propagate by.
730 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128]
731 $(\hat O)$ The observable to calculate the expectation value of.
732
733 Returns
734 -------
735 NDArray[Shape[time_steps + 1], complex128]
736 The expectation value of the observable,
737 $\left(\psi^\dagger(n\Delta t)\hat O\psi(N\Delta t)\right)_{n=0}^N$.
738
739 Note
740 ----
741 This function is a wrapper around the C++ function
742 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value_all()`.
743
744 See Also
745 --------
746 * :meth:`evolved_expectation_value()`
747 """
748 pass
[docs]
749 def evolved_inner_product(self,
750 ctrl_amp: np.ndarray[np.complex128],
751 state: np.ndarray[np.complex128],
752 dt: float,
753 fixed_vector: np.ndarray[np.complex128]
754 ) -> complex:
755 r"""
756 Calculates the real inner product of an evolved state vector with a
757 fixed vector. The evolved state vector is evolved under a control
758 Hamiltonian modulated by the control amplitudes. The integration is
759 performed using :meth:`propagate()`.
760
761 Parameters
762 ----------
763 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
764 $\left(a_{ij}\right)$ The control amplitudes at each time step
765 expressed as an $N\times\textrm{length}$ matrix where the element
766 $a_{ij}$ corresponds to the control amplitude of the $j$th control
767 Hamiltonian at the $i$th time step.
768 state : NDArray[Shape[runtime_dim], complex128]
769 $\left[\psi(0)\right]$ The state vector to propagate.
770 dt : float
771 ($\Delta t$) The time step to propagate by.
772 fixed_vector : NDArray[Shape[runtime_dim], complex128]
773 $(\xi)$ The fixed vector to calculate the inner product with.
774
775 Returns
776 -------
777 complex
778 The inner product of the evolved state vector with the fixed vector,
779 $\sum_{i=1}^\texttt{dim}\xi_i\psi_i(N\Delta t)$.
780
781 Note
782 ----
783 This function is a wrapper around the C++ function
784 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_inner_product()`.
785
786 See Also
787 --------
788 * :meth:`evolved_inner_product_all()`
789 """
790 pass
[docs]
791 def evolved_inner_product_all(self,
792 ctrl_amp: np.ndarray[np.complex128],
793 state: np.ndarray[np.complex128],
794 dt: float,
795 fixed_vector: np.ndarray[np.complex128]
796 ) -> np.ndarray[np.complex128]:
797 r"""
798 Calculates the real inner products of a time series of evolved state
799 vectors with a fixed vector. The evolved state vector is evolved under a
800 control Hamiltonian modulated by the control amplitudes. The integration
801 is performed using :meth:`propagate_all()``.
802
803 Parameters
804 ----------
805 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
806 $\left(a_{ij}\right)$ The control amplitudes at each time step
807 expressed as an $N\times\textrm{length}$ matrix where the element
808 $a_{ij}$ corresponds to the control amplitude of the $j$th control
809 Hamiltonian at the $i$th time step.
810 state : NDArray[Shape[runtime_dim], complex128]
811 $\left[\psi(0)\right]$ The state vector to propagate.
812 dt : float
813 ($\Delta t$) The time step to propagate by.
814 fixed_vector : NDArray[Shape[runtime_dim], complex128]
815 $(\xi)$ The fixed vector to calculate the inner product with.
816
817 Returns
818 -------
819 NDArray[Shape[time_steps + 1], complex128]
820 The inner products of the evolved state vectors with the fixed
821 vector,
822 $\left(
823 \sum_{i=1}^\texttt{dim}\xi_i\psi_i(n\Delta t)\right)_{n=0}^N$.
824
825 Note
826 ----
827 This function is a wrapper around the C++ function
828 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_inner_product_all()`.
829
830 See Also
831 --------
832 * :meth:`evolved_inner_product()`.
833 """
834 pass
[docs]
835 def switching_function(self,
836 ctrl_amp: np.ndarray[np.complex128],
837 state: np.ndarray[np.complex128],
838 dt: float,
839 cost: np.ndarray[np.complex128]
840 ) -> tuple[complex, np.ndarray[np.float64]]:
841 r"""
842 Calculates the switching function for a Mayer problem with an
843 expectation value as the cost function. More precisely if the cost
844 function is
845 $$
846 J\left[\vec a(t)\right]\coloneqq\langle\hat O\rangle
847 \equiv\psi^\dagger[\vec a(t);T]
848 \hat O\psi[\vec a(t);T],
849 $$
850 where $T=N\Delta t$, then the switching function is
851 $$
852 \phi_j(t)\coloneqq\frac{\delta J}{\delta a_j(t)}
853 =2\operatorname{Im}\left(\psi^\dagger[\vec a(t);T]
854 \hat OU(t\to T)H_j\psi[\vec a(t);t]\right).
855 $$
856 using the first-order Suzuki-Trotter expansion we can express the
857 switching function as
858 $$
859 \begin{align}
860 &\phi_j(n\Delta t)=\frac{1}{\Delta t}\pdv{J}{a_{nj}}\\
861 &=\!2\operatorname{Im}\!\left(\psi^\dagger(T)
862 \hat O\!\!\left[\prod_{i>n}^N\prod_{k=1}^{\textrm{length}}
863 e^{-ia_{ik}H_k\Delta t}\right]\!\!\!
864 \left[\prod_{k=j}^{\textrm{length}}
865 e^{-ia_{nk}H_k\Delta t}\right]\!H_j\!\!
866 \left[\prod_{k=0}^{j-1}
867 e^{-ia_{nk}H_k\Delta t}\right]
868 \!\psi(\left[n-1\right]\Delta t)\right),
869 \end{align}
870 $$
871 where for numerical efficiency we replace
872 $e^{-ia_{ik}H_k\Delta t}$
873 with
874 $U_ke^{-ia_{ik}D_k\Delta t}U_k^\dagger$
875 as in :meth:`propagate()`.
876
877 Parameters
878 ----------
879 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
880 $\left(a_{ij}\right)$ The control amplitudes at each time step
881 expressed as an $N\times\textrm{length}$ matrix where the element
882 $a_{ij}$ corresponds to the control amplitude of the $j$th control
883 Hamiltonian at the $i$th time step.
884 state : NDArray[Shape[runtime_dim], complex128]
885 $\left[\psi(0)\right]$ The initial state vector.
886 dt : float
887 ($\Delta t$) The time step.
888 cost : NDArray[Shape[runtime_dim, runtime_dim], complex128]
889 $(\hat O)$ The observable to calculate the expectation value of.
890
891 Returns
892 -------
893 tuple[complex, NDArray[Shape[time_steps, :attr:`length`], float64]]
894 The expectation value, $\psi^\dagger(T)\hat O\psi(T)$, and
895 the switching function,
896 $\phi_j(n\Delta t)$
897 for all
898 $j\in\left[1,\textrm{length}\right]$
899 and
900 $n\in\left[1,N\right]$.
901
902 See Also
903 --------
904 * :meth:`gate_switching_function()`.
905
906 Note
907 ----
908 This function is a wrapper around the C++ function
909 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::switching_function()`.
910 """
911 pass
[docs]
912 def get_evolution(self,
913 ctrl_amp: np.ndarray[np.complex128],
914 dt: float
915 ) -> np.ndarray[np.complex128]:
916 r"""
917 Computes the unitary corresponding to the evolution under the
918 differential equation
919 $$
920 \dot U=-iHU.
921 $$
922 The computation is performed using the first-order Suzuki-Trotter
923 expansion:
924 $$
925 \begin{align}
926 U(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
927 e^{-ia_{ij}H_j\Delta t}+\mathcal E\\
928 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
929 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger+\mathcal E.
930 \end{align}
931 $$
932 where
933 $a_{nj}\coloneqq a(n\Delta t)$,
934 we set
935 $a_{n0}=1$
936 for notational ease, and the additive error
937 $\mathcal E$
938 is
939 $$
940 \begin{align}
941 \mathcal E&=\mathcal O\left(
942 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
943 \norm{H_j}
944 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
945 \norm{[H_j,H_k]}\right]
946 \right)\\
947 &=\mathcal O\left(
948 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
949 \right)
950 \end{align}
951 $$
952 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
953 $$
954 \begin{align}
955 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
956 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
957 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
958 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
959 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
960 \end{align}
961 $$
962 Note the error is quadratic in $\Delta t$ but linear in $N$. We can also
963 view this as being linear in $\Delta t$ and linear in total evolution
964 time $N\Delta t$. Additionally, by Nyquist's theorem this asymptotic
965 error scaling will not be achieved until the time step $\Delta t$ is
966 smaller than $\frac{1}{2\Omega}$ where $\Omega$ is the largest energy or
967 frequency in the system.
968
969 Parameters
970 ----------
971 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
972 $\left(a_{ij}\right)$ The control amplitudes at each time step
973 expressed as an $N\times\textrm{length}$ matrix where the element
974 $a_{ij}$ corresponds to the control amplitude of the $j$th control
975 Hamiltonian at the $i$th time step.
976 dt : float
977 ($\Delta t$) The time step to propagate by.
978
979 Returns
980 -------
981 NDArray[Shape[runtime_dim, runtime_dim], complex128]
982 The unitary corresponding to the evolution,
983 $U(N\Delta t)$.
984
985 Note
986 ----
987 This function is a wrapper around the C++ function
988 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::get_evolution()`.
989
990 See Also
991 --------
992 * :meth:`propagate()`
993 * :meth:`propagate_all()`
994 * :meth:`propagate_collection()`
995 """
996 pass
[docs]
997 def evolved_gate_infidelity(self,
998 ctrl_amp: np.ndarray[np.complex128],
999 dt: float,
1000 target: np.ndarray[np.complex128]
1001 ) -> float:
1002 r"""
1003 Calculates the gate infidelity with respect to a target gate of the gate
1004 produced by the control Hamiltonian modulated by the control amplitudes.
1005 The integration is performed using
1006 :meth:`get_evolution()`.
1007
1008 Parameters
1009 ----------
1010 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1011 $\left(a_{ij}\right)$ The control amplitudes at each time step
1012 expressed as an $N\times\textrm{length}$ matrix where the element
1013 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1014 Hamiltonian at the $i$th time step.
1015 dt : float
1016 ($\Delta t$) The time step to propagate by.
1017 target : NDArray[Shape[runtime_dim, runtime_dim], complex128]
1018 The target gate to calculate the infidelity with respect to.
1019
1020 Returns
1021 -------
1022 float
1023 The gate infidelity with respect to the target gate:
1024 $$
1025 \mathcal I(U(N\Delta t), \texttt{target})
1026 \coloneqq 1-\frac{
1027 \left|\Tr\left[
1028 \texttt{target}^\dagger\cdot U(N\Delta t)\right]\right|^2
1029 +\texttt{dim}}{\texttt{dim}(\texttt{dim}+1)}.
1030 $$
1031
1032 Note
1033 ----
1034 This function is a wrapper around the C++ function
1035 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_gate_infidelity()`.
1036
1037 See Also
1038 --------
1039 * :func:`unitary_gate_infidelity()`.
1040 """
1041 pass
[docs]
1042 def gate_switching_function(self,
1043 ctrl_amp: np.ndarray[np.complex128],
1044 dt: float,
1045 target: np.ndarray[np.complex128]
1046 ) -> tuple[float, np.ndarray[np.float64]]:
1047 r"""
1048 Calculates the switching function for a Mayer problem with the gate
1049 infidelity as the cost function. More precisely if the cost function is
1050 $$
1051 J\left[\vec a(t)\right]
1052 \coloneqq\mathcal I(U\left[\vec a(t); T\right], \texttt{target})
1053 \coloneqq 1-\frac{\left|\Tr\left[\texttt{target}^\dagger
1054 \cdot U\left[\vec a(t); T\right]\right]\right|^2
1055 +\texttt{dim}}{\texttt{dim}(\texttt{dim}+1)}.
1056 $$
1057 where $T=N\Delta t$, then the switching function is
1058 $$
1059 \begin{align}
1060 &\phi_j(t)\coloneqq\frac{\delta J}{\delta a_j(t)}\\
1061 &=\frac{2}{\texttt{dim}(\texttt{dim}+1)}\operatorname{Im}\left(
1062 \Tr\left[U^\dagger(N\Delta t)\cdot\texttt{target}\right]
1063 \Tr\left[\texttt{target}^\dagger
1064 \cdot U(t\to T)H_j U[\vec a(t);t]\right]\right).
1065 \end{align}
1066 $$
1067 Using the first-order Suzuki-Trotter expansion we can express the
1068 switching function as
1069 $$
1070 \begin{align}
1071 &\phi_j(n\Delta t)=\frac{1}{\Delta t}\pdv{J}{a_{nj}}\\
1072 &=\!\frac{2}{\texttt{dim}(\texttt{dim}+1)}\operatorname{Im}
1073 \!\left(
1074 \Tr\!\left[U^\dagger(N\Delta t)\cdot\texttt{target}\right]
1075 \vphantom{[\prod_{k=j}^{\textrm{length}}}\right.\\
1076 &\left.\cdot\Tr\!\left[\texttt{target}^\dagger\!\cdot\!
1077 \left[\prod_{i>n}^N\prod_{k=1}^{\textrm{length}}
1078 e^{-ia_{ik}H_k\Delta t}\right]\!\!\!
1079 \left[\prod_{k=j}^{\textrm{length}}
1080 e^{-ia_{nk}H_k\Delta t}\right]\!H_j\!\!
1081 \left[\prod_{k=0}^{j-1}
1082 e^{-ia_{nk}H_k\Delta t}\right]
1083 \! U(\left[n-1\right]\Delta t)\right]\right),
1084 \end{align}
1085 $$
1086 where for numerical efficiency we replace
1087 $e^{-ia_{ik}H_k\Delta t}$
1088 with
1089 $U_ke^{-ia_{ik}D_k\Delta t}U_k^\dagger$
1090 as in :meth:`get_evolution()`.
1091
1092 Parameters
1093 ----------
1094 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1095 $\left(a_{ij}\right)$ The control amplitudes at each time step
1096 expressed as an $N\times\textrm{length}$ matrix where the element
1097 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1098 Hamiltonian at the $i$th time step.
1099 dt : float
1100 ($\Delta t$) The time step to propagate by.
1101 target : NDArray[Shape[runtime_dim, runtime_dim], complex128]
1102 The target gate to calculate the infidelity with respect to.
1103
1104 Returns
1105 -------
1106 tuple[float, NDArray[Shape[time_steps, :attr:`length`], float64]]
1107 The gate infidelity,
1108 $I(U\left[\vec a(t); T\right], \texttt{target})$ and
1109 the switching function,
1110 $\phi_j(n\Delta t)$
1111 for all
1112 $j\in\left[1,\textrm{length}\right]$
1113 and
1114 $n\in\left[1,N\right]$.
1115
1116 See Also
1117 --------
1118 * :meth:`switching_function()`.
1119 """
1120 pass
[docs]
1121class SparseUnitaryEvolver(UnitaryEvolver):
1122 r"""
1123 A class to store the diagonalised drift and control Hamiltonians with sparse
1124 matrices and dynamic values of ``n_ctrl`` and ``dim``. On initialisation the
1125 Hamiltonians are diagonalised and the eigenvectors and values stored as
1126 sparse and dense matrices, respectively. This initial diagonalisation may be
1127 slow and takes
1128 $O(\textrm{dim}^3)$
1129 time for a
1130 $\textrm{dim}\times \textrm{dim}$
1131 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion
1132 to be implimented with sparse matrix multiplication and only scalar
1133 exponentiation opposed to matrix exponentiation.
1134
1135 Note
1136 ----
1137 This class is a Python wrapper around the C++ struct:
1138
1139 .. code-block:: cpp
1140
1141 Suzuki_Trotter_Evolver::UnitaryEvolver<Dynamic, Dynamic, SMatrix<Dynamic, Dynamic>>
1142
1143 from
1144 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__.
1145
1146 Note
1147 ----
1148 Unlike :class:`SparseUnitaryEvolver_nctrl_dim`, :attr:`n_ctrl` and
1149 :attr:`dim` dynamically determined at runtime.
1150 :class:`SparseUnitaryEvolver_nctrl_dim` is typically more efficient as the
1151 values for :attr:`n_ctrl` and :attr:`dim` are baked in at compile time.
1152
1153 See Also
1154 --------
1155 * :class:`SparseUnitaryEvolver_nctrl_dim`
1156 * :class:`DenseUnitaryEvolver`
1157
1158
1159 ---
1160 """
1161
1162 n_ctrl: int = -1
1163 r"""
1164 The number of control Hamiltonians used to compile the C++ backend. A value
1165 of ``-1`` implies the value is not precompiled in the C++. This was the value
1166 at compile time while :attr:`length` is the value at runtime time.
1167 """
1168
1169 dim: int = -1
1170 r"""
1171 The dimension of the vector space the Hamiltonians act upon used to compile
1172 the C++ backend. A value of ``-1`` implies the value is not precompiled in
1173 the C++.
1174 """
1175
1176 dim_x_n_ctrl: int = -1
1177 r"""
1178 The dimension of rows in each control Hamiltonian multiplied by the
1179 number of control Hamiltonians upon used to compile the C++ backend. This is
1180 the number of rows for the ``control_hamiltonians`` argument for
1181 :meth:`__init__()`. A value of ``-1`` implies the value is not precompiled
1182 in the C++.
1183
1184 Note
1185 ----
1186 This is a wrapper around the C++ member
1187 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`.
1188 """
1189
1190 length: int
1191 r"""
1192 The number of control Hamiltonians. This is the value at runtime while
1193 :attr:`n_ctrl` was the value at compile time. These two values will be equal
1194 unless ``n_ctrl==-1``. If ``n_ctrl==-1`` then `length` is the actual number
1195 of control Hamiltonians.
1196
1197 Note
1198 ----
1199 This is a wrapper around the C++ member
1200 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::length`.
1201 """
1202
1203 d0: np.ndarray
1204 r"""
1205 The eigenvalues,
1206 $\operatorname{diag}(D_0)$,
1207 of the drift Hamiltonian:
1208 $H_0=U_0D_0U_0^\dagger$.
1209
1210 Note
1211 ----
1212 This is a wrapper around the C++ member
1213 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::d0`.
1214
1215 See Also
1216 --------
1217 * :attr:`ds`
1218 * :attr:`u0`
1219 * :attr:`u0_inverse`
1220 """
1221
1222 ds: list[np.ndarray]
1223 r"""
1224 The eigenvalues,
1225 $\left(\operatorname{diag}(D_i)\right)_{i=1}^{\textrm{length}}$,
1226 of the control Hamiltonians:
1227 $H_i=U_iD_iU_i^\dagger$
1228 for all
1229 $i\in\left[\textrm{length}\right]$.
1230
1231 Note
1232 ----
1233 This is a wrapper around the C++ member
1234 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::ds`.
1235
1236 See Also
1237 --------
1238 * :attr:`d0`
1239 * :attr:`us_individual`
1240 * :attr:`us_inverse_individual`
1241 * :attr:`hs`
1242 """
1243
1244 u0: np.ndarray
1245 r"""
1246 The unitary transformation,
1247 $U_0$,
1248 that diagonalises the drift Hamiltonian:
1249 $H_0=U_0D_0U_0^\dagger$.
1250
1251 Note
1252 ----
1253 This is a wrapper around the C++ member
1254 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0`.
1255
1256 See Also
1257 --------
1258 * :attr:`us_individual`
1259 * :attr:`d0`
1260 * :attr:`u0_inverse`
1261 """
1262
1263 u0_inverse: np.ndarray
1264 r"""
1265 The inverse of the unitary transformation,
1266 $U_0^\dagger$,
1267 that diagonalises the drift Hamiltonian:
1268 $H_0=U_0D_0U_0^\dagger$.
1269
1270 Note
1271 ----
1272 This is a wrapper around the C++ member
1273 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse`.
1274
1275 See Also
1276 --------
1277 * :attr:`us_inverse_individual`
1278 * :attr:`u0`
1279 * :attr:`d0`
1280 """
1281
1282 us: list[np.ndarray]
1283 r"""
1284 The unitary transformations,
1285 $(U_i^\dagger U_{i-1})_{i=1}^{\textrm{length}}$,
1286 from the eigen basis of
1287 $H_{i-1}$
1288 to the eigen basis of
1289 $H_i$.
1290
1291 Note
1292 ----
1293 This is a wrapper around the C++ member
1294 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us`.
1295
1296 See Also
1297 --------
1298 * :attr:`us_individual`
1299 * :attr:`us_inverse_individual`
1300 """
1301
1302 us_individual: list[np.ndarray]
1303 r"""
1304 The unitary transformations,
1305 $\left(U_i\right)_{i=1}^{\textrm{length}}$,
1306 that diagonalise the control Hamiltonians:
1307 $H_i=U_iD_iU_i^\dagger$
1308 for all
1309 $i\in\left[\textrm{length}\right]$.
1310
1311 Note
1312 ----
1313 This is a wrapper around the C++ member
1314 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_individual`.
1315
1316 See Also
1317 --------
1318 * :attr:`us_inverse_individual`
1319 * :attr:`us`
1320 * :attr:`ds`
1321 * :attr:`hs`
1322 """
1323
1324 us_inverse_individual: list[np.ndarray]
1325 r"""
1326 The inverse of the unitary transformations,
1327 $(U_i^\dagger)_{i=1}^{\textrm{length}}$,
1328 that diagonalise the control Hamiltonians:
1329 $H_i=U_iD_iU_i^\dagger$
1330 for all
1331 $i\in\left[\textrm{length}\right]$.
1332
1333 Note
1334 ----
1335 This is a wrapper around the C++ member
1336 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_inverse_individual`.
1337
1338 See Also
1339 --------
1340 * :attr:`us_individual`
1341 * :attr:`us`
1342 * :attr:`ds`
1343 * :attr:`hs`
1344 """
1345
1346 hs: list[np.ndarray]
1347 r"""
1348 The control Hamiltonians:
1349 $H_i$
1350 for all
1351 $i\in\left[\textrm{length}\right]$.
1352
1353 Note
1354 ----
1355 This is a wrapper around the C++ member
1356 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::hs`.
1357
1358 See Also
1359 --------
1360 * :attr:`us_individual`
1361 * :attr:`us_inverse_individual`
1362 * :attr:`ds`
1363 """
1364
1365 u0_inverse_u_last: list[np.ndarray]
1366 r"""
1367 The unitary transformation,
1368 $U_0^\dagger U_{\textrm{length}}$,
1369 from the eigen basis of
1370 $H_{\textrm{length}}$
1371 to the eigen basis of
1372 $H_0$.
1373
1374 Note
1375 ----
1376 This is a wrapper around the C++ member
1377 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse_u_last`.
1378
1379 See Also
1380 --------
1381 * :attr:`us_individual`
1382 * :attr:`us`
1383 * :attr:`u0_inverse`
1384 """
1385
[docs]
1386 def __init__(self,
1387 drift_hamiltonian: np.ndarray[np.complex128],
1388 control_hamiltonians: np.ndarray[np.complex128]):
1389 r"""
1390 Initialises a new unitary evolver with the Hamiltonian
1391 $$
1392 H(t)=H_0+\sum_{j=1}^{\textrm{length}}a_j(t)H_j,
1393 $$
1394 where $H_0$ is the drift Hamiltonian and $H_j$ are the control
1395 Hamiltonians modulated by control amplitudes $a_j(t)$ which need
1396 not be specified during initialisation.
1397
1398 Parameters
1399 ----------
1400 drift_hamiltonian : NDArray[Shape[runtime_dim, runtime_dim], complex128]
1401 The drift Hamiltonian.
1402 control_hamiltonians : NDArray[Shape[runtime_dim * :attr:`length`, runtime_dim], complex128]
1403 The control Hamiltonians.
1404 """
1405 pass
[docs]
1406 def propagate(self,
1407 ctrl_amp: np.ndarray[np.complex128],
1408 state: np.ndarray[np.complex128],
1409 dt: float
1410 ) -> np.ndarray[np.complex128]:
1411 r"""
1412 Propagates the state vector using the first-order Suzuki-Trotter
1413 expansion. More precisely, a state vector,
1414 $\psi(0)$,
1415 is evolved under the differential equation
1416 $$
1417 \dot\psi=-iH\psi
1418 $$
1419 using the first-order Suzuki-Trotter expansion:
1420 $$
1421 \begin{align}
1422 \psi(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
1423 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E\\
1424 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
1425 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E.
1426 \end{align}
1427 $$
1428 where
1429 $a_{nj}\coloneqq a(n\Delta t)$,
1430 we set
1431 $a_{n0}=1$
1432 for notational ease, and the additive error
1433 $\mathcal E$
1434 is
1435 $$
1436 \begin{align}
1437 \mathcal E&=\mathcal O\left(
1438 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
1439 \norm{H_j}
1440 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
1441 \norm{[H_j,H_k]}\right]
1442 \right)\\
1443 &=\mathcal O\left(
1444 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
1445 \right)
1446 \end{align}
1447 $$
1448 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
1449 $$
1450 \begin{align}
1451 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1452 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
1453 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1454 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
1455 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
1456 \end{align}
1457 $$
1458 Note the error is quadratic in $\Delta t$ but linear in $N$.
1459 We can also view this as being linear in $\Delta t$ and linear in
1460 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem
1461 this asymptotic error scaling will not be achieved until the time step
1462 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where
1463 $\Omega$ is the largest energy or frequency in the system.
1464
1465 Parameters
1466 ----------
1467 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1468 $\left(a_{ij}\right)$ The control amplitudes at each time step
1469 expressed as an $N\times\textrm{length}$ matrix where the element
1470 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1471 Hamiltonian at the $i$th time step.
1472 state : NDArray[Shape[runtime_dim], complex128]
1473 $\left[\psi(0)\right]$ The state vector to propagate.
1474 dt : float
1475 ($\Delta t$) The time step to propagate by.
1476
1477 Returns
1478 -------
1479 NDArray[Shape[runtime_dim], complex128]
1480 The propagated state vector, $\psi(N\Delta t)$.
1481
1482 Note
1483 ----
1484 This function is a wrapper around the C++ function
1485 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate()`.
1486
1487 See Also
1488 --------
1489 * :meth:`propagate_collection()`
1490 * :meth:`propagate_all()`
1491 * :meth:`get_evolution()`
1492 """
1493 pass
[docs]
1494 def propagate_collection(self,
1495 ctrl_amp: np.ndarray[np.complex128],
1496 states: np.ndarray[np.complex128],
1497 dt: float
1498 ) -> np.ndarray[np.complex128]:
1499 r"""
1500 Propagates a collection of state vectors using the first-order
1501 Suzuki-Trotter expansion. More precisely, a collection of state vectors,
1502 $\left(\psi_k(0)\right)_{k}$,
1503 are evolved under the differential equation
1504 $$
1505 \dot\psi_k=-iH\psi_k
1506 $$
1507 using the first-order Suzuki-Trotter expansion:
1508 $$
1509 \begin{align}
1510 \psi_k(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
1511 e^{-ia_{ij}H_j\Delta t}\psi_k(0)+\mathcal E\\
1512 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
1513 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi_k(0)+\mathcal E.
1514 \end{align}
1515 $$
1516 where
1517 $a_{nj}\coloneqq a(n\Delta t)$,
1518 we set
1519 $a_{n0}=1$
1520 for notational ease, and the addative error
1521 $\mathcal E$
1522 is
1523 $$
1524 \begin{align}
1525 \mathcal E&=\mathcal O\left(
1526 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
1527 \norm{H_j}
1528 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
1529 \norm{[H_j,H_k]}\right]
1530 \right)\\
1531 &=\mathcal O\left(
1532 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
1533 \right)
1534 \end{align}
1535 $$
1536 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
1537 $$
1538 \begin{align}
1539 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1540 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
1541 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1542 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
1543 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
1544 \end{align}
1545 $$
1546 Note the error is quadratic in $\Delta t$ but linear in $N$.
1547 We can also view this as being linear in $\Delta t$ and linear in
1548 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem
1549 this asymptotic error scaling will not be achieved until the time step
1550 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where
1551 $\Omega$ is the largest energy or frequency in the system.
1552
1553 Parameters
1554 ----------
1555 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1556 $\left(a_{ij}\right)$ The control amplitudes at each time step
1557 expressed as an $N\times\textrm{length}$ matrix where the element
1558 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1559 Hamiltonian at the $i$th time step.
1560 states : NDArray[Shape[runtime_dim, number_of_states], complex128]
1561 $\left[\left(\psi(0)\right)_{k}\right]$ A collection of state
1562 vectors to propagate expressed as a matrix with each column
1563 corresponding to a state vector.
1564 dt : float
1565 ($\Delta t$) The time step to propagate by.
1566
1567 Returns
1568 -------
1569 NDArray[Shape[runtime_dim, number_of_states], complex128]
1570 The propagated state vectors, $\left(\psi_k(N\Delta t)\right)_k$.
1571
1572 Note
1573 ----
1574 This function is a wrapper around the C++ function
1575 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_collection()`.
1576
1577 See Also
1578 --------
1579 * :meth:`propagate()`
1580 * :meth:`propagate_all()`
1581 * :meth:`get_evolution()`
1582 """
1583 pass
[docs]
1584 def propagate_all(self,
1585 ctrl_amp: np.ndarray[np.complex128],
1586 state: np.ndarray[np.complex128],
1587 dt: float
1588 ) -> np.ndarray[np.complex128]:
1589 r"""
1590 Propagates the state vector using the first-order Suzuki-Trotter
1591 expansion and returns the resulting state vector at every time step.
1592 More precisely, a state vector,
1593 $\psi(0)$,
1594 is evolved under the differential equation
1595 $$
1596 \dot\psi=-iH\psi
1597 $$
1598 using the first-order Suzuki-Trotter expansion:
1599 $$
1600 \begin{align}
1601 \psi(n\Delta t)&=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}}
1602 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E
1603 \quad\forall n\in\left[0, N\right]\\
1604 &=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}}
1605 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E
1606 \quad\forall n\in\left[0, N\right].
1607 \end{align}
1608 $$
1609 where
1610 $a_{nj}\coloneqq a(n\Delta t)$,
1611 we set
1612 $a_{n0}=1$
1613 for notational ease, and the additive error
1614 $\mathcal E$
1615 is
1616 $$
1617 \begin{align}
1618 \mathcal E&=\mathcal O\left(
1619 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
1620 \norm{H_j}
1621 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
1622 \norm{[H_j,H_k]}\right]
1623 \right)\\
1624 &=\mathcal O\left(
1625 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
1626 \right)
1627 \end{align}
1628 $$
1629 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
1630 $$
1631 \begin{align}
1632 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1633 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
1634 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1635 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
1636 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
1637 \end{align}
1638 $$
1639 Note the error is quadratic in $\Delta t$ but linear in $N$.
1640 We can also view this as being linear in $\Delta t$ and linear in
1641 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem
1642 this asymptotic error scaling will not be achieved until the time step
1643 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where
1644 $\Omega$ is the largest energy or frequency in the system.
1645
1646 Parameters
1647 ----------
1648 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1649 $\left(a_{ij}\right)$ The control amplitudes at each time step
1650 expressed as an $N\times\textrm{length}$ matrix where the element
1651 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1652 Hamiltonian at the $i$th time step.
1653 state : NDArray[Shape[runtime_dim], complex128]
1654 $\left[\psi(0)\right]$ The state vector to propagate.
1655 dt : float
1656 ($\Delta t$) The time step to propagate by.
1657
1658 Returns
1659 -------
1660 NDArray[Shape[runtime_dim, time_steps + 1], complex128]
1661 The propagated state vector at each time step,
1662 $\left(\psi(n\Delta t)\right)_{n=0}^N$.
1663
1664 Note
1665 ----
1666 This function is a wrapper around the C++ function
1667 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_all()`.
1668
1669 See Also
1670 --------
1671 * :meth:`propagate()`
1672 * :meth:`propagate_collection()`
1673 * :meth:`get_evolution()`
1674 """
1675 pass
[docs]
1676 def evolved_expectation_value(self,
1677 ctrl_amp: np.ndarray[np.complex128],
1678 state: np.ndarray[np.complex128],
1679 dt: float,
1680 observable: np.ndarray[np.complex128]
1681 ) -> complex:
1682 r"""
1683 Calculates the expectation value with respect to an observable of an
1684 evolved state vector evolved under a control Hamiltonian modulated by
1685 the control amplitudes. The integration is performed using
1686 :meth:`propagate()`.
1687
1688 Parameters
1689 ----------
1690 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1691 $\left(a_{ij}\right)$ The control amplitudes at each time step
1692 expressed as an $N\times\textrm{length}$ matrix where the element
1693 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1694 Hamiltonian at the $i$th time step.
1695 state : NDArray[Shape[runtime_dim], complex128]
1696 $\left[\psi(0)\right]$ The state vector to propagate.
1697 dt : float
1698 ($\Delta t$) The time step to propagate by.
1699 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128]
1700 $(\hat O)$ The observable to calculate the expectation value of.
1701
1702 Returns
1703 -------
1704 complex
1705 The expectation value of the observable, $\langle\hat O\rangle
1706 \equiv\psi^\dagger(N\Delta t)\hat O\psi(N\Delta t)$.
1707
1708 Note
1709 ----
1710 This function is a wrapper around the C++ function
1711 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value()`.
1712
1713 See Also
1714 --------
1715 * :meth:`evolved_expectation_value_all()`
1716 """
1717 pass
[docs]
1718 def evolved_expectation_value_all(self,
1719 ctrl_amp: np.ndarray[np.complex128],
1720 state: np.ndarray[np.complex128],
1721 dt: float,
1722 observable: np.ndarray[np.complex128]
1723 ) -> np.ndarray[np.complex128]:
1724 r"""
1725 Calculates the expectation values with respect to an observable of a
1726 time series of state vectors evolved under a control Hamiltonian
1727 modulated by the control amplitudes. The integration is performed using
1728 :meth:`propagate_all()`.
1729
1730 Parameters
1731 ----------
1732 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1733 $\left(a_{ij}\right)$ The control amplitudes at each time step
1734 expressed as an $N\times\textrm{length}$ matrix where the element
1735 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1736 Hamiltonian at the $i$th time step.
1737 state : NDArray[Shape[runtime_dim], complex128]
1738 $\left[\psi(0)\right]$ The state vector to propagate.
1739 dt : float
1740 ($\Delta t$) The time step to propagate by.
1741 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128]
1742 $(\hat O)$ The observable to calculate the expectation value of.
1743
1744 Returns
1745 -------
1746 NDArray[Shape[time_steps + 1], complex128]
1747 The expectation value of the observable,
1748 $\left(\psi^\dagger(n\Delta t)\hat O\psi(N\Delta t)\right)_{n=0}^N$.
1749
1750 Note
1751 ----
1752 This function is a wrapper around the C++ function
1753 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value_all()`.
1754
1755 See Also
1756 --------
1757 * :meth:`evolved_expectation_value()`
1758 """
1759 pass
[docs]
1760 def evolved_inner_product(self,
1761 ctrl_amp: np.ndarray[np.complex128],
1762 state: np.ndarray[np.complex128],
1763 dt: float,
1764 fixed_vector: np.ndarray[np.complex128]
1765 ) -> complex:
1766 r"""
1767 Calculates the real inner product of an evolved state vector with a
1768 fixed vector. The evolved state vector is evolved under a control
1769 Hamiltonian modulated by the control amplitudes. The integration is
1770 performed using :meth:`propagate()`.
1771
1772 Parameters
1773 ----------
1774 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1775 $\left(a_{ij}\right)$ The control amplitudes at each time step
1776 expressed as an $N\times\textrm{length}$ matrix where the element
1777 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1778 Hamiltonian at the $i$th time step.
1779 state : NDArray[Shape[runtime_dim], complex128]
1780 $\left[\psi(0)\right]$ The state vector to propagate.
1781 dt : float
1782 ($\Delta t$) The time step to propagate by.
1783 fixed_vector : NDArray[Shape[runtime_dim], complex128]
1784 $(\xi)$ The fixed vector to calculate the inner product with.
1785
1786 Returns
1787 -------
1788 complex
1789 The inner product of the evolved state vector with the fixed vector,
1790 $\sum_{i=1}^\texttt{dim}\xi_i\psi_i(N\Delta t)$.
1791
1792 Note
1793 ----
1794 This function is a wrapper around the C++ function
1795 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_inner_product()`.
1796
1797 See Also
1798 --------
1799 * :meth:`evolved_inner_product_all()`
1800 """
1801 pass
[docs]
1802 def evolved_inner_product_all(self,
1803 ctrl_amp: np.ndarray[np.complex128],
1804 state: np.ndarray[np.complex128],
1805 dt: float,
1806 fixed_vector: np.ndarray[np.complex128]
1807 ) -> np.ndarray[np.complex128]:
1808 r"""
1809 Calculates the real inner products of a time series of evolved state
1810 vectors with a fixed vector. The evolved state vector is evolved under a
1811 control Hamiltonian modulated by the control amplitudes. The integration
1812 is performed using :meth:`propagate_all()``.
1813
1814 Parameters
1815 ----------
1816 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1817 $\left(a_{ij}\right)$ The control amplitudes at each time step
1818 expressed as an $N\times\textrm{length}$ matrix where the element
1819 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1820 Hamiltonian at the $i$th time step.
1821 state : NDArray[Shape[runtime_dim], complex128]
1822 $\left[\psi(0)\right]$ The state vector to propagate.
1823 dt : float
1824 ($\Delta t$) The time step to propagate by.
1825 fixed_vector : NDArray[Shape[runtime_dim], complex128]
1826 $(\xi)$ The fixed vector to calculate the inner product with.
1827
1828 Returns
1829 -------
1830 NDArray[Shape[time_steps + 1], complex128]
1831 The inner products of the evolved state vectors with the fixed
1832 vector,
1833 $\left(
1834 \sum_{i=1}^\texttt{dim}\xi_i\psi_i(n\Delta t)\right)_{n=0}^N$.
1835
1836 Note
1837 ----
1838 This function is a wrapper around the C++ function
1839 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_inner_product_all()`.
1840
1841 See Also
1842 --------
1843 * :meth:`evolved_inner_product()`.
1844 """
1845 pass
[docs]
1846 def switching_function(self,
1847 ctrl_amp: np.ndarray[np.complex128],
1848 state: np.ndarray[np.complex128],
1849 dt: float,
1850 cost: np.ndarray[np.complex128]
1851 ) -> tuple[complex, np.ndarray[np.float64]]:
1852 r"""
1853 Calculates the switching function for a Mayer problem with an
1854 expectation value as the cost function. More precisely if the cost
1855 function is
1856 $$
1857 J\left[\vec a(t)\right]\coloneqq\langle\hat O\rangle
1858 \equiv\psi^\dagger[\vec a(t);T]
1859 \hat O\psi[\vec a(t);T],
1860 $$
1861 where $T=N\Delta t$, then the switching function is
1862 $$
1863 \phi_j(t)\coloneqq\frac{\delta J}{\delta a_j(t)}
1864 =2\operatorname{Im}\left(\psi^\dagger[\vec a(t);T]
1865 \hat OU(t\to T)H_j\psi[\vec a(t);t]\right).
1866 $$
1867 using the first-order Suzuki-Trotter expansion we can express the
1868 switching function as
1869 $$
1870 \begin{align}
1871 &\phi_j(n\Delta t)=\frac{1}{\Delta t}\pdv{J}{a_{nj}}\\
1872 &=\!2\operatorname{Im}\!\left(\psi^\dagger(T)
1873 \hat O\!\!\left[\prod_{i>n}^N\prod_{k=1}^{\textrm{length}}
1874 e^{-ia_{ik}H_k\Delta t}\right]\!\!\!
1875 \left[\prod_{k=j}^{\textrm{length}}
1876 e^{-ia_{nk}H_k\Delta t}\right]\!H_j\!\!
1877 \left[\prod_{k=0}^{j-1}
1878 e^{-ia_{nk}H_k\Delta t}\right]
1879 \!\psi(\left[n-1\right]\Delta t)\right),
1880 \end{align}
1881 $$
1882 where for numerical efficiency we replace
1883 $e^{-ia_{ik}H_k\Delta t}$
1884 with
1885 $U_ke^{-ia_{ik}D_k\Delta t}U_k^\dagger$
1886 as in :meth:`propagate()`.
1887
1888 Parameters
1889 ----------
1890 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1891 $\left(a_{ij}\right)$ The control amplitudes at each time step
1892 expressed as an $N\times\textrm{length}$ matrix where the element
1893 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1894 Hamiltonian at the $i$th time step.
1895 state : NDArray[Shape[runtime_dim], complex128]
1896 $\left[\psi(0)\right]$ The initial state vector.
1897 dt : float
1898 ($\Delta t$) The time step.
1899 cost : NDArray[Shape[runtime_dim, runtime_dim], complex128]
1900 $(\hat O)$ The observable to calculate the expectation value of.
1901
1902 Returns
1903 -------
1904 tuple[complex, NDArray[Shape[time_steps, :attr:`length`], float64]]
1905 The expectation value, $\psi^\dagger(T)\hat O\psi(T)$, and
1906 the switching function,
1907 $\phi_j(n\Delta t)$
1908 for all
1909 $j\in\left[1,\textrm{length}\right]$
1910 and
1911 $n\in\left[1,N\right]$.
1912
1913 See Also
1914 --------
1915 * :meth:`gate_switching_function()`.
1916
1917 Note
1918 ----
1919 This function is a wrapper around the C++ function
1920 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::switching_function()`.
1921 """
1922 pass
[docs]
1923 def get_evolution(self,
1924 ctrl_amp: np.ndarray[np.complex128],
1925 dt: float
1926 ) -> np.ndarray[np.complex128]:
1927 r"""
1928 Computes the unitary corresponding to the evolution under the
1929 differential equation
1930 $$
1931 \dot U=-iHU.
1932 $$
1933 The computation is performed using the first-order Suzuki-Trotter
1934 expansion:
1935 $$
1936 \begin{align}
1937 U(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
1938 e^{-ia_{ij}H_j\Delta t}+\mathcal E\\
1939 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}}
1940 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger+\mathcal E.
1941 \end{align}
1942 $$
1943 where
1944 $a_{nj}\coloneqq a(n\Delta t)$,
1945 we set
1946 $a_{n0}=1$
1947 for notational ease, and the additive error
1948 $\mathcal E$
1949 is
1950 $$
1951 \begin{align}
1952 \mathcal E&=\mathcal O\left(
1953 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij}
1954 \norm{H_j}
1955 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik}
1956 \norm{[H_j,H_k]}\right]
1957 \right)\\
1958 &=\mathcal O\left(
1959 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right]
1960 \right)
1961 \end{align}
1962 $$
1963 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and
1964 $$
1965 \begin{align}
1966 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1967 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\
1968 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\
1969 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\
1970 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}.
1971 \end{align}
1972 $$
1973 Note the error is quadratic in $\Delta t$ but linear in $N$. We can also
1974 view this as being linear in $\Delta t$ and linear in total evolution
1975 time $N\Delta t$. Additionally, by Nyquist's theorem this asymptotic
1976 error scaling will not be achieved until the time step $\Delta t$ is
1977 smaller than $\frac{1}{2\Omega}$ where $\Omega$ is the largest energy or
1978 frequency in the system.
1979
1980 Parameters
1981 ----------
1982 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
1983 $\left(a_{ij}\right)$ The control amplitudes at each time step
1984 expressed as an $N\times\textrm{length}$ matrix where the element
1985 $a_{ij}$ corresponds to the control amplitude of the $j$th control
1986 Hamiltonian at the $i$th time step.
1987 dt : float
1988 ($\Delta t$) The time step to propagate by.
1989
1990 Returns
1991 -------
1992 NDArray[Shape[runtime_dim, runtime_dim], complex128]
1993 The unitary corresponding to the evolution,
1994 $U(N\Delta t)$.
1995
1996 Note
1997 ----
1998 This function is a wrapper around the C++ function
1999 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::get_evolution()`.
2000
2001 See Also
2002 --------
2003 * :meth:`propagate()`
2004 * :meth:`propagate_all()`
2005 * :meth:`propagate_collection()`
2006 """
2007 pass
[docs]
2008 def evolved_gate_infidelity(self,
2009 ctrl_amp: np.ndarray[np.complex128],
2010 dt: float,
2011 target: np.ndarray[np.complex128]
2012 ) -> float:
2013 r"""
2014 Calculates the gate infidelity with respect to a target gate of the gate
2015 produced by the control Hamiltonian modulated by the control amplitudes.
2016 The integration is performed using
2017 :meth:`get_evolution()`.
2018
2019 Parameters
2020 ----------
2021 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
2022 $\left(a_{ij}\right)$ The control amplitudes at each time step
2023 expressed as an $N\times\textrm{length}$ matrix where the element
2024 $a_{ij}$ corresponds to the control amplitude of the $j$th control
2025 Hamiltonian at the $i$th time step.
2026 dt : float
2027 ($\Delta t$) The time step to propagate by.
2028 target : NDArray[Shape[runtime_dim, runtime_dim], complex128]
2029 The target gate to calculate the infidelity with respect to.
2030
2031 Returns
2032 -------
2033 float
2034 The gate infidelity with respect to the target gate:
2035 $$
2036 \mathcal I(U(N\Delta t), \texttt{target})
2037 \coloneqq 1-\frac{
2038 \left|\Tr\left[
2039 \texttt{target}^\dagger\cdot U(N\Delta t)\right]\right|^2
2040 +\texttt{dim}}{\texttt{dim}(\texttt{dim}+1)}.
2041 $$
2042
2043 Note
2044 ----
2045 This function is a wrapper around the C++ function
2046 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_gate_infidelity()`.
2047
2048 See Also
2049 --------
2050 * :func:`unitary_gate_infidelity()`.
2051 """
2052 pass
[docs]
2053 def gate_switching_function(self,
2054 ctrl_amp: np.ndarray[np.complex128],
2055 dt: float,
2056 target: np.ndarray[np.complex128]
2057 ) -> tuple[float, np.ndarray[np.float64]]:
2058 r"""
2059 Calculates the switching function for a Mayer problem with the gate
2060 infidelity as the cost function. More precisely if the cost function is
2061 $$
2062 J\left[\vec a(t)\right]
2063 \coloneqq\mathcal I(U\left[\vec a(t); T\right], \texttt{target})
2064 \coloneqq 1-\frac{\left|\Tr\left[\texttt{target}^\dagger
2065 \cdot U\left[\vec a(t); T\right]\right]\right|^2
2066 +\texttt{dim}}{\texttt{dim}(\texttt{dim}+1)}.
2067 $$
2068 where $T=N\Delta t$, then the switching function is
2069 $$
2070 \begin{align}
2071 &\phi_j(t)\coloneqq\frac{\delta J}{\delta a_j(t)}\\
2072 &=\frac{2}{\texttt{dim}(\texttt{dim}+1)}\operatorname{Im}\left(
2073 \Tr\left[U^\dagger(N\Delta t)\cdot\texttt{target}\right]
2074 \Tr\left[\texttt{target}^\dagger
2075 \cdot U(t\to T)H_j U[\vec a(t);t]\right]\right).
2076 \end{align}
2077 $$
2078 Using the first-order Suzuki-Trotter expansion we can express the
2079 switching function as
2080 $$
2081 \begin{align}
2082 &\phi_j(n\Delta t)=\frac{1}{\Delta t}\pdv{J}{a_{nj}}\\
2083 &=\!\frac{2}{\texttt{dim}(\texttt{dim}+1)}\operatorname{Im}
2084 \!\left(
2085 \Tr\!\left[U^\dagger(N\Delta t)\cdot\texttt{target}\right]
2086 \vphantom{[\prod_{k=j}^{\textrm{length}}}\right.\\
2087 &\left.\cdot\Tr\!\left[\texttt{target}^\dagger\!\cdot\!
2088 \left[\prod_{i>n}^N\prod_{k=1}^{\textrm{length}}
2089 e^{-ia_{ik}H_k\Delta t}\right]\!\!\!
2090 \left[\prod_{k=j}^{\textrm{length}}
2091 e^{-ia_{nk}H_k\Delta t}\right]\!H_j\!\!
2092 \left[\prod_{k=0}^{j-1}
2093 e^{-ia_{nk}H_k\Delta t}\right]
2094 \! U(\left[n-1\right]\Delta t)\right]\right),
2095 \end{align}
2096 $$
2097 where for numerical efficiency we replace
2098 $e^{-ia_{ik}H_k\Delta t}$
2099 with
2100 $U_ke^{-ia_{ik}D_k\Delta t}U_k^\dagger$
2101 as in :meth:`get_evolution()`.
2102
2103 Parameters
2104 ----------
2105 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128]
2106 $\left(a_{ij}\right)$ The control amplitudes at each time step
2107 expressed as an $N\times\textrm{length}$ matrix where the element
2108 $a_{ij}$ corresponds to the control amplitude of the $j$th control
2109 Hamiltonian at the $i$th time step.
2110 dt : float
2111 ($\Delta t$) The time step to propagate by.
2112 target : NDArray[Shape[runtime_dim, runtime_dim], complex128]
2113 The target gate to calculate the infidelity with respect to.
2114
2115 Returns
2116 -------
2117 tuple[float, NDArray[Shape[time_steps, :attr:`length`], float64]]
2118 The gate infidelity,
2119 $I(U\left[\vec a(t); T\right], \texttt{target})$ and
2120 the switching function,
2121 $\phi_j(n\Delta t)$
2122 for all
2123 $j\in\left[1,\textrm{length}\right]$
2124 and
2125 $n\in\left[1,N\right]$.
2126
2127 See Also
2128 --------
2129 * :meth:`switching_function()`.
2130 """
2131 pass
2132
[docs]
2133class DenseUnitaryEvolver_nctrl_dim(DenseUnitaryEvolver):
2134 r"""
2135 A class to store the diagonalised drift and control Hamiltonians with dense
2136 matrices and precompiled values of ``n_ctrl`` and ``dim``.
2137
2138 Important
2139 ---------
2140 This is not the actual class name ``nctrl`` and ``dim`` should be replaced
2141 with positive integers to specify their values. For example,
2142 ``DenseUnitaryEvolver_3_2`` is a valid class name with :attr:`n_ctrl`
2143 ``=3`` control Hamiltonians acting on a :attr:`dim` ``=2`` dimensional
2144 vector space. If only one of ``nctrl`` and ``dim`` is precompiled the other
2145 should be specified as `Dynamic`. For example,
2146 ``DenseUnitaryEvolver_Dynamic_2`` is a valid class name with a dynamic
2147 number of control Hamiltonians (:attr:`n_ctrl` ``=-1``) acting on a
2148 :attr:`dim` ``=2`` dimensional vector space.
2149
2150
2151 On initialisation the Hamiltonians are diagonalised and the eigenvectors and
2152 values stored as dense matrices. This initial diagonalisation may be slow
2153 and takes
2154 $O(\textrm{dim}^3)$
2155 time for a
2156 $\textrm{dim}\times \textrm{dim}$
2157 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion
2158 to be implimented in
2159 $O(\textrm{dim}^2)$
2160 time with matrix multiplication and only scalar exponentiation opposed to
2161 matrix exponentiation which takes
2162 $O(\textrm{dim}^3)$
2163 time.
2164
2165 Note
2166 ----
2167 This class is a Python wrapper around the C++ struct:
2168
2169 .. code-block:: cpp
2170
2171 Suzuki_Trotter_Evolver::UnitaryEvolver<n_ctrl, dim, DMatrix<dim, dim>>
2172
2173 from
2174 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__.
2175
2176 Note
2177 ----
2178 Unlike :class:`DenseUnitaryEvolver`, :attr:`n_ctrl` and :attr:`dim` are
2179 baked into the class when the C++ code is compiled allowing for more
2180 efficient state propagation.
2181
2182 See Also
2183 --------
2184 * :class:`SparseUnitaryEvolver_nctrl_dim`
2185 * :class:`DenseUnitaryEvolver`
2186
2187
2188 ---
2189 """
2190
2191 n_ctrl: int = None
2192 r"""
2193 The number of control Hamiltonians used to compile the C++ backend. Equal to
2194 the value in the class name. A value of ``-1`` represents ``Dynamic`` in the
2195 class name and implies the value is not precompiled in the C++. This was the
2196 value at compile time while :attr:`length` is the value at runtime time.
2197 """
2198
2199 dim: int = None
2200 r"""
2201 The dimension of the vector space the Hamiltonians act upon used to compile
2202 the C++ backend. Equal to the value in the class name. A value of ``-1``
2203 represents ``Dynamic`` in the class name and implies the value is not
2204 precompiled in the C++.
2205 """
2206
2207 dim_x_n_ctrl: int = None
2208 r"""
2209 The dimension of rows in each control Hamiltonian multiplied by the
2210 number of control Hamiltonians upon used to compile the C++ backend. This is
2211 the number of rows for the ``control_hamiltonians`` argument for
2212 :meth:`__init__()`. A value of ``-1`` implies the value is not precompiled
2213 in the C++.
2214
2215 Note
2216 ----
2217 This is a wrapper around the C++ member
2218 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`.
2219 """
2220
[docs]
2221class SparseUnitaryEvolver_nctrl_dim(SparseUnitaryEvolver):
2222 r"""
2223 A class to store the diagonalised drift and control Hamiltonians with sparse
2224 matrices and precompiled values of ``n_ctrl`` and ``dim``.
2225
2226 Important
2227 ---------
2228 This is not the actual class name ``nctrl`` and ``dim`` should be replaced
2229 with positive integers to specify their values. For example,
2230 ``SparseUnitaryEvolver_3_2`` is a valid class name with :attr:`n_ctrl`
2231 ``=3`` control Hamiltonians acting on a :attr:`dim` ``=2`` dimensional
2232 vector space. If only one of ``nctrl`` and ``dim`` is precompiled the other
2233 should be specified as `Dynamic`. For example,
2234 ``SparseUnitaryEvolver_Dynamic_2`` is a valid class name with a dynamic
2235 number of control Hamiltonians (:attr:`n_ctrl` ``=-1``) acting on a
2236 :attr:`dim` ``=2`` dimensional vector space.
2237
2238
2239 On initialisation the Hamiltonians are diagonalised and the eigenvectors and
2240 values stored as sparse and dense matrices, respectively. This initial
2241 diagonalisation may be slow and takes
2242 $O(\textrm{dim}^3)$
2243 time for a
2244 $\textrm{dim}\times \textrm{dim}$
2245 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion
2246 to be implimented with sparse matrix multiplication and only scalar
2247 exponentiation opposed to matrix exponentiation.
2248
2249 Note
2250 ----
2251 This class is a Python wrapper around the C++ struct:
2252
2253 .. code-block:: cpp
2254
2255 Suzuki_Trotter_Evolver::UnitaryEvolver<n_ctrl, dim, SMatrix>
2256
2257 from
2258 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__.
2259
2260 Note
2261 ----
2262 Unlike :class:`SparseUnitaryEvolver`, :attr:`n_ctrl` and :attr:`dim` are
2263 baked into the class when the C++ code is compiled allowing for more
2264 efficient state propagation.
2265
2266 See Also
2267 --------
2268 * :class:`DenseUnitaryEvolver_nctrl_dim`
2269 * :class:`SparseUnitaryEvolver`
2270
2271
2272 ---
2273 """
2274
2275 n_ctrl: int = None
2276 r"""
2277 The number of control Hamiltonians used to compile the C++ backend. Equal to
2278 the value in the class name. A value of ``-1`` represents ``Dynamic`` in the
2279 class name and implies the value is not precompiled in the C++. This was the
2280 value at compile time while :attr:`length` is the value at runtime time.
2281 """
2282
2283 dim: int = None
2284 r"""
2285 The dimension of the vector space the Hamiltonians act upon used to compile
2286 the C++ backend. Equal to the value in the class name. A value of ``-1``
2287 represents ``Dynamic`` in the class name and implies the value is not
2288 precompiled in the C++.
2289 """
2290
2291 dim_x_n_ctrl: int = None
2292 r"""
2293 The dimension of rows in each control Hamiltonian multiplied by the
2294 number of control Hamiltonians upon used to compile the C++ backend. This is
2295 the number of rows for the ``control_hamiltonians`` argument for :meth:`__init__()`. A value
2296 of ``-1`` implies the value is not precompiled in the C++.
2297
2298 Note
2299 ----
2300 This is a wrapper around the C++ member
2301 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`.
2302 """