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
[docs] 53class UnitaryEvolver(pybind11_object): 54 """ 55 A base class for 56 :class:`DenseUnitaryEvolver` 57 and 58 :class:`SparseUnitaryEvolver` 59 with no implemented functionality. 60 61 The purpose of ``UnitaryEvolver`` is to allow for checks such as:: 62 63 isinstance(evolver, evolvers.UnitaryEvolver) 64 65 See Also 66 -------- 67 * :class:`DenseUnitaryEvolver` 68 * :class:`SparseUnitaryEvolver` 69 70 71 --- 72 """ 73 pass
74
[docs] 75class DenseUnitaryEvolver(UnitaryEvolver): 76 r""" 77 A class to store the diagonalised drift and control Hamiltonians with dense 78 matrices and dynamic values of ``n_ctrl`` and ``dim``. On initialisation the 79 Hamiltonians are diagonalised and the eigenvectors and values stored as 80 dense matrices. This initial diagonalisation may be slow and takes 81 $O(\textrm{dim}^3)$ 82 time for a 83 $\textrm{dim}\times \textrm{dim}$ 84 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion 85 to be implimented in 86 $O(\textrm{dim}^2)$ 87 time with matrix multiplication and only scalar exponentiation opposed to 88 matrix exponentiation which takes 89 $O(\textrm{dim}^3)$ 90 time. 91 92 Note 93 ---- 94 This class is a Python wrapper around the C++ struct: 95 96 .. code-block:: cpp 97 98 Suzuki_Trotter_Evolver::UnitaryEvolver<Dynamic, Dynamic, DMatrix<Dynamic, Dynamic>> 99 100 from 101 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__. 102 103 Note 104 ---- 105 Unlike :class:`DenseUnitaryEvolver_nctrl_dim`, :attr:`n_ctrl` and :attr:`dim` 106 dynamically determined at runtime. :class:`DenseUnitaryEvolver_nctrl_dim` is 107 typically more efficient as the values for :attr:`n_ctrl` and :attr:`dim` 108 are baked in at compile time. 109 110 See Also 111 -------- 112 * :class:`DenseUnitaryEvolver_nctrl_dim` 113 * :class:`SparseUnitaryEvolver` 114 115 116 --- 117 """ 118 119 n_ctrl: int = -1 120 r""" 121 The number of control Hamiltonians used to compile the C++ backend. A value 122 of ``-1`` implies the value is not precompiled in the C++. This was the 123 value at compile time while :attr:`length` is the value at runtime time. 124 """ 125 126 dim: int = -1 127 r""" 128 The dimension of the vector space the Hamiltonians act upon used to compile 129 the C++ backend. A value of ``-1`` implies the value is not precompiled in 130 the C++. 131 """ 132 133 dim_x_n_ctrl: int = -1 134 r""" 135 The dimension of rows in each control Hamiltonian multiplied by the 136 number of control Hamiltonians upon used to compile the C++ backend. This is 137 the number of rows for the ``control_hamiltonians`` argument for :meth:`__init__()`. A value 138 of ``-1`` implies the value is not precompiled in the C++. 139 140 Note 141 ---- 142 This is a wrapper around the C++ member 143 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`. 144 """ 145 146 length: int 147 r""" 148 The number of control Hamiltonians. This is the value at runtime while 149 :attr:`n_ctrl` was the value at compile time. These two values will be equal 150 unless ``n_ctrl==-1``. If ``n_ctrl==-1`` then `length` is the actual number 151 of control Hamiltonians. 152 153 Note 154 ---- 155 This is a wrapper around the C++ member 156 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::length`. 157 """ 158 159 d0: np.ndarray 160 r""" 161 The eigenvalues, 162 $\operatorname{diag}(D_0)$, 163 of the drift Hamiltonian: 164 $H_0=U_0D_0U_0^\dagger$. 165 166 Note 167 ---- 168 This is a wrapper around the C++ member 169 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::d0`. 170 171 See Also 172 -------- 173 * :attr:`ds` 174 * :attr:`u0` 175 * :attr:`u0_inverse` 176 """ 177 178 ds: list[np.ndarray] 179 r""" 180 The eigenvalues, 181 $\left(\operatorname{diag}(D_i)\right)_{i=1}^{\textrm{length}}$, 182 of the control Hamiltonians: 183 $H_i=U_iD_iU_i^\dagger$ 184 for all 185 $i\in\left[\textrm{length}\right]$. 186 187 Note 188 ---- 189 This is a wrapper around the C++ member 190 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::ds`. 191 192 See Also 193 -------- 194 * :attr:`d0` 195 * :attr:`us_individual` 196 * :attr:`us_inverse_individual` 197 * :attr:`hs` 198 """ 199 200 u0: np.ndarray 201 r""" 202 The unitary transformation, 203 $U_0$, 204 that diagonalises the drift Hamiltonian: 205 $H_0=U_0D_0U_0^\dagger$. 206 207 Note 208 ---- 209 This is a wrapper around the C++ member 210 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0`. 211 212 See Also 213 -------- 214 * :attr:`us_individual` 215 * :attr:`d0` 216 * :attr:`u0_inverse` 217 """ 218 219 u0_inverse: np.ndarray 220 r""" 221 The inverse of the unitary transformation, 222 $U_0^\dagger$, 223 that diagonalises the drift Hamiltonian: 224 $H_0=U_0D_0U_0^\dagger$. 225 226 Note 227 ---- 228 This is a wrapper around the C++ member 229 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse`. 230 231 See Also 232 -------- 233 * :attr:`us_inverse_individual` 234 * :attr:`u0` 235 * :attr:`d0` 236 """ 237 238 us: list[np.ndarray] 239 r""" 240 The unitary transformations, 241 $(U_i^\dagger U_{i-1})_{i=1}^{\textrm{length}}$, 242 from the eigen basis of 243 $H_{i-1}$ 244 to the eigen basis of 245 $H_i$. 246 247 Note 248 ---- 249 This is a wrapper around the C++ member 250 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us`. 251 252 See Also 253 -------- 254 * :attr:`us_individual` 255 * :attr:`us_inverse_individual` 256 """ 257 258 us_individual: list[np.ndarray] 259 r""" 260 The unitary transformations, 261 $\left(U_i\right)_{i=1}^{\textrm{length}}$, 262 that diagonalise the control Hamiltonians: 263 $H_i=U_iD_iU_i^\dagger$ 264 for all 265 $i\in\left[\textrm{length}\right]$. 266 267 Note 268 ---- 269 This is a wrapper around the C++ member 270 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_individual`. 271 272 See Also 273 -------- 274 * :attr:`us_inverse_individual` 275 * :attr:`us` 276 * :attr:`ds` 277 * :attr:`hs` 278 """ 279 280 us_inverse_individual: list[np.ndarray] 281 r""" 282 The inverse of the unitary transformations, 283 $(U_i^\dagger)_{i=1}^{\textrm{length}}$, 284 that diagonalise the control Hamiltonians: 285 $H_i=U_iD_iU_i^\dagger$ 286 for all 287 $i\in\left[\textrm{length}\right]$. 288 289 Note 290 ---- 291 This is a wrapper around the C++ member 292 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_inverse_individual`. 293 294 See Also 295 -------- 296 * :attr:`us_individual` 297 * :attr:`us` 298 * :attr:`ds` 299 * :attr:`hs` 300 """ 301 302 hs: list[np.ndarray] 303 r""" 304 The control Hamiltonians: 305 $H_i$ 306 for all 307 $i\in\left[\textrm{length}\right]$. 308 309 Note 310 ---- 311 This is a wrapper around the C++ member 312 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::hs`. 313 314 See Also 315 -------- 316 * :attr:`us_individual` 317 * :attr:`us_inverse_individual` 318 * :attr:`ds` 319 """ 320 321 u0_inverse_u_last: list[np.ndarray] 322 r""" 323 The unitary transformation, 324 $U_0^\dagger U_{\textrm{length}}$, 325 from the eigen basis of 326 $H_{\textrm{length}}$ 327 to the eigen basis of 328 $H_0$. 329 330 Note 331 ---- 332 This is a wrapper around the C++ member 333 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse_u_last`. 334 335 See Also 336 -------- 337 * :attr:`us_individual` 338 * :attr:`us` 339 * :attr:`u0_inverse` 340 """ 341
[docs] 342 def __init__(self, 343 drift_hamiltonian: np.ndarray[np.complex128], 344 control_hamiltonians: np.ndarray[np.complex128]): 345 r""" 346 Initialises a new unitary evolver with the Hamiltonian 347 $$ 348 H(t)=H_0+\sum_{j=1}^{\textrm{length}}a_j(t)H_j, 349 $$ 350 where $H_0$ is the drift Hamiltonian and $H_j$ are the control 351 Hamiltonians modulated by control amplitudes $a_j(t)$ which need 352 not be specified during initialisation. 353 354 Parameters 355 ---------- 356 drift_hamiltonian : NDArray[Shape[runtime_dim, runtime_dim], complex128] 357 The drift Hamiltonian. 358 control_hamiltonians : NDArray[Shape[runtime_dim * :attr:`length`, runtime_dim], complex128] 359 The control Hamiltonians. 360 """ 361 pass
[docs] 362 def propagate(self, 363 ctrl_amp: np.ndarray[np.complex128], 364 state: np.ndarray[np.complex128], 365 dt: float 366 ) -> np.ndarray[np.complex128]: 367 r""" 368 Propagates the state vector using the first-order Suzuki-Trotter 369 expansion. More precisely, a state vector, 370 $\psi(0)$, 371 is evolved under the differential equation 372 $$ 373 \dot\psi=-iH\psi 374 $$ 375 using the first-order Suzuki-Trotter expansion: 376 $$ 377 \begin{align} 378 \psi(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 379 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E\\ 380 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 381 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E. 382 \end{align} 383 $$ 384 where 385 $a_{nj}\coloneqq a(n\Delta t)$, 386 we set 387 $a_{n0}=1$ 388 for notational ease, and the additive error 389 $\mathcal E$ 390 is 391 $$ 392 \begin{align} 393 \mathcal E&=\mathcal O\left( 394 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij} 395 \norm{H_j} 396 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik} 397 \norm{[H_j,H_k]}\right] 398 \right)\\ 399 &=\mathcal O\left( 400 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right] 401 \right) 402 \end{align} 403 $$ 404 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and 405 $$ 406 \begin{align} 407 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 408 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\ 409 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 410 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\ 411 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}. 412 \end{align} 413 $$ 414 Note the error is quadratic in $\Delta t$ but linear in $N$. 415 We can also view this as being linear in $\Delta t$ and linear in 416 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem 417 this asymptotic error scaling will not be achieved until the time step 418 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where 419 $\Omega$ is the largest energy or frequency in the system. 420 421 Parameters 422 ---------- 423 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 424 $\left(a_{ij}\right)$ The control amplitudes at each time step 425 expressed as an $N\times\textrm{length}$ matrix where the element 426 $a_{ij}$ corresponds to the control amplitude of the $j$th control 427 Hamiltonian at the $i$th time step. 428 state : NDArray[Shape[runtime_dim], complex128] 429 $\left[\psi(0)\right]$ The state vector to propagate. 430 dt : float 431 ($\Delta t$) The time step to propagate by. 432 433 Returns 434 ------- 435 NDArray[Shape[runtime_dim], complex128] 436 The propagated state vector, $\psi(N\Delta t)$. 437 438 Note 439 ---- 440 This function is a wrapper around the C++ function 441 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate()`. 442 443 See Also 444 -------- 445 * :meth:`propagate_collection()` 446 * :meth:`propagate_all()` 447 """ 448 pass
[docs] 449 def propagate_collection(self, 450 ctrl_amp: np.ndarray[np.complex128], 451 states: np.ndarray[np.complex128], 452 dt: float 453 ) -> np.ndarray[np.complex128]: 454 r""" 455 Propagates a collection of state vectors using the first-order 456 Suzuki-Trotter expansion. More precisely, a collection of state vectors, 457 $\left(\psi_k(0)\right)_{k}$, 458 are evolved under the differential equation 459 $$ 460 \dot\psi_k=-iH\psi_k 461 $$ 462 using the first-order Suzuki-Trotter expansion: 463 $$ 464 \begin{align} 465 \psi_k(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 466 e^{-ia_{ij}H_j\Delta t}\psi_k(0)+\mathcal E\\ 467 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 468 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi_k(0)+\mathcal E. 469 \end{align} 470 $$ 471 where 472 $a_{nj}\coloneqq a(n\Delta t)$, 473 we set 474 $a_{n0}=1$ 475 for notational ease, and the addative error 476 $\mathcal E$ 477 is 478 $$ 479 \begin{align} 480 \mathcal E&=\mathcal O\left( 481 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij} 482 \norm{H_j} 483 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik} 484 \norm{[H_j,H_k]}\right] 485 \right)\\ 486 &=\mathcal O\left( 487 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right] 488 \right) 489 \end{align} 490 $$ 491 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and 492 $$ 493 \begin{align} 494 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 495 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\ 496 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 497 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\ 498 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}. 499 \end{align} 500 $$ 501 Note the error is quadratic in $\Delta t$ but linear in $N$. 502 We can also view this as being linear in $\Delta t$ and linear in 503 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem 504 this asymptotic error scaling will not be achieved until the time step 505 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where 506 $\Omega$ is the largest energy or frequency in the system. 507 508 Parameters 509 ---------- 510 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 511 $\left(a_{ij}\right)$ The control amplitudes at each time step 512 expressed as an $N\times\textrm{length}$ matrix where the element 513 $a_{ij}$ corresponds to the control amplitude of the $j$th control 514 Hamiltonian at the $i$th time step. 515 states : NDArray[Shape[runtime_dim, number_of_states], complex128] 516 $\left[\left(\psi(0)\right)_{k}\right]$ A collection of state 517 vectors to propagate expressed as a matrix with each column 518 corresponding to a state vector. 519 dt : float 520 ($\Delta t$) The time step to propagate by. 521 522 Returns 523 ------- 524 NDArray[Shape[runtime_dim, number_of_states], complex128] 525 The propagated state vectors, $\left(\psi_k(N\Delta t)\right)_k$. 526 527 Note 528 ---- 529 This function is a wrapper around the C++ function 530 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_collection()`. 531 532 See Also 533 -------- 534 * :meth:`propagate()` 535 * :meth:`propagate_all()` 536 """ 537 pass
[docs] 538 def propagate_all(self, 539 ctrl_amp: np.ndarray[np.complex128], 540 state: np.ndarray[np.complex128], 541 dt: float 542 ) -> np.ndarray[np.complex128]: 543 r""" 544 Propagates the state vector using the first-order Suzuki-Trotter 545 expansion and returns the resulting state vector at every time step. 546 More precisely, a state vector, 547 $\psi(0)$, 548 is evolved under the differential equation 549 $$ 550 \dot\psi=-iH\psi 551 $$ 552 using the first-order Suzuki-Trotter expansion: 553 $$ 554 \begin{align} 555 \psi(n\Delta t)&=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}} 556 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E 557 \quad\forall n\in\left[0, N\right]\\ 558 &=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}} 559 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E 560 \quad\forall n\in\left[0, N\right]. 561 \end{align} 562 $$ 563 where 564 $a_{nj}\coloneqq a(n\Delta t)$, 565 we set 566 $a_{n0}=1$ 567 for notational ease, and the additive error 568 $\mathcal E$ 569 is 570 $$ 571 \begin{align} 572 \mathcal E&=\mathcal O\left( 573 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij} 574 \norm{H_j} 575 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik} 576 \norm{[H_j,H_k]}\right] 577 \right)\\ 578 &=\mathcal O\left( 579 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right] 580 \right) 581 \end{align} 582 $$ 583 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and 584 $$ 585 \begin{align} 586 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 587 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\ 588 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 589 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\ 590 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}. 591 \end{align} 592 $$ 593 Note the error is quadratic in $\Delta t$ but linear in $N$. 594 We can also view this as being linear in $\Delta t$ and linear in 595 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem 596 this asymptotic error scaling will not be achieved until the time step 597 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where 598 $\Omega$ is the largest energy or frequency in the system. 599 600 Parameters 601 ---------- 602 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 603 $\left(a_{ij}\right)$ The control amplitudes at each time step 604 expressed as an $N\times\textrm{length}$ matrix where the element 605 $a_{ij}$ corresponds to the control amplitude of the $j$th control 606 Hamiltonian at the $i$th time step. 607 state : NDArray[Shape[runtime_dim], complex128] 608 $\left[\psi(0)\right]$ The state vector to propagate. 609 dt : float 610 ($\Delta t$) The time step to propagate by. 611 612 Returns 613 ------- 614 NDArray[Shape[runtime_dim, time_steps + 1], complex128] 615 The propagated state vector at each time step, 616 $\left(\psi(n\Delta t)\right)_{n=0}^N$. 617 618 Note 619 ---- 620 This function is a wrapper around the C++ function 621 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_all()`. 622 623 See Also 624 -------- 625 * :meth:`propagate()` 626 * :meth:`propagate_collection()` 627 """ 628 pass
[docs] 629 def evolved_expectation_value(self, 630 ctrl_amp: np.ndarray[np.complex128], 631 state: np.ndarray[np.complex128], 632 dt: float, 633 observable: np.ndarray[np.complex128] 634 ) -> complex: 635 r""" 636 Calculates the expectation value with respect to an observable of an 637 evolved state vector evolved under a control Hamiltonian modulated by 638 the control amplitudes. The integration is performed using 639 :meth:`propagate()`. 640 641 Parameters 642 ---------- 643 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 644 $\left(a_{ij}\right)$ The control amplitudes at each time step 645 expressed as an $N\times\textrm{length}$ matrix where the element 646 $a_{ij}$ corresponds to the control amplitude of the $j$th control 647 Hamiltonian at the $i$th time step. 648 state : NDArray[Shape[runtime_dim], complex128] 649 $\left[\psi(0)\right]$ The state vector to propagate. 650 dt : float 651 ($\Delta t$) The time step to propagate by. 652 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128] 653 $(\hat O)$ The observable to calculate the expectation value of. 654 655 Returns 656 ------- 657 complex 658 The expectation value of the observable, $\langle\hat O\rangle 659 \equiv\psi^\dagger(N\Delta t)\hat O\psi(N\Delta t)$. 660 661 Note 662 ---- 663 This function is a wrapper around the C++ function 664 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value()`. 665 666 See Also 667 -------- 668 * :meth:`evolved_expectation_value_all()` 669 """ 670 pass
[docs] 671 def evolved_expectation_value_all(self, 672 ctrl_amp: np.ndarray[np.complex128], 673 state: np.ndarray[np.complex128], 674 dt: float, 675 observable: np.ndarray[np.complex128] 676 ) -> np.ndarray[np.complex128]: 677 r""" 678 Calculates the expectation values with respect to an observable of a 679 time series of state vectors evolved under a control Hamiltonian 680 modulated by the control amplitudes. The integration is performed using 681 :meth:`propagate_all()`. 682 683 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 684 $\left(a_{ij}\right)$ The control amplitudes at each time step 685 expressed as an $N\times\textrm{length}$ matrix where the element 686 $a_{ij}$ corresponds to the control amplitude of the $j$th control 687 Hamiltonian at the $i$th time step. 688 state : NDArray[Shape[runtime_dim], complex128] 689 $\left[\psi(0)\right]$ The state vector to propagate. 690 dt : float 691 ($\Delta t$) The time step to propagate by. 692 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128] 693 $(\hat O)$ The observable to calculate the expectation value of. 694 695 Returns 696 ------- 697 NDArray[Shape[time_steps + 1], complex128] 698 The expectation value of the observable, 699 $\left(\psi^\dagger(n\Delta t)\hat O\psi(N\Delta t)\right)_{n=0}^N$. 700 701 Note 702 ---- 703 This function is a wrapper around the C++ function 704 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value_all()`. 705 706 See Also 707 -------- 708 * :meth:`evolved_expectation_value()` 709 """ 710 pass
[docs] 711 def switching_function(self, 712 ctrl_amp: np.ndarray[np.complex128], 713 state: np.ndarray[np.complex128], 714 dt: float, 715 cost: np.ndarray[np.complex128] 716 ) -> tuple[complex, np.ndarray[np.float64]]: 717 r""" 718 Calculates the switching function for a Mayer problem with an 719 expectation value as the cost function. More precisely if the cost 720 function is 721 $$ 722 J\left[\vec a(t)\right]\coloneqq\langle\hat O\rangle 723 \equiv\psi^\dagger[\vec a(t);T] 724 \hat O\psi[\vec a(t);T], 725 $$ 726 where $T=N\Delta t$, then the switching function is 727 $$ 728 \phi_j(t)\coloneqq\frac{\delta J}{\delta a_j(t)} 729 =2\operatorname{Im}\left(\psi^\dagger[\vec a(t);T] 730 \hat OU(t\to T)H_j\psi[\vec a(t);t]\right). 731 $$ 732 using the first-order Suzuki-Trotter expansion we can express the 733 switching function as 734 $$ 735 \begin{align} 736 &\phi_j(n\Delta t)=\frac{1}{\Delta t}\pdv{J}{a_{nj}}\\ 737 &=\!2\operatorname{Im}\!\left(\psi^\dagger(T) 738 \hat O\!\!\left[\prod_{i>n}^N\prod_{k=1}^{\textrm{length}} 739 e^{-ia_{ik}H_k\Delta t}\right]\!\!\! 740 \left[\prod_{k=j}^{\textrm{length}} 741 e^{-ia_{nk}H_k\Delta t}\right]\!H_j\!\! 742 \left[\prod_{k=0}^{j-1} 743 e^{-ia_{nk}H_k\Delta t}\right] 744 \!\psi(\left[n-1\right]\Delta t)\right), 745 \end{align} 746 $$ 747 where for numerical efficiency we replace 748 $e^{-ia_{ik}H_k\Delta t}$ 749 with 750 $U_ke^{-ia_{ik}D_k\Delta t}U_k^\dagger$ 751 as in :meth:`propagate()`. 752 753 Parameters 754 ---------- 755 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 756 $\left(a_{ij}\right)$ The control amplitudes at each time step 757 expressed as an $N\times\textrm{length}$ matrix where the element 758 $a_{ij}$ corresponds to the control amplitude of the $j$th control 759 Hamiltonian at the $i$th time step. 760 state : NDArray[Shape[runtime_dim], complex128] 761 $\left[\psi(0)\right]$ The initial state vector. 762 dt : float 763 ($\Delta t$) The time step. 764 cost : NDArray[Shape[runtime_dim, runtime_dim], complex128] 765 $(\hat O)$ The observable to calculate the expectation value of. 766 767 Returns 768 ------- 769 tuple[complex, NDArray[Shape[time_steps, :attr:`length`], float64]] 770 The expectation value, $\psi^\dagger(T)\hat O\psi(T)$, and 771 the switching function, 772 $\phi_j(n\Delta t)$ 773 for all 774 $j\in\left[1,\textrm{length}\right]$ 775 and 776 $n\in\left[1,N\right]$. 777 778 Note 779 ---- 780 This function is a wrapper around the C++ function 781 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::switching_function()`. 782 """ 783 pass
784
[docs] 785class SparseUnitaryEvolver(UnitaryEvolver): 786 r""" 787 A class to store the diagonalised drift and control Hamiltonians with sparse 788 matrices and dynamic values of ``n_ctrl`` and ``dim``. On initialisation the 789 Hamiltonians are diagonalised and the eigenvectors and values stored as 790 sparse and dense matrices, respectively. This initial diagonalisation may be 791 slow and takes 792 $O(\textrm{dim}^3)$ 793 time for a 794 $\textrm{dim}\times \textrm{dim}$ 795 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion 796 to be implimented with sparse matrix multiplication and only scalar 797 exponentiation opposed to matrix exponentiation. 798 799 Note 800 ---- 801 This class is a Python wrapper around the C++ struct: 802 803 .. code-block:: cpp 804 805 Suzuki_Trotter_Evolver::UnitaryEvolver<Dynamic, Dynamic, SMatrix<Dynamic, Dynamic>> 806 807 from 808 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__. 809 810 Note 811 ---- 812 Unlike :class:`SparseUnitaryEvolver_nctrl_dim`, :attr:`n_ctrl` and 813 :attr:`dim` dynamically determined at runtime. 814 :class:`SparseUnitaryEvolver_nctrl_dim` is typically more efficient as the 815 values for :attr:`n_ctrl` and :attr:`dim` are baked in at compile time. 816 817 See Also 818 -------- 819 * :class:`SparseUnitaryEvolver_nctrl_dim` 820 * :class:`DenseUnitaryEvolver` 821 822 823 --- 824 """ 825 826 n_ctrl: int = -1 827 r""" 828 The number of control Hamiltonians used to compile the C++ backend. A value 829 of ``-1`` implies the value is not precompiled in the C++. This was the value 830 at compile time while :attr:`length` is the value at runtime time. 831 """ 832 833 dim: int = -1 834 r""" 835 The dimension of the vector space the Hamiltonians act upon used to compile 836 the C++ backend. A value of ``-1`` implies the value is not precompiled in 837 the C++. 838 """ 839 840 dim_x_n_ctrl: int = -1 841 r""" 842 The dimension of rows in each control Hamiltonian multiplied by the 843 number of control Hamiltonians upon used to compile the C++ backend. This is 844 the number of rows for the ``control_hamiltonians`` argument for 845 :meth:`__init__()`. A value of ``-1`` implies the value is not precompiled 846 in the C++. 847 848 Note 849 ---- 850 This is a wrapper around the C++ member 851 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`. 852 """ 853 854 length: int 855 r""" 856 The number of control Hamiltonians. This is the value at runtime while 857 :attr:`n_ctrl` was the value at compile time. These two values will be equal 858 unless ``n_ctrl==-1``. If ``n_ctrl==-1`` then `length` is the actual number 859 of control Hamiltonians. 860 861 Note 862 ---- 863 This is a wrapper around the C++ member 864 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::length`. 865 """ 866 867 d0: np.ndarray 868 r""" 869 The eigenvalues, 870 $\operatorname{diag}(D_0)$, 871 of the drift Hamiltonian: 872 $H_0=U_0D_0U_0^\dagger$. 873 874 Note 875 ---- 876 This is a wrapper around the C++ member 877 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::d0`. 878 879 See Also 880 -------- 881 * :attr:`ds` 882 * :attr:`u0` 883 * :attr:`u0_inverse` 884 """ 885 886 ds: list[np.ndarray] 887 r""" 888 The eigenvalues, 889 $\left(\operatorname{diag}(D_i)\right)_{i=1}^{\textrm{length}}$, 890 of the control Hamiltonians: 891 $H_i=U_iD_iU_i^\dagger$ 892 for all 893 $i\in\left[\textrm{length}\right]$. 894 895 Note 896 ---- 897 This is a wrapper around the C++ member 898 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::ds`. 899 900 See Also 901 -------- 902 * :attr:`d0` 903 * :attr:`us_individual` 904 * :attr:`us_inverse_individual` 905 * :attr:`hs` 906 """ 907 908 u0: np.ndarray 909 r""" 910 The unitary transformation, 911 $U_0$, 912 that diagonalises the drift Hamiltonian: 913 $H_0=U_0D_0U_0^\dagger$. 914 915 Note 916 ---- 917 This is a wrapper around the C++ member 918 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0`. 919 920 See Also 921 -------- 922 * :attr:`us_individual` 923 * :attr:`d0` 924 * :attr:`u0_inverse` 925 """ 926 927 u0_inverse: np.ndarray 928 r""" 929 The inverse of the unitary transformation, 930 $U_0^\dagger$, 931 that diagonalises the drift Hamiltonian: 932 $H_0=U_0D_0U_0^\dagger$. 933 934 Note 935 ---- 936 This is a wrapper around the C++ member 937 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse`. 938 939 See Also 940 -------- 941 * :attr:`us_inverse_individual` 942 * :attr:`u0` 943 * :attr:`d0` 944 """ 945 946 us: list[np.ndarray] 947 r""" 948 The unitary transformations, 949 $(U_i^\dagger U_{i-1})_{i=1}^{\textrm{length}}$, 950 from the eigen basis of 951 $H_{i-1}$ 952 to the eigen basis of 953 $H_i$. 954 955 Note 956 ---- 957 This is a wrapper around the C++ member 958 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us`. 959 960 See Also 961 -------- 962 * :attr:`us_individual` 963 * :attr:`us_inverse_individual` 964 """ 965 966 us_individual: list[np.ndarray] 967 r""" 968 The unitary transformations, 969 $\left(U_i\right)_{i=1}^{\textrm{length}}$, 970 that diagonalise the control Hamiltonians: 971 $H_i=U_iD_iU_i^\dagger$ 972 for all 973 $i\in\left[\textrm{length}\right]$. 974 975 Note 976 ---- 977 This is a wrapper around the C++ member 978 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_individual`. 979 980 See Also 981 -------- 982 * :attr:`us_inverse_individual` 983 * :attr:`us` 984 * :attr:`ds` 985 * :attr:`hs` 986 """ 987 988 us_inverse_individual: list[np.ndarray] 989 r""" 990 The inverse of the unitary transformations, 991 $(U_i^\dagger)_{i=1}^{\textrm{length}}$, 992 that diagonalise the control Hamiltonians: 993 $H_i=U_iD_iU_i^\dagger$ 994 for all 995 $i\in\left[\textrm{length}\right]$. 996 997 Note 998 ---- 999 This is a wrapper around the C++ member 1000 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::us_inverse_individual`. 1001 1002 See Also 1003 -------- 1004 * :attr:`us_individual` 1005 * :attr:`us` 1006 * :attr:`ds` 1007 * :attr:`hs` 1008 """ 1009 1010 hs: list[np.ndarray] 1011 r""" 1012 The control Hamiltonians: 1013 $H_i$ 1014 for all 1015 $i\in\left[\textrm{length}\right]$. 1016 1017 Note 1018 ---- 1019 This is a wrapper around the C++ member 1020 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::hs`. 1021 1022 See Also 1023 -------- 1024 * :attr:`us_individual` 1025 * :attr:`us_inverse_individual` 1026 * :attr:`ds` 1027 """ 1028 1029 u0_inverse_u_last: list[np.ndarray] 1030 r""" 1031 The unitary transformation, 1032 $U_0^\dagger U_{\textrm{length}}$, 1033 from the eigen basis of 1034 $H_{\textrm{length}}$ 1035 to the eigen basis of 1036 $H_0$. 1037 1038 Note 1039 ---- 1040 This is a wrapper around the C++ member 1041 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::u0_inverse_u_last`. 1042 1043 See Also 1044 -------- 1045 * :attr:`us_individual` 1046 * :attr:`us` 1047 * :attr:`u0_inverse` 1048 """ 1049
[docs] 1050 def __init__(self, 1051 drift_hamiltonian: np.ndarray[np.complex128], 1052 control_hamiltonians: np.ndarray[np.complex128]): 1053 r""" 1054 Initialises a new unitary evolver with the Hamiltonian 1055 $$ 1056 H(t)=H_0+\sum_{j=1}^{\textrm{length}}a_j(t)H_j, 1057 $$ 1058 where $H_0$ is the drift Hamiltonian and $H_j$ are the control 1059 Hamiltonians modulated by control amplitudes $a_j(t)$ which need 1060 not be specified during initialisation. 1061 1062 Parameters 1063 ---------- 1064 drift_hamiltonian : NDArray[Shape[runtime_dim, runtime_dim], complex128] 1065 The drift Hamiltonian. 1066 control_hamiltonians : NDArray[Shape[runtime_dim * :attr:`length`, runtime_dim], complex128] 1067 The control Hamiltonians. 1068 """ 1069 pass
[docs] 1070 def propagate(self, 1071 ctrl_amp: np.ndarray[np.complex128], 1072 state: np.ndarray[np.complex128], 1073 dt: float 1074 ) -> np.ndarray[np.complex128]: 1075 r""" 1076 Propagates the state vector using the first-order Suzuki-Trotter 1077 expansion. More precisely, a state vector, 1078 $\psi(0)$, 1079 is evolved under the differential equation 1080 $$ 1081 \dot\psi=-iH\psi 1082 $$ 1083 using the first-order Suzuki-Trotter expansion: 1084 $$ 1085 \begin{align} 1086 \psi(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 1087 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E\\ 1088 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 1089 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E. 1090 \end{align} 1091 $$ 1092 where 1093 $a_{nj}\coloneqq a(n\Delta t)$, 1094 we set 1095 $a_{n0}=1$ 1096 for notational ease, and the additive error 1097 $\mathcal E$ 1098 is 1099 $$ 1100 \begin{align} 1101 \mathcal E&=\mathcal O\left( 1102 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij} 1103 \norm{H_j} 1104 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik} 1105 \norm{[H_j,H_k]}\right] 1106 \right)\\ 1107 &=\mathcal O\left( 1108 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right] 1109 \right) 1110 \end{align} 1111 $$ 1112 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and 1113 $$ 1114 \begin{align} 1115 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 1116 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\ 1117 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 1118 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\ 1119 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}. 1120 \end{align} 1121 $$ 1122 Note the error is quadratic in $\Delta t$ but linear in $N$. 1123 We can also view this as being linear in $\Delta t$ and linear in 1124 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem 1125 this asymptotic error scaling will not be achieved until the time step 1126 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where 1127 $\Omega$ is the largest energy or frequency in the system. 1128 1129 Parameters 1130 ---------- 1131 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 1132 $\left(a_{ij}\right)$ The control amplitudes at each time step 1133 expressed as an $N\times\textrm{length}$ matrix where the element 1134 $a_{ij}$ corresponds to the control amplitude of the $j$th control 1135 Hamiltonian at the $i$th time step. 1136 state : NDArray[Shape[runtime_dim], complex128] 1137 $\left[\psi(0)\right]$ The state vector to propagate. 1138 dt : float 1139 ($\Delta t$) The time step to propagate by. 1140 1141 Returns 1142 ------- 1143 NDArray[Shape[runtime_dim], complex128] 1144 The propagated state vector, $\psi(N\Delta t)$. 1145 1146 Note 1147 ---- 1148 This function is a wrapper around the C++ function 1149 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate()`. 1150 1151 See Also 1152 -------- 1153 * :meth:`propagate_collection()` 1154 * :meth:`propagate_all()` 1155 """ 1156 pass
[docs] 1157 def propagate_collection(self, 1158 ctrl_amp: np.ndarray[np.complex128], 1159 states: np.ndarray[np.complex128], 1160 dt: float 1161 ) -> np.ndarray[np.complex128]: 1162 r""" 1163 Propagates a collection of state vectors using the first-order 1164 Suzuki-Trotter expansion. More precisely, a collection of state vectors, 1165 $\left(\psi_k(0)\right)_{k}$, 1166 are evolved under the differential equation 1167 $$ 1168 \dot\psi_k=-iH\psi_k 1169 $$ 1170 using the first-order Suzuki-Trotter expansion: 1171 $$ 1172 \begin{align} 1173 \psi_k(N\Delta t)&=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 1174 e^{-ia_{ij}H_j\Delta t}\psi_k(0)+\mathcal E\\ 1175 &=\prod_{i=1}^N\prod_{j=0}^{\textrm{length}} 1176 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi_k(0)+\mathcal E. 1177 \end{align} 1178 $$ 1179 where 1180 $a_{nj}\coloneqq a(n\Delta t)$, 1181 we set 1182 $a_{n0}=1$ 1183 for notational ease, and the addative error 1184 $\mathcal E$ 1185 is 1186 $$ 1187 \begin{align} 1188 \mathcal E&=\mathcal O\left( 1189 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij} 1190 \norm{H_j} 1191 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik} 1192 \norm{[H_j,H_k]}\right] 1193 \right)\\ 1194 &=\mathcal O\left( 1195 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right] 1196 \right) 1197 \end{align} 1198 $$ 1199 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and 1200 $$ 1201 \begin{align} 1202 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 1203 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\ 1204 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 1205 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\ 1206 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}. 1207 \end{align} 1208 $$ 1209 Note the error is quadratic in $\Delta t$ but linear in $N$. 1210 We can also view this as being linear in $\Delta t$ and linear in 1211 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem 1212 this asymptotic error scaling will not be achieved until the time step 1213 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where 1214 $\Omega$ is the largest energy or frequency in the system. 1215 1216 Parameters 1217 ---------- 1218 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 1219 $\left(a_{ij}\right)$ The control amplitudes at each time step 1220 expressed as an $N\times\textrm{length}$ matrix where the element 1221 $a_{ij}$ corresponds to the control amplitude of the $j$th control 1222 Hamiltonian at the $i$th time step. 1223 states : NDArray[Shape[runtime_dim, number_of_states], complex128] 1224 $\left[\left(\psi(0)\right)_{k}\right]$ A collection of state 1225 vectors to propagate expressed as a matrix with each column 1226 corresponding to a state vector. 1227 dt : float 1228 ($\Delta t$) The time step to propagate by. 1229 1230 Returns 1231 ------- 1232 NDArray[Shape[runtime_dim, number_of_states], complex128] 1233 The propagated state vectors, $\left(\psi_k(N\Delta t)\right)_k$. 1234 1235 Note 1236 ---- 1237 This function is a wrapper around the C++ function 1238 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_collection()`. 1239 1240 See Also 1241 -------- 1242 * :meth:`propagate()` 1243 * :meth:`propagate_all()` 1244 """ 1245 pass
[docs] 1246 def propagate_all(self, 1247 ctrl_amp: np.ndarray[np.complex128], 1248 state: np.ndarray[np.complex128], 1249 dt: float 1250 ) -> np.ndarray[np.complex128]: 1251 r""" 1252 Propagates the state vector using the first-order Suzuki-Trotter 1253 expansion and returns the resulting state vector at every time step. 1254 More precisely, a state vector, 1255 $\psi(0)$, 1256 is evolved under the differential equation 1257 $$ 1258 \dot\psi=-iH\psi 1259 $$ 1260 using the first-order Suzuki-Trotter expansion: 1261 $$ 1262 \begin{align} 1263 \psi(n\Delta t)&=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}} 1264 e^{-ia_{ij}H_j\Delta t}\psi(0)+\mathcal E 1265 \quad\forall n\in\left[0, N\right]\\ 1266 &=\prod_{i=1}^n\prod_{j=0}^{\textrm{length}} 1267 U_je^{-ia_{ij}D_j\Delta t}U_j^\dagger\psi(0)+\mathcal E 1268 \quad\forall n\in\left[0, N\right]. 1269 \end{align} 1270 $$ 1271 where 1272 $a_{nj}\coloneqq a(n\Delta t)$, 1273 we set 1274 $a_{n0}=1$ 1275 for notational ease, and the additive error 1276 $\mathcal E$ 1277 is 1278 $$ 1279 \begin{align} 1280 \mathcal E&=\mathcal O\left( 1281 \Delta t^2\left[\sum_{i=1}^N\sum_{j=1}^{\textrm{length}}\dot a_{ij} 1282 \norm{H_j} 1283 +\sum_{i=1}^N\sum_{j,k=0}^{\textrm{length}}a_{ij}a_{ik} 1284 \norm{[H_j,H_k]}\right] 1285 \right)\\ 1286 &=\mathcal O\left( 1287 N\Delta t^2\textrm{length}\left[\omega E+\alpha^2+E^2\right] 1288 \right) 1289 \end{align} 1290 $$ 1291 where $\dot a_{nj}\coloneqq\dot a_j(n\Delta t)$ and 1292 $$ 1293 \begin{align} 1294 \omega&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 1295 j\in\left[1,\textrm{length}\right]}}\left|\dot a_{ij}\right|,\\ 1296 \alpha&\coloneqq\max_{\substack{i\in\left[1,N\right]\\ 1297 j\in\left[0,\textrm{length}\right]}}\left|a_{ij}\right|,\\ 1298 E&\coloneqq\max_{j\in\left[0,\textrm{length}\right]}\norm{H_j}. 1299 \end{align} 1300 $$ 1301 Note the error is quadratic in $\Delta t$ but linear in $N$. 1302 We can also view this as being linear in $\Delta t$ and linear in 1303 total evolution time $N\Delta t$. Additionally, by Nyquist's theorem 1304 this asymptotic error scaling will not be achieved until the time step 1305 $\Delta t$ is smaller than $\frac{1}{2\Omega}$ where 1306 $\Omega$ is the largest energy or frequency in the system. 1307 1308 Parameters 1309 ---------- 1310 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 1311 $\left(a_{ij}\right)$ The control amplitudes at each time step 1312 expressed as an $N\times\textrm{length}$ matrix where the element 1313 $a_{ij}$ corresponds to the control amplitude of the $j$th control 1314 Hamiltonian at the $i$th time step. 1315 state : NDArray[Shape[runtime_dim], complex128] 1316 $\left[\psi(0)\right]$ The state vector to propagate. 1317 dt : float 1318 ($\Delta t$) The time step to propagate by. 1319 1320 Returns 1321 ------- 1322 NDArray[Shape[runtime_dim, time_steps + 1], complex128] 1323 The propagated state vector at each time step, 1324 $\left(\psi(n\Delta t)\right)_{n=0}^N$. 1325 1326 Note 1327 ---- 1328 This function is a wrapper around the C++ function 1329 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::propagate_all()`. 1330 1331 See Also 1332 -------- 1333 * :meth:`propagate()` 1334 * :meth:`propagate_collection()` 1335 """ 1336 pass
[docs] 1337 def evolved_expectation_value(self, 1338 ctrl_amp: np.ndarray[np.complex128], 1339 state: np.ndarray[np.complex128], 1340 dt: float, 1341 observable: np.ndarray[np.complex128] 1342 ) -> complex: 1343 r""" 1344 Calculates the expectation value with respect to an observable of an 1345 evolved state vector evolved under a control Hamiltonian modulated by 1346 the control amplitudes. The integration is performed using 1347 :meth:`propagate()`. 1348 1349 Parameters 1350 ---------- 1351 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 1352 $\left(a_{ij}\right)$ The control amplitudes at each time step 1353 expressed as an $N\times\textrm{length}$ matrix where the element 1354 $a_{ij}$ corresponds to the control amplitude of the $j$th control 1355 Hamiltonian at the $i$th time step. 1356 state : NDArray[Shape[runtime_dim], complex128] 1357 $\left[\psi(0)\right]$ The state vector to propagate. 1358 dt : float 1359 ($\Delta t$) The time step to propagate by. 1360 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128] 1361 $(\hat O)$ The observable to calculate the expectation value of. 1362 1363 Returns 1364 ------- 1365 complex 1366 The expectation value of the observable, $\langle\hat O\rangle 1367 \equiv\psi^\dagger(N\Delta t)\hat O\psi(N\Delta t)$. 1368 1369 Note 1370 ---- 1371 This function is a wrapper around the C++ function 1372 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value()`. 1373 1374 See Also 1375 -------- 1376 * :meth:`evolved_expectation_value_all()` 1377 """ 1378 pass
[docs] 1379 def evolved_expectation_value_all(self, 1380 ctrl_amp: np.ndarray[np.complex128], 1381 state: np.ndarray[np.complex128], 1382 dt: float, 1383 observable: np.ndarray[np.complex128] 1384 ) -> np.ndarray[np.complex128]: 1385 r""" 1386 Calculates the expectation values with respect to an observable of a 1387 time series of state vectors evolved under a control Hamiltonian 1388 modulated by the control amplitudes. The integration is performed using 1389 :meth:`propagate_all()`. 1390 1391 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 1392 $\left(a_{ij}\right)$ The control amplitudes at each time step 1393 expressed as an $N\times\textrm{length}$ matrix where the element 1394 $a_{ij}$ corresponds to the control amplitude of the $j$th control 1395 Hamiltonian at the $i$th time step. 1396 state : NDArray[Shape[runtime_dim], complex128] 1397 $\left[\psi(0)\right]$ The state vector to propagate. 1398 dt : float 1399 ($\Delta t$) The time step to propagate by. 1400 observable : NDArray[Shape[runtime_dim, runtime_dim], complex128] 1401 $(\hat O)$ The observable to calculate the expectation value of. 1402 1403 Returns 1404 ------- 1405 NDArray[Shape[time_steps + 1], complex128] 1406 The expectation value of the observable, 1407 $\left(\psi^\dagger(n\Delta t)\hat O\psi(N\Delta t)\right)_{n=0}^N$. 1408 1409 Note 1410 ---- 1411 This function is a wrapper around the C++ function 1412 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::evolved_expectation_value_all()`. 1413 1414 See Also 1415 -------- 1416 * :meth:`evolved_expectation_value()` 1417 """ 1418 pass
[docs] 1419 def switching_function(self, 1420 ctrl_amp: np.ndarray[np.complex128], 1421 state: np.ndarray[np.complex128], 1422 dt: float, 1423 cost: np.ndarray[np.complex128] 1424 ) -> tuple[complex, np.ndarray[np.float64]]: 1425 r""" 1426 Calculates the switching function for a Mayer problem with an 1427 expectation value as the cost function. More precisely if the cost 1428 function is 1429 $$ 1430 J\left[\vec a(t)\right]\coloneqq\langle\hat O\rangle 1431 \equiv\psi^\dagger[\vec a(t);T] 1432 \hat O\psi[\vec a(t);T], 1433 $$ 1434 where $T=N\Delta t$, then the switching function is 1435 $$ 1436 \phi_j(t)\coloneqq\frac{\delta J}{\delta a_j(t)} 1437 =2\operatorname{Im}\left(\psi^\dagger[\vec a(t);T] 1438 \hat OU(t\to T)H_j\psi[\vec a(t);t]\right). 1439 $$ 1440 using the first-order Suzuki-Trotter expansion we can express the 1441 switching function as 1442 $$ 1443 \begin{align} 1444 &\phi_j(n\Delta t)=\frac{1}{\Delta t}\pdv{J}{a_{nj}}\\ 1445 &=\!2\operatorname{Im}\!\left(\psi^\dagger(T) 1446 \hat O\!\!\left[\prod_{i>n}^N\prod_{k=1}^{\textrm{length}} 1447 e^{-ia_{ik}H_k\Delta t}\right]\!\!\! 1448 \left[\prod_{k=j}^{\textrm{length}} 1449 e^{-ia_{nk}H_k\Delta t}\right]\!H_j\!\! 1450 \left[\prod_{k=0}^{j-1} 1451 e^{-ia_{nk}H_k\Delta t}\right] 1452 \!\psi(\left[n-1\right]\Delta t)\right), 1453 \end{align} 1454 $$ 1455 where for numerical efficiency we replace 1456 $e^{-ia_{ik}H_k\Delta t}$ 1457 with 1458 $U_ke^{-ia_{ik}D_k\Delta t}U_k^\dagger$ 1459 as in :meth:`propagate()`. 1460 1461 Parameters 1462 ---------- 1463 ctrl_amp : NDArray[Shape[time_steps, :attr:`length`], complex128] 1464 $\left(a_{ij}\right)$ The control amplitudes at each time step 1465 expressed as an $N\times\textrm{length}$ matrix where the element 1466 $a_{ij}$ corresponds to the control amplitude of the $j$th control 1467 Hamiltonian at the $i$th time step. 1468 state : NDArray[Shape[runtime_dim], complex128] 1469 $\left[\psi(0)\right]$ The initial state vector. 1470 dt : float 1471 ($\Delta t$) The time step. 1472 cost : NDArray[Shape[runtime_dim, runtime_dim], complex128] 1473 $(\hat O)$ The observable to calculate the expectation value of. 1474 1475 Returns 1476 ------- 1477 tuple[complex, NDArray[Shape[time_steps, :attr:`length`], float64]] 1478 The expectation value, $\psi^\dagger(T)\hat O\psi(T)$, and 1479 the switching function, 1480 $\phi_j(n\Delta t)$ 1481 for all 1482 $j\in\left[1,\textrm{length}\right]$ 1483 and 1484 $n\in\left[1,N\right]$. 1485 1486 Note 1487 ---- 1488 This function is a wrapper around the C++ function 1489 :cpp:func:`Suzuki_Trotter_Evolver::UnitaryEvolver::switching_function()`. 1490 """ 1491 pass
1492
[docs] 1493class DenseUnitaryEvolver_nctrl_dim(DenseUnitaryEvolver): 1494 r""" 1495 A class to store the diagonalised drift and control Hamiltonians with dense 1496 matrices and precompiled values of ``n_ctrl`` and ``dim``. 1497 1498 Important 1499 --------- 1500 This is not the actual class name ``nctrl`` and ``dim`` should be replaced 1501 with positive integers to specify their values. For example, 1502 ``DenseUnitaryEvolver_3_2`` is a valid class name with :attr:`n_ctrl` 1503 ``=3`` control Hamiltonians acting on a :attr:`dim` ``=2`` dimensional 1504 vector space. If only one of ``nctrl`` and ``dim`` is precompiled the other 1505 should be specified as `Dynamic`. For example, 1506 ``DenseUnitaryEvolver_Dynamic_2`` is a valid class name with a dynamic 1507 number of control Hamiltonians (:attr:`n_ctrl` ``=-1``) acting on a 1508 :attr:`dim` ``=2`` dimensional vector space. 1509 1510 1511 On initialisation the Hamiltonians are diagonalised and the eigenvectors and 1512 values stored as dense matrices. This initial diagonalisation may be slow 1513 and takes 1514 $O(\textrm{dim}^3)$ 1515 time for a 1516 $\textrm{dim}\times \textrm{dim}$ 1517 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion 1518 to be implimented in 1519 $O(\textrm{dim}^2)$ 1520 time with matrix multiplication and only scalar exponentiation opposed to 1521 matrix exponentiation which takes 1522 $O(\textrm{dim}^3)$ 1523 time. 1524 1525 Note 1526 ---- 1527 This class is a Python wrapper around the C++ struct: 1528 1529 .. code-block:: cpp 1530 1531 Suzuki_Trotter_Evolver::UnitaryEvolver<n_ctrl, dim, DMatrix<dim, dim>> 1532 1533 from 1534 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__. 1535 1536 Note 1537 ---- 1538 Unlike :class:`DenseUnitaryEvolver`, :attr:`n_ctrl` and :attr:`dim` are 1539 baked into the class when the C++ code is compiled allowing for more 1540 efficient state propagation. 1541 1542 See Also 1543 -------- 1544 * :class:`SparseUnitaryEvolver_nctrl_dim` 1545 * :class:`DenseUnitaryEvolver` 1546 1547 1548 --- 1549 """ 1550 1551 n_ctrl: int = None 1552 r""" 1553 The number of control Hamiltonians used to compile the C++ backend. Equal to 1554 the value in the class name. A value of ``-1`` represents ``Dynamic`` in the 1555 class name and implies the value is not precompiled in the C++. This was the 1556 value at compile time while :attr:`length` is the value at runtime time. 1557 """ 1558 1559 dim: int = None 1560 r""" 1561 The dimension of the vector space the Hamiltonians act upon used to compile 1562 the C++ backend. Equal to the value in the class name. A value of ``-1`` 1563 represents ``Dynamic`` in the class name and implies the value is not 1564 precompiled in the C++. 1565 """ 1566 1567 dim_x_n_ctrl: int = None 1568 r""" 1569 The dimension of rows in each control Hamiltonian multiplied by the 1570 number of control Hamiltonians upon used to compile the C++ backend. This is 1571 the number of rows for the ``control_hamiltonians`` argument for 1572 :meth:`__init__()`. A value of ``-1`` implies the value is not precompiled 1573 in the C++. 1574 1575 Note 1576 ---- 1577 This is a wrapper around the C++ member 1578 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`. 1579 """
1580
[docs] 1581class SparseUnitaryEvolver_nctrl_dim(SparseUnitaryEvolver): 1582 r""" 1583 A class to store the diagonalised drift and control Hamiltonians with sparse 1584 matrices and precompiled values of ``n_ctrl`` and ``dim``. 1585 1586 Important 1587 --------- 1588 This is not the actual class name ``nctrl`` and ``dim`` should be replaced 1589 with positive integers to specify their values. For example, 1590 ``SparseUnitaryEvolver_3_2`` is a valid class name with :attr:`n_ctrl` 1591 ``=3`` control Hamiltonians acting on a :attr:`dim` ``=2`` dimensional 1592 vector space. If only one of ``nctrl`` and ``dim`` is precompiled the other 1593 should be specified as `Dynamic`. For example, 1594 ``SparseUnitaryEvolver_Dynamic_2`` is a valid class name with a dynamic 1595 number of control Hamiltonians (:attr:`n_ctrl` ``=-1``) acting on a 1596 :attr:`dim` ``=2`` dimensional vector space. 1597 1598 1599 On initialisation the Hamiltonians are diagonalised and the eigenvectors and 1600 values stored as sparse and dense matrices, respectively. This initial 1601 diagonalisation may be slow and takes 1602 $O(\textrm{dim}^3)$ 1603 time for a 1604 $\textrm{dim}\times \textrm{dim}$ 1605 Hamiltonian. However, it allows each step of the Suzuki-Trotter expansion 1606 to be implimented with sparse matrix multiplication and only scalar 1607 exponentiation opposed to matrix exponentiation. 1608 1609 Note 1610 ---- 1611 This class is a Python wrapper around the C++ struct: 1612 1613 .. code-block:: cpp 1614 1615 Suzuki_Trotter_Evolver::UnitaryEvolver<n_ctrl, dim, SMatrix> 1616 1617 from 1618 `Suzuki-Trotter-Evolver <https://github.com/Christopher-K-Long/Suzuki-Trotter-Evolver>`__. 1619 1620 Note 1621 ---- 1622 Unlike :class:`SparseUnitaryEvolver`, :attr:`n_ctrl` and :attr:`dim` are 1623 baked into the class when the C++ code is compiled allowing for more 1624 efficient state propagation. 1625 1626 See Also 1627 -------- 1628 * :class:`DenseUnitaryEvolver_nctrl_dim` 1629 * :class:`SparseUnitaryEvolver` 1630 1631 1632 --- 1633 """ 1634 1635 n_ctrl: int = None 1636 r""" 1637 The number of control Hamiltonians used to compile the C++ backend. Equal to 1638 the value in the class name. A value of ``-1`` represents ``Dynamic`` in the 1639 class name and implies the value is not precompiled in the C++. This was the 1640 value at compile time while :attr:`length` is the value at runtime time. 1641 """ 1642 1643 dim: int = None 1644 r""" 1645 The dimension of the vector space the Hamiltonians act upon used to compile 1646 the C++ backend. Equal to the value in the class name. A value of ``-1`` 1647 represents ``Dynamic`` in the class name and implies the value is not 1648 precompiled in the C++. 1649 """ 1650 1651 dim_x_n_ctrl: int = None 1652 r""" 1653 The dimension of rows in each control Hamiltonian multiplied by the 1654 number of control Hamiltonians upon used to compile the C++ backend. This is 1655 the number of rows for the ``control_hamiltonians`` argument for :meth:`__init__()`. A value 1656 of ``-1`` implies the value is not precompiled in the C++. 1657 1658 Note 1659 ---- 1660 This is a wrapper around the C++ member 1661 :cpp:member:`Suzuki_Trotter_Evolver::UnitaryEvolver::dim_x_n_ctrl`. 1662 """