Source code for py_ste.evolvers

   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 """