diff --git a/src/openfermion/circuits/gates/fermionic_simulation.py b/src/openfermion/circuits/gates/fermionic_simulation.py index e1332ac4b..b54594266 100644 --- a/src/openfermion/circuits/gates/fermionic_simulation.py +++ b/src/openfermion/circuits/gates/fermionic_simulation.py @@ -184,18 +184,18 @@ def sum_of_interaction_operator_gate_generators( @cirq.value_equality(approximate=True) class ParityPreservingFermionicGate(cirq.Gate, metaclass=abc.ABCMeta): - r"""The Jordan-Wigner transform of :math:`\exp(-i H)` for a fermionic - Hamiltonian :math:`H`. + r"""The Jordan-Wigner transform of $\exp(-i H)$ for a fermionic + Hamiltonian $H$. - Each subclass corresponds to a set of generators :math:`\{G_i\}` - corresponding to the family of Hamiltonians :math:`\sum_i w_i G_i + - \text{h.c.}`, where the weights :math:`w_i \in \mathbb C` are specified by + Each subclass corresponds to a set of generators $\{G_i\}$ + corresponding to the family of Hamiltonians $\sum_i w_i G_i + + \text{h.c.}$, where the weights $w_i \in \mathbb C$ are specified by the instance. - The Jordan-Wigner mapping maps the fermionic modes :math:`(0, \ldots, n - - 1)` to qubits :math:`(0, \ldots, n - 1)`, respectively. + The Jordan-Wigner mapping maps the fermionic modes $(0, \ldots, n - + 1)$ to qubits $(0, \ldots, n - 1)$, respectively. - Each generator :math:`G_i` must be a linear combination of fermionic + Each generator $G_i$ must be a linear combination of fermionic monomials consisting of an even number of creation/annihilation operators. This is so that the Jordan-Wigner transform acts only on the gate's qubits, even when the fermionic modes are offset as part of a larger Jordan-Wigner @@ -231,8 +231,8 @@ def __init__( @staticmethod @abc.abstractmethod def fermion_generator_components() -> Tuple['openfermion.FermionOperator']: - r"""The FermionOperators :math:`(G_i)_i` such that the gate's fermionic - generator is :math:`\sum_i w_i G_i + \text{h.c.}` where :math:`(w_i)_i` + r"""The FermionOperators $(G_i)_i$ such that the gate's fermionic + generator is $\sum_i w_i G_i + \text{h.c.}$ where $(w_i)_i$ are the gate's weights.""" @abc.abstractmethod @@ -360,8 +360,8 @@ def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs class InteractionOperatorFermionicGate(ParityPreservingFermionicGate): - r"""The Jordan-Wigner transform of :math:`\exp(-i H)` for a fermionic - Hamiltonian :math:`H`, where :math:`H` is an interaction operator. + r"""The Jordan-Wigner transform of $\exp(-i H)$ for a fermionic + Hamiltonian $H$, where $H$ is an interaction operator. See openfermion.ParityPreservingFermionicGate and openfermion.InteractionOperator for more details. @@ -401,27 +401,30 @@ class QuadraticFermionicSimulationGate(InteractionOperatorFermionicGate, cirq.TwoQubitGate, cirq.EigenGate): r"""``(w0 |10⟩⟨01| + h.c.) + w1 |11⟩⟨11|`` interaction. - With weights :math:`(w_0, w_1)` and exponent :math:`t`, this gate's matrix + With weights $(w_0, w_1)$ and exponent $t$, this gate's matrix is defined as - .. math:: + $$ e^{-i t H}, + $$ where - .. math:: + $$ H = \left(w_0 \left| 10 \right\rangle\left\langle 01 \right| + \text{h.c.}\right) - w_1 \left| 11 \right\rangle \left\langle 11 \right|. + $$ This corresponds to the Jordan-Wigner transform of - .. math:: + $$ H = (w_0 a^{\dagger}_i a_{i+1} + \text{h.c.}) + w_1 a_{i}^{\dagger} a_{i+1}^{\dagger} a_{i} a_{i+1}, + $$ - where :math:`a_i` and :math:`a_{i+1}` are the annihilation operators for - the fermionic modes :math:`i` and :math:`(i+1)`, respectively mapped to the + where $a_i$ and $a_{i+1}$ are the annihilation operators for + the fermionic modes $i$ and $(i+1)$, respectively mapped to the first and second qubits on which this gate acts. Args: @@ -532,34 +535,37 @@ class CubicFermionicSimulationGate(InteractionOperatorFermionicGate, cirq.ThreeQubitGate, cirq.EigenGate): r"""``w0|110⟩⟨101| + w1|110⟩⟨011| + w2|101⟩⟨011|`` + h.c. interaction. - With weights :math:`(w_0, w_1, w_2)` and exponent :math:`t`, this gate's + With weights $(w_0, w_1, w_2)$ and exponent $t$, this gate's matrix is defined as - .. math:: + $$ e^{-i t H}, + $$ where - .. math:: + $$ H = \left(w_0 \left| 110 \right\rangle\left\langle 101 \right| + \text{h.c.}\right) + \left(w_1 \left| 110 \right\rangle\left\langle 011 \right| + \text{h.c.}\right) + \left(w_2 \left| 101 \right\rangle\left\langle 011 \right| + \text{h.c.}\right) + $$ This corresponds to the Jordan-Wigner transform of - .. math:: + $$ H = -\left(w_0 a^{\dagger}_i a^{\dagger}_{i+1} a_{i} a_{i+2} + \text{h.c.}\right) - \left(w_1 a^{\dagger}_i a^{\dagger}_{i+1} a_{i+1} a_{i+2} + \text{h.c.}\right) - \left(w_2 a^{\dagger}_i a^{\dagger}_{i+2} a_{i+1} a_{i+2} + \text{h.c.}\right), + $$ - where :math:`a_i`, :math:`a_{i+1}`, :math:`a_{i+2}` are the annihilation - operators for the fermionic modes :math:`i`, :math:`(i+1)` :math:`(i+2)`, + where $a_i$, $a_{i+1}$, $a_{i+2}$ are the annihilation + operators for the fermionic modes $i$, $(i+1)$ $(i+2)$, respectively mapped to the three qubits on which this gate acts. Args: @@ -681,34 +687,37 @@ class QuarticFermionicSimulationGate(InteractionOperatorFermionicGate, cirq.EigenGate): r"""Rotates Hamming-weight 2 states into their bitwise complements. - With weights :math:`(w_0, w_1, w_2)` and exponent :math:`t`, this gate's + With weights $(w_0, w_1, w_2)$ and exponent $t$, this gate's matrix is defined as - .. math:: + $$ e^{-i t H}, + $$ where - .. math:: + $$ H = \left(w_0 \left| 1001 \right\rangle\left\langle 0110 \right| + \text{h.c.}\right) + \left(w_1 \left| 1010 \right\rangle\left\langle 0101 \right| + \text{h.c.}\right) + \left(w_2 \left| 1100 \right\rangle\left\langle 0011 \right| + \text{h.c.}\right) + $$ This corresponds to the Jordan-Wigner transform of - .. math:: + $$ H = -\left(w_0 a^{\dagger}_i a^{\dagger}_{i+3} a_{i+1} a_{i+2} + \text{h.c.}\right) - \left(w_1 a^{\dagger}_i a^{\dagger}_{i+2} a_{i+1} a_{i+3} + \text{h.c.}\right) - \left(w_2 a^{\dagger}_i a^{\dagger}_{i+1} a_{i+2} a_{i+3} + \text{h.c.}\right), + $$ - where :math:`a_i`, ..., :math:`a_{i+3}` are the annihilation operators for - the fermionic modes :math:`i`, ..., :math:`(i+3)`, respectively + where $a_i$, ..., $a_{i+3}$ are the annihilation operators for + the fermionic modes $i$, ..., $(i+3)$, respectively mapped to the four qubits on which this gate acts. diff --git a/src/openfermion/circuits/low_rank.py b/src/openfermion/circuits/low_rank.py index bf4a25dce..b88c2eb80 100644 --- a/src/openfermion/circuits/low_rank.py +++ b/src/openfermion/circuits/low_rank.py @@ -22,10 +22,10 @@ def get_chemist_two_body_coefficients(two_body_coefficients, spin_basis=True): r"""Convert two-body operator coefficients to low rank tensor. The input is a two-body fermionic Hamiltonian expressed as - :math:`\sum_{pqrs} h_{pqrs} a^\dagger_p a^\dagger_q a_r a_s` + $\sum_{pqrs} h_{pqrs} a^\dagger_p a^\dagger_q a_r a_s$ We will convert this to the chemistry convention expressing it as - :math:`\sum_{pqrs} g_{pqrs} a^\dagger_p a_q a^\dagger_r a_s` + $\sum_{pqrs} g_{pqrs} a^\dagger_p a_q a^\dagger_r a_s$ but without the spin degree of freedom. In the process of performing this conversion, constants and one-body @@ -33,15 +33,15 @@ def get_chemist_two_body_coefficients(two_body_coefficients, spin_basis=True): Args: two_body_coefficients (ndarray): an N x N x N x N - numpy array giving the :math:`h_{pqrs}` tensor. + numpy array giving the $h_{pqrs}$ tensor. spin_basis (bool): True if the two-body terms are passed in spin orbital basis. False if already in spatial orbital basis. Returns: one_body_correction (ndarray): an N x N array of floats giving - coefficients of the :math:`a^\dagger_p a_q` terms that come out. + coefficients of the $a^\dagger_p a_q$ terms that come out. chemist_two_body_coefficients (ndarray): an N x N x N x N numpy array - giving the :math:`g_{pqrs}` tensor in chemist notation. + giving the $g_{pqrs}$ tensor in chemist notation. Raises: TypeError: Input must be two-body number conserving @@ -80,14 +80,14 @@ def low_rank_two_body_decomposition(two_body_coefficients, r"""Convert two-body operator into sum of squared one-body operators. As in arXiv:1808.02625, this function decomposes - :math:`\sum_{pqrs} h_{pqrs} a^\dagger_p a^\dagger_q a_r a_s` as - :math:`\sum_{l} \lambda_l (\sum_{pq} g_{lpq} a^\dagger_p a_q)^2` + $\sum_{pqrs} h_{pqrs} a^\dagger_p a^\dagger_q a_r a_s$ as + $\sum_{l} \lambda_l (\sum_{pq} g_{lpq} a^\dagger_p a_q)^2$ l is truncated to take max value L so that - :math:`\sum_{l=0}^{L-1} (\sum_{pq} |g_{lpq}|)^2 |\lambda_l| < x` + $\sum_{l=0}^{L-1} (\sum_{pq} |g_{lpq}|)^2 |\lambda_l| < x$ Args: two_body_coefficients (ndarray): an N x N x N x N - numpy array giving the :math:`h_{pqrs}` tensor. + numpy array giving the $h_{pqrs}$ tensor. This tensor must be 8-fold symmetric (real integrals). truncation_threshold (optional Float): the value of x, above. final_rank (optional int): if provided, this specifies the value of @@ -97,13 +97,13 @@ def low_rank_two_body_decomposition(two_body_coefficients, Returns: eigenvalues (ndarray of floats): length L array - giving the :math:`\lambda_l`. + giving the $\lambda_l$. one_body_squares (ndarray of floats): L x N x N array of floats - corresponding to the value of :math:`g_{pql}`. + corresponding to the value of $g_{pql}$. one_body_correction (ndarray): One-body correction terms that result from reordering to chemist ordering, in spin-orbital basis. truncation_value (float): after truncation, this is the value - :math:`\sum_{l=0}^{L-1} (\sum_{pq} |g_{lpq}|)^2 |\lambda_l| < x` + $\sum_{l=0}^{L-1} (\sum_{pq} |g_{lpq}|)^2 |\lambda_l| < x$ Raises: TypeError: Invalid two-body coefficient tensor specification. @@ -163,22 +163,22 @@ def prepare_one_body_squared_evolution(one_body_matrix, spin_basis=True): r"""Get Givens angles and DiagonalHamiltonian to simulate squared one-body. The goal here will be to prepare to simulate evolution under - :math:`(\sum_{pq} h_{pq} a^\dagger_p a_q)^2` by decomposing as - :math:`R e^{-i \sum_{pq} V_{pq} n_p n_q} R^\dagger' where - :math:`R` is a basis transformation matrix. + $(\sum_{pq} h_{pq} a^\dagger_p a_q)^2$ by decomposing as + $R e^{-i \sum_{pq} V_{pq} n_p n_q} R^\dagger$ where + $R$ is a basis transformation matrix. TODO: Add option for truncation based on one-body eigenvalues. Args: one_body_matrix (ndarray of floats): an N by N array storing the coefficients of a one-body operator to be squared. For instance, - in the above the elements of this matrix are :math:`h_{pq}`. + in the above the elements of this matrix are $h_{pq}$. spin_basis (bool): Whether the matrix is passed in the spin orbital basis. Returns: density_density_matrix(ndarray of floats) an N by N array storing - the diagonal two-body coefficeints :math:`V_{pq}` above. + the diagonal two-body coefficeints $V_{pq}$ above. basis_transformation_matrix (ndarray of floats) an N by N array storing the values of the basis transformation. diff --git a/src/openfermion/circuits/primitives/bogoliubov_transform.py b/src/openfermion/circuits/primitives/bogoliubov_transform.py index 780172822..a46b6ccb2 100644 --- a/src/openfermion/circuits/primitives/bogoliubov_transform.py +++ b/src/openfermion/circuits/primitives/bogoliubov_transform.py @@ -28,41 +28,41 @@ def bogoliubov_transform( r"""Perform a Bogoliubov transformation. This circuit performs the transformation to a basis determined by a new set - of fermionic ladder operators. It performs the unitary :math:`U` such that - - .. math:: + of fermionic ladder operators. It performs the unitary $U$ such that + $$ U a^\dagger_p U^{-1} = b^\dagger_p + $$ - where the :math:`a^\dagger_p` are the original creation operators and the - :math:`b^\dagger_p` are the new creation operators. The new creation + where the $a^\dagger_p$ are the original creation operators and the + $b^\dagger_p$ are the new creation operators. The new creation operators are linear combinations of the original ladder operators with coefficients given by the matrix `transformation_matrix`, which will be - referred to as :math:`W` in the following. + referred to as $W$ in the following. - If :math:`W` is an `N \times N` matrix, then the :math:`b^\dagger_p` are + If $W$ is an $N \times N$ matrix, then the $b^\dagger_p$ are given by - .. math:: - + $$ b^\dagger_p = \sum_{q=1}^N W_{pq} a^\dagger_q. + $$ - If :math:`W` is an `N \times 2N` matrix, then the :math:`b^\dagger_p` are + If $W$ is an $N \times 2N$ matrix, then the $b^\dagger_p$ are given by - .. math:: - + $$ b^\dagger_p = \sum_{q=1}^N W_{pq} a^\dagger_q + \sum_{q=N+1}^{2N} W_{pq} a_q. + $$ This algorithm assumes the Jordan-Wigner Transform. Args: qubits: The qubits to which to apply the circuit. - transformation_matrix: The matrix :math:`W` holding the coefficients + transformation_matrix: The matrix $W$ holding the coefficients that describe the new creation operators in terms of the original - ladder operators. Its shape should be either :math:`NxN` or - :math:`Nx(2N)`, where :math:`N` is the number of qubits. + ladder operators. Its shape should be either $NxN$ or + $Nx(2N)$, where $N$ is the number of qubits. initial_state: Optionally specifies a computational basis state to assume that the qubits start in. This assumption enables optimizations that result in a circuit with fewer gates. diff --git a/src/openfermion/circuits/primitives/ffft.py b/src/openfermion/circuits/primitives/ffft.py index e45cecc93..b1aeb36cc 100644 --- a/src/openfermion/circuits/primitives/ffft.py +++ b/src/openfermion/circuits/primitives/ffft.py @@ -25,42 +25,48 @@ class _F0Gate(cirq.MatrixGate): r"""Two-qubit gate that performs fermionic Fourier transform of size 2. - Realizes unitary gate :math:`F_0` that transforms Fermionic creation - operators :math:`a_0^\dagger` and :math:`a_1^\dagger` according to: + Realizes unitary gate $F_0$ that transforms Fermionic creation + operators $a_0^\dagger$ and $a_1^\dagger$ according to: - .. math:: + $$ F_0^\dagger a_0^\dagger F_0 = {1 \over \sqrt{2}} (a_0^\dagger + a_1^\dagger) + $$ - .. math:: + $$ F_0^\dagger a_1^\dagger F_0 = {1 \over \sqrt{2}} (a_0^\dagger - a_1^\dagger) \, . + $$ This gate assumes JWT representation of fermionic modes which are big-endian encoded on consecutive qubits: - :math:`a_0^\dagger \lvert 00 \rangle = \lvert 10 \rangle` and - :math:`a_1^\dagger \lvert 00 \rangle = \vert 01 \rangle`. + $a_0^\dagger \lvert 00 \rangle = \lvert 10 \rangle$ and + $a_1^\dagger \lvert 00 \rangle = \vert 01 \rangle$. - Internally, this leads to expansion of :math:`F_0^\dagger`: + Internally, this leads to expansion of $F_0^\dagger$: - .. math:: + $$ \langle 00 \rvert F_0^\dagger \lvert 00 \rangle = 1 + $$ - .. math:: + $$ \langle 01 \rvert F_0^\dagger \lvert 01 \rangle = -{1 \over \sqrt{2}} + $$ - .. math:: + $$ \langle 10 \rvert F_0^\dagger \lvert 10 \rangle = \langle 10 \rvert F_0^\dagger \lvert 01 \rangle = \langle 01 \rvert F_0^\dagger \lvert 10 \rangle = {1 \over \sqrt{2}} + $$ - .. math:: + $$ \langle 11 \rvert F_0^\dagger \lvert 11 \rangle = -1 \, . + $$ """ def __init__(self): - """Initializes :math:`F_0` gate.""" + """Initializes $F_0$ gate.""" cirq.MatrixGate.__init__(self, np.array([[1, 0, 0, 0], [0, -2**(-0.5), 2**(-0.5), 0], @@ -80,12 +86,13 @@ def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs class _TwiddleGate(cirq.SingleQubitGate): r"""Gate that introduces arbitrary FFT twiddle factors. - Realizes unitary gate :math:`\omega^{k\dagger}_n` that phases creation - operator :math:`a^\dagger_x` according to: + Realizes unitary gate $\omega^{k\dagger}_n$ that phases creation + operator $a^\dagger_x$ according to: - .. math:: + $$ \omega^{k\dagger}_n a^\dagger_x \omega^k_n = e^{-2 \pi i {k \over n}} a^\dagger_x \, . + $$ Under JWT representation this is realized by appropriately rotated pauli Z gate acting on qubit x. @@ -123,27 +130,28 @@ def ffft(qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE: Generates a circuit that performs fast fermionic Fourier transform (FFFT) which transforms a set of fermionic creation operators - :math:`\hat{a}_n^\dagger`, :math:`n \in 1, 2, \dots, N` according to: + $\hat{a}_n^\dagger$, $n \in 1, 2, \dots, N$ according to: - .. math:: + $$ \mathit{FFFT}^\dagger \tilde{a}_k^\dagger \mathit{FFFT} = {1 \over \sqrt{N}} \sum_{n=0}^{N-1} e^{-i {2\pi \over N} n k} \hat{a}^\dagger_n \, , + $$ - where :math:`\tilde{a}_k^\dagger` are transformed operators and :math:`N` is + where $\tilde{a}_k^\dagger$ are transformed operators and $N$ is size of the input `qubits` sequence. This function assumes JWT representation of fermionic modes which are big-endian encoded on consecutive qubits: - :math:`a_0^\dagger \lvert 0.. \rangle = \lvert 1.. \rangle`, - :math:`a_1^\dagger \lvert 0.. \rangle = \vert 01.. \rangle`, - :math:`a_2^\dagger \lvert 0.. \rangle = \vert 001.. \rangle`, :math:`\dots`. + $a_0^\dagger \lvert 0.. \rangle = \lvert 1.. \rangle$, + $a_1^\dagger \lvert 0.. \rangle = \vert 01.. \rangle$, + $a_2^\dagger \lvert 0.. \rangle = \vert 001.. \rangle$, $\dots$. - The gate count of generated circuit is :math:`\theta(N^2)`, generated - circuit depth is :math:`\theta(N)` and distinct gates count is - :math:`\theta(N_1^2 + N_2^2 + \dots + N_n^2)`, where - :math:`N = N_1 N_2 \dots N_n` is prime decomposition of :math:`N`. In a case - where :math:`N` is some power of 2, it reduces to :math:`\theta(\log(N))`. + The gate count of generated circuit is $\theta(N^2)$, generated + circuit depth is $\theta(N)$ and distinct gates count is + $\theta(N_1^2 + N_2^2 + \dots + N_n^2)$, where + $N = N_1 N_2 \dots N_n$ is prime decomposition of $N$. In a case + where $N$ is some power of 2, it reduces to $\theta(\log(N))$. An equivalent circuit can be generated using the `openfermion.bogoliubov_transform` function with appropriately prepared @@ -165,7 +173,7 @@ def fourier_transform_matrix(size): The advantage of circuit generated by the FFFT algorithm over the one created with the `bogoliubov_transform` is that a smaller variety of gates - is created, which is :math:`O(N^2)` in case of pure `bogoliubov_transform`. + is created, which is $O(N^2)$ in case of pure `bogoliubov_transform`. This implementation of `FFFT` fall-backs to the `bogoliubov_transform` for the inputs where qubits length is prime. diff --git a/src/openfermion/circuits/primitives/optimal_givens_decomposition.py b/src/openfermion/circuits/primitives/optimal_givens_decomposition.py index 615853379..35a6abd13 100644 --- a/src/openfermion/circuits/primitives/optimal_givens_decomposition.py +++ b/src/openfermion/circuits/primitives/optimal_givens_decomposition.py @@ -40,9 +40,9 @@ def optimal_givens_decomposition(qubits: Sequence[cirq.Qid], Implement a circuit that provides the unitary that is generated by single-particle fermion generators - .. math:: - + $$ U(v) = exp(log(v)_{p,q}(a_{p}^{\dagger}a_{q} - a_{q}^{\dagger}a_{p}) + $$ This can be used for implementing an exact single-body basis rotation diff --git a/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py b/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py index 2a8288743..fb3744acd 100644 --- a/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py +++ b/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py @@ -27,12 +27,12 @@ def test_givens_inverse(): r""" The Givens rotation in OpenFermion is defined as - .. math:: - + $$ \begin{pmatrix} \cos(\theta) & -e^{i \varphi} \sin(\theta) \\ \sin(\theta) & e^{i \varphi} \cos(\theta) \end{pmatrix}. + $$ confirm numerically its hermitian conjugate is it's inverse """ diff --git a/src/openfermion/circuits/primitives/state_preparation.py b/src/openfermion/circuits/primitives/state_preparation.py index 8268c0752..53452f412 100644 --- a/src/openfermion/circuits/primitives/state_preparation.py +++ b/src/openfermion/circuits/primitives/state_preparation.py @@ -146,27 +146,27 @@ def prepare_slater_determinant(qubits: Sequence[cirq.Qid], ) -> cirq.OP_TREE: r"""Prepare a Slater determinant from a computational basis state. - A Slater determinant is described by an :math:`\eta \times N` matrix - :math:`Q` with orthonormal rows, where :math:`\eta` is the particle number - and :math:`N` is the total number of modes. The state corresponding to this + A Slater determinant is described by an $\eta \times N$ matrix + $Q$ with orthonormal rows, where $\eta$ is the particle number + and $N$ is the total number of modes. The state corresponding to this matrix is - .. math:: - + $$ b^\dagger_1 \cdots b^\dagger_{\eta} \lvert \text{vac} \rangle, + $$ where - .. math:: - + $$ b^\dagger_j = \sum_{k = 1}^N Q_{jk} a^\dagger_k. + $$ The algorithm used is described in arXiv:1711.05395. It assumes the Jordan-Wigner transform. Args: qubits: The qubits to which to apply the circuit. - slater_determinant_matrix: The matrix :math:`Q` which describes the + slater_determinant_matrix: The matrix $Q$ which describes the Slater determinant to be prepared. initial_state: The computational basis state that the qubits start in. This can be either an integer or a container of integers. diff --git a/src/openfermion/circuits/slater_determinants.py b/src/openfermion/circuits/slater_determinants.py index 4098cca7f..2644606f7 100644 --- a/src/openfermion/circuits/slater_determinants.py +++ b/src/openfermion/circuits/slater_determinants.py @@ -39,27 +39,28 @@ def gaussian_state_preparation_circuit(quadratic_hamiltonian, together. Each elementary operation is either - the string 'pht', indicating the particle-hole transformation - on the last fermionic mode, which is the operator :math:`\mathcal{B}` + on the last fermionic mode, which is the operator $\mathcal{B}$ such that - .. math:: - + $$ \begin{align} \mathcal{B} a_N \mathcal{B}^\dagger &= a_N^\dagger,\\ \mathcal{B} a_j \mathcal{B}^\dagger &= a_j, \quad j = 1, \ldots, N-1, \end{align} + $$ or - - a tuple :math:`(i, j, \theta, \varphi)`, indicating the operation + - a tuple $(i, j, \theta, \varphi)$, indicating the operation - .. math:: + $$ \exp[i \varphi a_j^\dagger a_j] \exp[\theta (a_i^\dagger a_j - a_j^\dagger a_i)], + $$ - a Givens rotation of modes :math:`i` and :math:`j` by angles - :math:`\theta` and :math:`\varphi`. + a Givens rotation of modes $i$ and $j$ by angles + $\theta$ and $\varphi$. Args: quadratic_hamiltonian(QuadraticHamiltonian): @@ -85,10 +86,10 @@ def gaussian_state_preparation_circuit(quadratic_hamiltonian, can be performed in parallel. Each elementary operation is either the string 'pht', indicating a particle-hole transformation on the last fermionic mode, or a tuple of - the form :math:`(i, j, \theta, \varphi)`, + the form $(i, j, \theta, \varphi)$, indicating a Givens rotation - of modes :math:`i` and :math:`j` by angles :math:`\theta` - and :math:`\varphi`. + of modes $i$ and $j$ by angles $\theta$ + and $\varphi$. start_orbitals (list): The occupied orbitals to start with. This describes the initial state that the circuit should be applied to: it should @@ -152,36 +153,36 @@ def gaussian_state_preparation_circuit(quadratic_hamiltonian, def slater_determinant_preparation_circuit(slater_determinant_matrix): r"""Obtain the description of a circuit which prepares a Slater determinant. - The input is an :math:`N_f \times N` matrix :math:`Q` with orthonormal + The input is an $N_f \times N$ matrix $Q$ with orthonormal rows. Such a matrix describes the Slater determinant - .. math:: - + $$ b^\dagger_1 \cdots b^\dagger_{N_f} \lvert \text{vac} \rangle, + $$ where - .. math:: - + $$ b^\dagger_j = \sum_{k = 1}^N Q_{jk} a^\dagger_k. + $$ The output is the description of a circuit which prepares this Slater determinant, up to a global phase. The starting state which the circuit should be applied to is a Slater determinant (in the computational basis) with - the first :math:`N_f` orbitals filled. + the first $N_f$ orbitals filled. Args: - slater_determinant_matrix: The matrix :math:`Q` which describes the + slater_determinant_matrix: The matrix $Q$ which describes the Slater determinant to be prepared. Returns: circuit_description: A list of operations describing the circuit. Each operation is a tuple of elementary operations that can be performed in parallel. Each elementary operation is a tuple of the form - :math:`(i, j, \theta, \varphi)`, indicating a Givens rotation - of modes :math:`i` and :math:`j` by angles :math:`\theta` - and :math:`\varphi`. + $(i, j, \theta, \varphi)$, indicating a Givens rotation + of modes $i$ and $j$ by angles $\theta$ + and $\varphi$. """ decomposition, _, _ = givens_decomposition(slater_determinant_matrix) circuit_description = list(reversed(decomposition)) @@ -254,21 +255,21 @@ def jw_get_gaussian_state(quadratic_hamiltonian, occupied_orbitals=None): def jw_slater_determinant(slater_determinant_matrix): r"""Obtain a Slater determinant. - The input is an :math:`N_f \times N` matrix :math:`Q` with orthonormal + The input is an $N_f \times N$ matrix $Q$ with orthonormal rows. Such a matrix describes the Slater determinant - .. math:: - + $$ b^\dagger_1 \cdots b^\dagger_{N_f} \lvert \text{vac} \rangle, + $$ where - .. math:: - + $$ b^\dagger_j = \sum_{k = 1}^N Q_{jk} a^\dagger_k. + $$ Args: - slater_determinant_matrix: The matrix :math:`Q` which describes the + slater_determinant_matrix: The matrix $Q$ which describes the Slater determinant to be prepared. Returns: The Slater determinant as a sparse matrix. diff --git a/src/openfermion/circuits/trotter/algorithms/low_rank.py b/src/openfermion/circuits/trotter/algorithms/low_rank.py index 1db05df90..a23b3f9bc 100644 --- a/src/openfermion/circuits/trotter/algorithms/low_rank.py +++ b/src/openfermion/circuits/trotter/algorithms/low_rank.py @@ -43,20 +43,20 @@ class LowRankTrotterAlgorithm(TrotterAlgorithm): singular components and possibly truncating. Then, each singular component is simulated in the appropriate basis using a (non-fermionic) swap network. The general idea is based on expressing the two-body operator as - :math:`\sum_{pqrs} h_{pqrs} a^\dagger_p a^\dagger_q a_r a_s = - \sum_{j=0}^{J-1} \lambda_j (\sum_{pq} g_{jpq} a^\dagger_p a_q)^2` + $\sum_{pqrs} h_{pqrs} a^\dagger_p a^\dagger_q a_r a_s = + \sum_{j=0}^{J-1} \lambda_j (\sum_{pq} g_{jpq} a^\dagger_p a_q)^2$ One can then diagonalize the squared one-body component as math:`\sum_{pq} g_{pqj} a^\dagger_p a_q = R_j (\sum_{p} f_{pj} n_p) R_j^\dagger` Then, a 'low rank' Trotter step of the two-body tensor can be simulated as - :math:`\prod_{j=0}^{J-1} - R_j e^{-i \lambda_j \sum_{pq} f_{pj} f_{qj} n_p n_q} R_j^\dagger`. - The :math:`R_j` are Bogoliubov transformations, and one can - use a swap network to simulate the diagonal :math:`n_p n_q` terms. + $\prod_{j=0}^{J-1} + R_j e^{-i \lambda_j \sum_{pq} f_{pj} f_{qj} n_p n_q} R_j^\dagger$. + The $R_j$ are Bogoliubov transformations, and one can + use a swap network to simulate the diagonal $n_p n_q$ terms. The value of J is either fully the square of the number of qubits, which would imply no truncation, or it is specified by the user, or it is chosen so that - :math:`\sum_{l=0}^{L-1} (\sum_{pq} |g_{lpq}|)^2 |\lambda_l| < x` + $\sum_{l=0}^{L-1} (\sum_{pq} |g_{lpq}|)^2 |\lambda_l| < x$ where x is a truncation threshold specified by user. """ diff --git a/src/openfermion/hamiltonians/general_hubbard.py b/src/openfermion/hamiltonians/general_hubbard.py index 57cb63cf2..d4f9b1847 100644 --- a/src/openfermion/hamiltonians/general_hubbard.py +++ b/src/openfermion/hamiltonians/general_hubbard.py @@ -61,9 +61,7 @@ class FermiHubbardModel: itself and its neighbors, the Hamiltonian for the spinful model has the form - .. math:: - - \begin{align} + $$ H = &- \sum_{a < b} t_{a, b}^{(\mathrm{onsite})} \sum_{i} \sum_{\sigma} (a^\dagger_{i, a, \sigma} a_{i, b, \sigma} + @@ -105,33 +103,32 @@ class FermiHubbardModel: \\ &- h \sum_{i} \sum_{a} \left(n_{i, a, \uparrow} - n_{i, a, \downarrow}\right) - \end{align} + $$ where - - The indices :math:`(i, j)` and :math:`\{i, j\}` run over ordered and - unordered pairs, respectively of sites :math:`i` and :math:`j` of + - The indices $(i, j)$ and $\{i, j\}$ run over ordered and + unordered pairs, respectively of sites $i$ and $j$ of neighboring sites in the lattice, - - :math:`a` and :math:`b` index degrees of freedom on each site, - - :math:`\sigma \in \{\uparrow, \downarrow\}` is the spin, - - :math:`t_{a, b}^{(\mathrm{onsite})}` is the tunneling amplitude + - $a$ and $b$ index degrees of freedom on each site, + - $\sigma \in \{\uparrow, \downarrow\}$ is the spin, + - $t_{a, b}^{(\mathrm{onsite})}$ is the tunneling amplitude between spin orbitals on the same site, - - :math:`t_{a, b}^{(\mathrm{nghbr})}` is the tunneling amplitude + - $t_{a, b}^{(\mathrm{nghbr})}$ is the tunneling amplitude between spin orbitals on neighboring sites, - - :math:`U_{a, b}^{(\mathrm{onsite, \pm})}` is the Coulomb potential + - $U_{a, b}^{(\mathrm{onsite, \pm})}$ is the Coulomb potential between spin orbitals on the same site with the same (+) or different (-) spins, - - :math:`U_{a, b}^{(\mathrm{nghbr, \pm})}` is the Coulomb potential - betwen spin orbitals on neighborings sites with the same (+) or + - $U_{a, b}^{(\mathrm{nghbr, \pm})}$ is the Coulomb potential + between spin orbitals on neighboring sites with the same (+) or different (-) spins, - - :math:`\mu_{a}` is the chemical potential, and - - :math:`h` is the magnetic field. + - $\mu_{a}$ is the chemical potential, and + - $h$ is the magnetic field. One can also construct the Hamiltonian for the spinless model, which has the form - .. math:: - + $$ \begin{align} H = &- \sum_{a < b} t_{a, b}^{(\mathrm{onsite})} \sum_{i} @@ -181,8 +178,8 @@ def __init__(self, potential_parameters (Iterable[Tuple[int, float]], optional): The potential parameters. magnetic_field (float, optional): The magnetic field. Default is 0. - particle_hole_symmetry: If true, each number operator :math:`n` is - replaced with :math:`n - 1/2`. + particle_hole_symmetry: If true, each number operator $n$ is + replaced with $n - 1/2$. Each group of parameters is specified as an iterable of tuples. @@ -190,29 +187,29 @@ def __init__(self, In the spinful, model, the tunneling parameter corresponds to the terms - .. math:: - + $$ t \sum_{(i, j) \in E^{(\mathrm{edge type})}} \sum_{\sigma} \left(a_{i, a, \sigma}^{\dagger} a_{j, b, \sigma} + a_{j, b, \sigma}^{\dagger} a_{i, a, \sigma}\right) + $$ and in the spinless model to - .. math:: - + $$ -t \sum_{(i, j) \in E^{(\mathrm{edge type})}} \left(a_{i, a}^{\dagger} a_{j, b} + a_{j, b}^{\dagger} a_{i, a}\right), + $$ where - - :math:`(a, b)` is the pair of degrees + - $(a, b)$ is the pair of degrees of freedom given by ``dofs``; - - :math:`E^{(\mathrm{edge type})}` is the set of ordered pairs of + - $E^{(\mathrm{edge type})}$ is the set of ordered pairs of site indices returned by ``lattice.site_pairs_iter(edge_type, a != b)``; and - - :math:`t` is the ``coefficient``. + - $t$ is the ``coefficient``. Each interaction parameter is a tuple ``(edge_type, dofs, coefficient, spin_pairs)``. The final ``spin_pairs`` element is @@ -223,36 +220,38 @@ def __init__(self, indicates distinct degrees of freedom then the parameter corresponds to the terms - .. math:: + $$ U \sum_{(i, j) \in E^{(\mathrm{edge type})}} \sum_{(\sigma, \sigma')} n_{i, a, \sigma} n_{j, b, \sigma'} + $$ where - - :math:`(a, b)` is the pair of degrees of + - $(a, b)$ is the pair of degrees of freedom given by ``dofs``; - - :math:`E^{(\mathrm{edge type})}` is the set of ordered pairs of + - $E^{(\mathrm{edge type})}$ is the set of ordered pairs of site indices returned by ``lattice.site_pairs_iter(edge_type)``; - - :math:`U` is the ``coefficient``; and - - :math:`(\sigma, \sigma')` runs over + - $U$ is the ``coefficient``; and + - $(\sigma, \sigma')$ runs over - all four possible pairs of spins if `spin_pairs == SpinPairs.ALL`, - - :math:`\{(\uparrow, \downarrow), (\downarrow, \uparrow)\}` + - $\{(\uparrow, \downarrow), (\downarrow, \uparrow)\}$ if `spin_pairs == SpinPairs.DIFF`, and - - :math:`\{(\uparrow, \uparrow), (\downarrow, \downarrow)\}' + - $\{(\uparrow, \uparrow), (\downarrow, \downarrow)\}$ if 'spin_pairs == SpinPairs.SAME`. Each potential parameter is a tuple ``(dof, coefficient)``. For example, in the spinful model, it corresponds to the terms - .. math:: + $$ -\mu \sum_{i} \sum_{\sigma} n_{i, a, \sigma}, + $$ where - - :math:`i` runs over the sites of the lattice; - - :math:`a` is the degree of freedom ``dof``; and - - :math:`\mu` is the ``coefficient``. + - $i$ runs over the sites of the lattice; + - $a$ is the degree of freedom ``dof``; and + - $\mu$ is the ``coefficient``. In the spinless model, the magnetic field is ignored. """ diff --git a/src/openfermion/hamiltonians/hubbard.py b/src/openfermion/hamiltonians/hubbard.py index 45aa28dbb..d1ec839a7 100644 --- a/src/openfermion/hamiltonians/hubbard.py +++ b/src/openfermion/hamiltonians/hubbard.py @@ -42,8 +42,7 @@ def fermi_hubbard(x_dimension, The Hamiltonian for the spinful model has the form - .. math:: - + $$ \begin{align} H = &- t \sum_{\langle i,j \rangle} \sum_{\sigma} (a^\dagger_{i, \sigma} a_{j, \sigma} + @@ -55,35 +54,36 @@ def fermi_hubbard(x_dimension, - h \sum_i (a^\dagger_{i, \uparrow} a_{i, \uparrow} - a^\dagger_{i, \downarrow} a_{i, \downarrow}) \end{align} + $$ where - - The indices :math:`\langle i, j \rangle` run over pairs - :math:`i` and :math:`j` of sites that are connected to each other + - The indices $\langle i, j \rangle$ run over pairs + $i$ and $j$ of sites that are connected to each other in the grid - - :math:`\sigma \in \{\uparrow, \downarrow\}` is the spin - - :math:`t` is the tunneling amplitude - - :math:`U` is the Coulomb potential - - :math:`\mu` is the chemical potential - - :math:`h` is the magnetic field + - $\sigma \in \{\uparrow, \downarrow\}$ is the spin + - $t$ is the tunneling amplitude + - $U$ is the Coulomb potential + - $\mu$ is the chemical potential + - $h$ is the magnetic field One can also construct the Hamiltonian for the spinless model, which has the form - .. math:: - + $$ H = - t \sum_{\langle i, j \rangle} (a^\dagger_i a_j + a^\dagger_j a_i) + U \sum_{\langle i, j \rangle} a^\dagger_i a_i a^\dagger_j a_j - \mu \sum_i a_i^\dagger a_i. + $$ Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. - tunneling (float): The tunneling amplitude :math:`t`. - coulomb (float): The attractive local interaction strength :math:`U`. + tunneling (float): The tunneling amplitude $t$. + coulomb (float): The attractive local interaction strength $U$. chemical_potential (float, optional): The chemical potential - :math:`\mu` at each site. Default value is 0. - magnetic_field (float, optional): The magnetic field :math:`h` + $\mu$ at each site. Default value is 0. + magnetic_field (float, optional): The magnetic field $h$ at each site. Default value is 0. Ignored for the spinless case. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. @@ -92,16 +92,16 @@ def fermi_hubbard(x_dimension, particle_hole_symmetry (bool, optional): If False, the repulsion term corresponds to: - .. math:: - + $$ U \sum_{k=1}^{N-1} a_k^\dagger a_k a_{k+1}^\dagger a_{k+1} + $$ If True, the repulsion term is replaced by: - .. math:: - + $$ U \sum_{k=1}^{N-1} (a_k^\dagger a_k - \frac12) (a_{k+1}^\dagger a_{k+1} - \frac12) + $$ which is unchanged under a particle-hole transformation. Default is False @@ -240,34 +240,34 @@ def bose_hubbard(x_dimension, The Hamiltonian for the Bose-Hubbard model has the form - .. math:: - + $$ H = - t \sum_{\langle i, j \rangle} (b_i^\dagger b_j + b_j^\dagger b_i) + V \sum_{\langle i, j \rangle} b_i^\dagger b_i b_j^\dagger b_j + \frac{U}{2} \sum_i b_i^\dagger b_i (b_i^\dagger b_i - 1) - \mu \sum_i b_i^\dagger b_i. + $$ where - - The indices :math:`\langle i, j \rangle` run over pairs - :math:`i` and :math:`j` of nodes that are connected to each other + - The indices $\langle i, j \rangle$ run over pairs + $i$ and $j$ of nodes that are connected to each other in the grid - - :math:`t` is the tunneling amplitude - - :math:`U` is the on-site interaction potential - - :math:`\mu` is the chemical potential - - :math:`V` is the dipole or nearest-neighbour interaction potential + - $t$ is the tunneling amplitude + - $U$ is the on-site interaction potential + - $\mu$ is the chemical potential + - $V$ is the dipole or nearest-neighbour interaction potential Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. - tunneling (float): The tunneling amplitude :math:`t`. + tunneling (float): The tunneling amplitude $t$. interaction (float): The attractive local interaction - strength :math:`U`. + strength $U$. chemical_potential (float, optional): The chemical potential - :math:`\mu` at each site. Default value is 0. + $\mu$ at each site. Default value is 0. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. - dipole (float): The attractive dipole interaction strength :math:`V`. + dipole (float): The attractive dipole interaction strength $V$. Returns: bose_hubbard_model: An instance of the BosonOperator class. diff --git a/src/openfermion/hamiltonians/mean_field_dwave.py b/src/openfermion/hamiltonians/mean_field_dwave.py index 43d146bf6..49bfecd82 100644 --- a/src/openfermion/hamiltonians/mean_field_dwave.py +++ b/src/openfermion/hamiltonians/mean_field_dwave.py @@ -37,8 +37,7 @@ def mean_field_dwave(x_dimension: int, The Hamiltonian for this model has the form - .. math:: - + $$ \begin{align} H = &- t \sum_{\langle i,j \rangle} \sum_\sigma (a^\dagger_{i, \sigma} a_{j, \sigma} + @@ -51,26 +50,27 @@ def mean_field_dwave(x_dimension: int, a_{j, \downarrow} a_{i, \uparrow} - a_{j, \uparrow} a_{i, \downarrow}) \end{align} + $$ where - - The indices :math:`\langle i, j \rangle` run over pairs - :math:`i` and :math:`j` of sites that are connected to each other + - The indices $\langle i, j \rangle$ run over pairs + $i$ and $j$ of sites that are connected to each other in the grid - - :math:`\sigma \in \{\uparrow, \downarrow\}` is the spin - - :math:`t` is the tunneling amplitude - - :math:`\Delta_{ij}` is equal to :math:`+\Delta/2` for - horizontal edges and :math:`-\Delta/2` for vertical edges, - where :math:`\Delta` is the superconducting gap. - - :math:`\mu` is the chemical potential + - $\sigma \in \{\uparrow, \downarrow\}$ is the spin + - $t$ is the tunneling amplitude + - $\Delta_{ij}$ is equal to $+\Delta/2$ for + horizontal edges and $-\Delta/2$ for vertical edges, + where $\Delta$ is the superconducting gap. + - $\mu$ is the chemical potential Args: x_dimension (int): The width of the grid. y_dimension (int): The height of the grid. - tunneling (float): The tunneling amplitude :math:`t`. - sc_gap (float): The superconducting gap :math:`\Delta` + tunneling (float): The tunneling amplitude $t$. + sc_gap (float): The superconducting gap $\Delta$ chemical_potential (float, optional): The chemical potential - :math:`\mu` at each site. Default value is 0. + $\mu$ at each site. Default value is 0. periodic (bool, optional): If True, add periodic boundary conditions. Default is True. diff --git a/src/openfermion/hamiltonians/special_operators.py b/src/openfermion/hamiltonians/special_operators.py index 1dc0190b2..d1c8b2774 100644 --- a/src/openfermion/hamiltonians/special_operators.py +++ b/src/openfermion/hamiltonians/special_operators.py @@ -19,10 +19,11 @@ def s_plus_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the s+ operator. - .. math:: + $$ \begin{align} S^{+} = \sum_{i=1}^{n} a_{i, \alpha}^{\dagger}a_{i, \beta} \end{align} + $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2). @@ -49,10 +50,11 @@ def s_plus_operator(n_spatial_orbitals: int) -> FermionOperator: def s_minus_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the s+ operator. - .. math:: + $$ \begin{align} S^{-} = \sum_{i=1}^{n} a_{i, \beta}^{\dagger}a_{i, \alpha} \end{align} + $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2). @@ -79,10 +81,11 @@ def s_minus_operator(n_spatial_orbitals: int) -> FermionOperator: def sx_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the sx operator. - .. math:: + $$ \begin{align} S^{x} = \frac{1}{2}\sum_{i = 1}^{n}(S^{+} + S^{-}) \end{align} + $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits // 2). @@ -112,10 +115,11 @@ def sx_operator(n_spatial_orbitals: int) -> FermionOperator: def sy_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the sy operator. - .. math:: + $$ \begin{align} S^{y} = \frac{-i}{2}\sum_{i = 1}^{n}(S^{+} - S^{-}) \end{align} + $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits // 2). @@ -145,10 +149,11 @@ def sy_operator(n_spatial_orbitals: int) -> FermionOperator: def sz_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the sz operator. - .. math:: + $$ \begin{align} S^{z} = \frac{1}{2}\sum_{i = 1}^{n}(n_{i, \alpha} - n_{i, \beta}) \end{align} + $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits // 2). @@ -177,10 +182,11 @@ def sz_operator(n_spatial_orbitals: int) -> FermionOperator: def s_squared_operator(n_spatial_orbitals: int) -> FermionOperator: r"""Return the s^{2} operator. - .. math:: + $$ \begin{align} S^{2} = S^{-} S^{+} + S^{z}( S^{z} + 1) \end{align} + $$ Args: n_spatial_orbitals: number of spatial orbitals (n_qubits + 1 // 2). @@ -215,11 +221,11 @@ def majorana_operator(term: Optional[Union[Tuple[int, int], str]] = None, The second element of the tuple is an integer, either 0 or 1, indicating which type of Majorana operator it is: - Type 0: :math:`a^\dagger_p + a_p` + Type 0: $a^\dagger_p + a_p$ - Type 1: :math:`i (a^\dagger_p - a_p)` + Type 1: $i (a^\dagger_p - a_p)$ - where the :math:`a^\dagger_p` and :math:`a_p` are the usual + where the $a^\dagger_p$ and $a_p$ are the usual fermionic ladder operators. Alternatively, one can provide a string such as 'c2', which is a Type 0 operator on mode 2, or 'd3', which is a Type 1 diff --git a/src/openfermion/linalg/givens_rotations.py b/src/openfermion/linalg/givens_rotations.py index ae073b986..547244a3b 100644 --- a/src/openfermion/linalg/givens_rotations.py +++ b/src/openfermion/linalg/givens_rotations.py @@ -147,31 +147,31 @@ def double_givens_rotate(operator, givens_rotation, i, j, which='row'): def givens_decomposition_square(unitary_matrix, always_insert=False): r"""Decompose a square matrix into a sequence of Givens rotations. - The input is a square :math:`n \times n` matrix :math:`Q`. - :math:`Q` can be decomposed as follows: - - .. math:: + The input is a square $n \times n$ matrix $Q$. + $Q$ can be decomposed as follows: + $$ Q = DU + $$ - where :math:`U` is unitary and :math:`D` is diagonal. - Furthermore, we can decompose :math:`U` as - - .. math:: + where $U$ is unitary and $D$ is diagonal. + Furthermore, we can decompose $U$ as + $$ U = G_k ... G_1 + $$ - where :math:`G_1, \ldots, G_k` are complex Givens rotations. + where $G_1, \ldots, G_k$ are complex Givens rotations. A Givens rotation is a rotation within the two-dimensional subspace spanned by two coordinate axes. Within the two relevant coordinate axes, a Givens rotation has the form - .. math:: - + $$ \begin{pmatrix} \cos(\theta) & -e^{i \varphi} \sin(\theta) \\ \sin(\theta) & e^{i \varphi} \cos(\theta) \end{pmatrix}. + $$ Args: unitary_matrix: A numpy array with orthonormal rows, @@ -184,12 +184,12 @@ def givens_decomposition_square(unitary_matrix, always_insert=False): rotations. The list looks like [(G_1, ), (G_2, G_3), ... ]. The Givens rotations within a tuple can be implemented in parallel. The description of a Givens rotation is itself a tuple of the - form :math:`(i, j, \theta, \varphi)`, which represents a + form $(i, j, \theta, \varphi)$, which represents a Givens rotation of coordinates - :math:`i` and :math:`j` by angles :math:`\theta` and - :math:`\varphi`. + $i$ and $j$ by angles $\theta$ and + $\varphi$. diagonal (ndarray): - A list of the nonzero entries of :math:`D`. + A list of the nonzero entries of $D$. """ current_matrix = numpy.copy(unitary_matrix) n = current_matrix.shape[0] @@ -249,34 +249,34 @@ def givens_decomposition_square(unitary_matrix, always_insert=False): def givens_decomposition(unitary_rows, always_insert=False): r"""Decompose a matrix into a sequence of Givens rotations. - The input is an :math:`m \times n` matrix :math:`Q` with :math:`m \leq n`. - The rows of :math:`Q` are orthonormal. - :math:`Q` can be decomposed as follows: - - .. math:: + The input is an $m \times n$ matrix $Q$ with $m \leq n$. + The rows of $Q$ are orthonormal. + $Q$ can be decomposed as follows: + $$ V Q U^\dagger = D + $$ - where :math:`V` and :math:`U` are unitary matrices, and :math:`D` - is an :math:`m \times n` matrix with the - first :math:`m` columns forming a diagonal matrix and the rest of the - columns being zero. Furthermore, we can decompose :math:`U` as - - .. math:: + where $V$ and $U$ are unitary matrices, and $D$ + is an $m \times n$ matrix with the + first $m$ columns forming a diagonal matrix and the rest of the + columns being zero. Furthermore, we can decompose $U$ as + $$ U = G_k ... G_1 + $$ - where :math:`G_1, \ldots, G_k` are complex Givens rotations. + where $G_1, \ldots, G_k$ are complex Givens rotations. A Givens rotation is a rotation within the two-dimensional subspace spanned by two coordinate axes. Within the two relevant coordinate axes, a Givens rotation has the form - .. math:: - + $$ \begin{pmatrix} \cos(\theta) & -e^{i \varphi} \sin(\theta) \\ \sin(\theta) & e^{i \varphi} \cos(\theta) \end{pmatrix}. + $$ Args: unitary_rows: A numpy array or matrix with orthonormal rows, @@ -289,15 +289,15 @@ def givens_decomposition(unitary_rows, always_insert=False): rotations. The list looks like [(G_1, ), (G_2, G_3), ... ]. The Givens rotations within a tuple can be implemented in parallel. The description of a Givens rotation is itself a tuple of the - form :math:`(i, j, \theta, \varphi)`, which represents a + form $(i, j, \theta, \varphi)$, which represents a Givens rotation of coordinates - :math:`i` and :math:`j` by angles :math:`\theta` and - :math:`\varphi`. + $i$ and $j$ by angles $\theta$ and + $\varphi$. left_unitary (ndarray): - An :math:`m \times m` numpy array representing the matrix - :math:`V`. + An $m \times m$ numpy array representing the matrix + $V$. diagonal (ndarray): - A list of the nonzero entries of :math:`D`. + A list of the nonzero entries of $D$. """ current_matrix = numpy.copy(unitary_rows) m, n = current_matrix.shape @@ -399,52 +399,52 @@ def fermionic_gaussian_decomposition(unitary_rows): r"""Decompose a matrix into a sequence of Givens rotations and particle-hole transformations on the last fermionic mode. - The input is an :math:`N \times 2N` matrix :math:`W` with orthonormal - rows. Furthermore, :math:`W` must have the block form - - .. math:: + The input is an $N \times 2N$ matrix $W$ with orthonormal + rows. Furthermore, $W$ must have the block form + $$ W = ( W_1 \hspace{4pt} W_2 ) + $$ - where :math:`W_1` and :math:`W_2` satisfy - - .. math:: + where $W_1$ and $W_2$ satisfy + $$ W_1 W_1^\dagger + W_2 W_2^\dagger &= I + $$ W_1 W_2^T + W_2 W_1^T &= 0. - Then :math:`W` can be decomposed as - - .. math:: + Then $W$ can be decomposed as + $$ V W U^\dagger = ( 0 \hspace{6pt} D ) + $$ - where :math:`V` and :math:`U` are unitary matrices and :math:`D` - is a diagonal unitary matrix. Furthermore, :math:`U` can be decomposed + where $V$ and $U$ are unitary matrices and $D$ + is a diagonal unitary matrix. Furthermore, $U$ can be decomposed as follows: - .. math:: - + $$ U = B G_{k} \cdots B G_3 G_2 B G_1 B, + $$ - where each :math:`G_i` is a Givens rotation, and :math:`B` represents - swapping the :math:`N`-th column with the :math:`2N`-th column, + where each $G_i$ is a Givens rotation, and $B$ represents + swapping the $N$-th column with the $2N$-th column, which corresponds to a particle-hole transformation on the last fermionic mode. This particle-hole transformation maps - :math:`a^\dagger_N` to :math:`a_N` and vice versa, while leaving the + $a^\dagger_N$ to $a_N$ and vice versa, while leaving the other fermionic ladder operators invariant. - The decomposition of :math:`U` is returned as a list of tuples of objects + The decomposition of $U$ is returned as a list of tuples of objects describing rotations and particle-hole transformations. The list looks something like [('pht', ), (G_1, ), ('pht', G_2), ... ]. The objects within a tuple are either the string 'pht', which indicates a particle-hole transformation on the last fermionic mode, or a tuple - of the form :math:`(i, j, \theta, \varphi)`, which indicates a - Givens rotation of rows :math:`i` and :math:`j` by angles - :math:`\theta` and :math:`\varphi`. + of the form $(i, j, \theta, \varphi)$, which indicates a + Givens rotation of rows $i$ and $j$ by angles + $\theta$ and $\varphi$. - The matrix :math:`V^T D^*` can also be decomposed as a sequence of + The matrix $V^T D^*$ can also be decomposed as a sequence of Givens rotations. This decomposition is needed for a circuit that prepares an excited state. @@ -455,14 +455,14 @@ def fermionic_gaussian_decomposition(unitary_rows): Returns ------- decomposition (list[tuple]): - The decomposition of :math:`U`. + The decomposition of $U$. left_decomposition (list[tuple]): - The decomposition of :math:`V^T D^*`. + The decomposition of $V^T D^*$. diagonal (ndarray): - A list of the nonzero entries of :math:`D`. + A list of the nonzero entries of $D$. left_diagonal (ndarray): A list of the nonzero entries left from the decomposition - of :math:`V^T D^*`. + of $V^T D^*$. """ current_matrix = numpy.copy(unitary_rows) n, p = current_matrix.shape diff --git a/src/openfermion/linalg/sparse_tools.py b/src/openfermion/linalg/sparse_tools.py index b9915f643..59a47ebdc 100644 --- a/src/openfermion/linalg/sparse_tools.py +++ b/src/openfermion/linalg/sparse_tools.py @@ -312,10 +312,11 @@ def jw_sz_indices(sz_value, The returned indices label computational basis vectors which lie within the corresponding eigenspace of the Sz operator, - .. math:: + $$ \begin{align} S^{z} = \frac{1}{2}\sum_{i = 1}^{n}(n_{i, \alpha} - n_{i, \beta}) \end{align} + $$ Args: sz_value(float): Desired Sz value. Should be an integer or diff --git a/src/openfermion/linalg/wedge_product.py b/src/openfermion/linalg/wedge_product.py index 5adf79c11..29c618343 100644 --- a/src/openfermion/linalg/wedge_product.py +++ b/src/openfermion/linalg/wedge_product.py @@ -73,7 +73,7 @@ def wedge(left_tensor, right_tensor, left_index_ranks, right_index_ranks): The wedge product is defined as - .. math:: + $$ \\begin{align} a_{j_{1}, j_{2}, ...,j_{p}}^{i_{1}, i_{2}, ..., i_{p}} \\wedge b_{j_{p+1}, j_{p+2}, ..., j_{N}}^{i_{p+1}, i_{p + 2}, ..., i_{N}} = @@ -82,6 +82,7 @@ def wedge(left_tensor, right_tensor, left_index_ranks, right_index_ranks): a_{j_{1}, j_{2}, ...,j_{p}}^{i_{1}, i_{2}, ..., i_{p}} b_{j_{p+1}, j_{p+2}, ..., j_{N}}^{i_{p+1}, i_{p + 2}, ..., i_{N}} \\end{align} + $$ The top indices are those that transform contravariently. The bottom indices transform covariently. diff --git a/src/openfermion/ops/operators/majorana_operator.py b/src/openfermion/ops/operators/majorana_operator.py index fe5fe0db9..3977027f1 100644 --- a/src/openfermion/ops/operators/majorana_operator.py +++ b/src/openfermion/ops/operators/majorana_operator.py @@ -19,23 +19,24 @@ class MajoranaOperator: r"""A linear combination of products of Majorana operators. A system of N fermionic modes can be described using 2N Majorana operators - :math:`\gamma_1, \ldots, \gamma_{2N}` + $\gamma_1, \ldots, \gamma_{2N}$ as an alternative to using N fermionic annihilation operators. The algebra of Majorana operators amounts to the relation - .. math:: + $$ \{\gamma_i, \gamma_j\} = \gamma_i \gamma_j + \gamma_j \gamma_i = 2 \delta_{ij} + $$ - Note that this implies :math:`\gamma_i^2 = 1`. + Note that this implies $\gamma_i^2 = 1$. The MajoranaOperator class stores a linear combination of products of Majorana operators. Each product is represented as a tuple of integers representing the indices of the operators. As an example, `MajoranaOperator((2, 3, 5), -1.5)` initializes an operator with a single term which represents the operator - :math:`-1.5 \gamma_2 \gamma_3 \gamma_5`. MajoranaOperators can be + $-1.5 \gamma_2 \gamma_3 \gamma_5$. MajoranaOperators can be added, subtracted, multiplied, and divided by scalars. They can be compared for approximate numerical equality using `==`. @@ -89,16 +90,16 @@ def commutes_with(self, other): def with_basis_rotated_by(self, transformation_matrix): r"""Change to a basis of new Majorana operators. - The input to this method is a real orthogonal matrix :math:`O`. + The input to this method is a real orthogonal matrix $O$. It returns a new MajoranaOperator which is equivalent to the old one but rewritten in terms of a new basis of Majorana operators. Let the original Majorana operators be denoted by - :math:`\gamma_i` and the new operators be denoted by - :math:`\tilde{\gamma_i}`. Then they are related by the equation - - .. math:: + $\gamma_i$ and the new operators be denoted by + $\tilde{\gamma_i}$. Then they are related by the equation + $$ \tilde{\gamma_i} = \sum_j O_{ij} \gamma_j. + $$ Args: transformation_matrix: A real orthogonal matrix representing diff --git a/src/openfermion/ops/operators/symbolic_operator.py b/src/openfermion/ops/operators/symbolic_operator.py index 11af67bb4..fcab63860 100644 --- a/src/openfermion/ops/operators/symbolic_operator.py +++ b/src/openfermion/ops/operators/symbolic_operator.py @@ -684,10 +684,10 @@ def induced_norm(self, order=1): Compute the induced p-norm of the operator. If we represent an operator as - :math: `\sum_{j} w_j H_j` - where :math: `w_j` are scalar coefficients then this norm is - :math: `\left(\sum_{j} \| w_j \|^p \right)^{\frac{1}{p}} - where :math: `p` is the order of the induced norm + $\sum_{j} w_j H_j$ + where $w_j$ are scalar coefficients then this norm is + $\left(\sum_{j} \| w_j \|^p \right)^{\frac{1}{p}}$ + where $p$ is the order of the induced norm Args: order(int): the order of the induced norm. diff --git a/src/openfermion/ops/representations/diagonal_coulomb_hamiltonian.py b/src/openfermion/ops/representations/diagonal_coulomb_hamiltonian.py index dda385e90..3ccad8e81 100644 --- a/src/openfermion/ops/representations/diagonal_coulomb_hamiltonian.py +++ b/src/openfermion/ops/representations/diagonal_coulomb_hamiltonian.py @@ -19,20 +19,20 @@ class DiagonalCoulombHamiltonian: r"""Class for storing Hamiltonians of the form - .. math:: - + $$ \sum_{p, q} T_{pq} a^\dagger_p a_q + \sum_{p, q} V_{pq} a^\dagger_p a_p a^\dagger_q a_q + \text{constant} + $$ where - - :math:`T` is a Hermitian matrix. - - :math:`V` is a real symmetric matrix. + - $T$ is a Hermitian matrix. + - $V$ is a real symmetric matrix. Attributes: - one_body(ndarray): The Hermitian matrix :math:`T`. - two_body(ndarray): The real symmetric matrix :math:`V`. + one_body(ndarray): The Hermitian matrix $T$. + two_body(ndarray): The real symmetric matrix $V$. constant(float): The constant. """ diff --git a/src/openfermion/ops/representations/doci_hamiltonian.py b/src/openfermion/ops/representations/doci_hamiltonian.py index 12fcd03a3..1f48c52ff 100644 --- a/src/openfermion/ops/representations/doci_hamiltonian.py +++ b/src/openfermion/ops/representations/doci_hamiltonian.py @@ -28,8 +28,7 @@ class DOCIHamiltonian(PolynomialTensor): Note that the operators stored in this class take the form: - .. math:: - + $$ constant + \sum_{p} h^{(r1)}_{p, p}/2 (1 - \sigma^Z_p) + \sum_{p \neq q} h^{(r1)}_{p, q}/4 * (\sigma^X_p \sigma^X_q + \sigma^Y_p \sigma^Y_q) + @@ -39,11 +38,11 @@ class DOCIHamiltonian(PolynomialTensor): constant + \sum_{p} h_{p, p} N_p + \sum_{p \neq q} w_{p, p} N_p N_q + \sum_{p \neq q} v_{p, p} P_p^\dagger P_q, + $$ where - .. math:: - + $$ N_p = (1 - \sigma^Z_p)/2, P_p = a_{i,\beta} a_{i,\alpha}, h_p = h^{(r1)}_{p, p} = \langle p|h|p \rangle = @@ -54,22 +53,23 @@ class DOCIHamiltonian(PolynomialTensor): I^{(2)}_{p, q, p, q}, v_{p, q} = h^{(r1)}_{p, q} = \langle pp|v|qq \rangle = I^{(2)}_{p, p, q, q}, + $$ - with (:math:`I^{(1)}_{p, q}`) and (:math:`I^{(2)}_{p, q, r, s}`) are the one - and two body electron integrals and (:math:`h`) and (:math:`v`) are the + with ($I^{(1)}_{p, q}$) and ($I^{(2)}_{p, q, r, s}$) are the one + and two body electron integrals and ($h$) and ($v$) are the coefficients of the corresponding InteractionOperator - .. math:: - + $$ constant + \sum_{p, q} h_{p, q} a^\dagger_p a_q + \sum_{p, q, r, s} h_{p, q, r, s} a^\dagger_p a^\dagger_q a_r a_s. + $$ Attributes: constant: The constant offset. - hr1: The coefficients of (:math:`h^{r1}_{p, q}`). + hr1: The coefficients of ($h^{r1}_{p, q}$). This is an n_qubits x n_qubits numpy array of floats. - hr2: The coefficients of (:math:`h^{r2}_{p, q}`). + hr2: The coefficients of ($h^{r2}_{p, q}$). This is an n_qubits x n_qubits numpy array of floats. """ @@ -80,10 +80,10 @@ def __init__(self, constant, hc, hr1, hr2): Args: constant: A constant term in the operator given as a float. For instance, the nuclear repulsion energy. - hc: the coefficients of (:math:`h^{(c)}_{p}`) - hr1: The coefficients of (:math:`h^{(r1)}_{p, q}`). + hc: the coefficients of ($h^{(c)}_{p}$) + hr1: The coefficients of ($`h^{(r1)}_{p, q}$). This is an n_qubits x n_qubits numpy array of floats. - hr2: The coefficients of (:math:`h^{(r2)}_{p, q}`). + hr2: The coefficients of ($h^{(r2)}_{p, q}$). This is an n_qubits x n_qubits array of floats. """ super(DOCIHamiltonian, self).__init__(None) diff --git a/src/openfermion/ops/representations/interaction_operator.py b/src/openfermion/ops/representations/interaction_operator.py index d10f2d880..45d337c0f 100644 --- a/src/openfermion/ops/representations/interaction_operator.py +++ b/src/openfermion/ops/representations/interaction_operator.py @@ -33,17 +33,17 @@ class InteractionOperator(PolynomialTensor): efficient manipulation of the data. Note that the operators stored in this class take the form: - .. math:: - + $$ constant + \sum_{p, q} h_{p, q} a^\dagger_p a_q + \sum_{p, q, r, s} h_{p, q, r, s} a^\dagger_p a^\dagger_q a_r a_s. + $$ Attributes: one_body_tensor: The coefficients of the one-body terms - (:math:`h_{p, q}`). This is an n_qubits x n_qubits + ($h_{p, q}$). This is an n_qubits x n_qubits numpy array of floats. two_body_tensor: The coefficients of the two-body terms - (:math:`h_{p, q, r, s}`). + ($h_{p, q, r, s}$). This is an n_qubits x n_qubits x n_qubits x n_qubits numpy array of floats. """ @@ -56,10 +56,10 @@ def __init__(self, constant, one_body_tensor, two_body_tensor): constant: A constant term in the operator given as a float. For instance, the nuclear repulsion energy. one_body_tensor: The coefficients of the one-body terms - (:math:`h_{p,q}`). + ($h_{p,q}$). This is an n_qubits x n_qubits numpy array of floats. two_body_tensor: The coefficients of the two-body terms - (:math:`h_{p, q, r, s}`). + ($h_{p, q, r, s}$). This is an n_qubits x n_qubits x n_qubits x n_qubits numpy array of floats. """ diff --git a/src/openfermion/ops/representations/polynomial_tensor.py b/src/openfermion/ops/representations/polynomial_tensor.py index 7a340b0ca..09110341c 100644 --- a/src/openfermion/ops/representations/polynomial_tensor.py +++ b/src/openfermion/ops/representations/polynomial_tensor.py @@ -49,8 +49,8 @@ def general_basis_change(general_tensor, rotation_matrix, key): n_qubits by n_qubits. Assumed to be unitary. key: A tuple indicating the type of general_tensor. Assumed to be non-empty. For example, a tensor storing coefficients of - :math:`a^\dagger_p a_q` would have a key of (1, 0) whereas a tensor - storing coefficients of :math:`a^\dagger_p a_q a_r a^\dagger_s` + $a^\dagger_p a_q$ would have a key of (1, 0) whereas a tensor + storing coefficients of $a^\dagger_p a_q a_r a^\dagger_s$ would have a key of (1, 0, 0, 1). Returns: diff --git a/src/openfermion/ops/representations/quadratic_hamiltonian.py b/src/openfermion/ops/representations/quadratic_hamiltonian.py index 5534271fe..c8171a9ef 100644 --- a/src/openfermion/ops/representations/quadratic_hamiltonian.py +++ b/src/openfermion/ops/representations/quadratic_hamiltonian.py @@ -27,26 +27,26 @@ class QuadraticHamiltonian(PolynomialTensor): r"""Class for storing Hamiltonians that are quadratic in the fermionic ladder operators. The operators stored in this class take the form - .. math:: - + $$ \sum_{p, q} (M_{pq} - \mu \delta_{pq}) a^\dagger_p a_q + \frac12 \sum_{p, q} (\Delta_{pq} a^\dagger_p a^\dagger_q + \text{h.c.}) + \text{constant} + $$ where - - :math:`M` is a Hermitian `n_qubits` x `n_qubits` matrix. - - :math:`\Delta` is an antisymmetric `n_qubits` x `n_qubits` matrix. - - :math:`\mu` is a real number representing the chemical potential. - - :math:`\delta_{pq}` is the Kronecker delta symbol. + - $M$ is a Hermitian `n_qubits` x `n_qubits` matrix. + - $\Delta$ is an antisymmetric `n_qubits` x `n_qubits` matrix. + - $\mu$ is a real number representing the chemical potential. + - $\delta_{pq}$ is the Kronecker delta symbol. - We separate the chemical potential :math:`\mu` from :math:`M` so that + We separate the chemical potential $\mu$ from $M$ so that we can use it to adjust the expectation value of the total number of particles. Attributes: - chemical_potential(float): The chemical potential :math:`\mu`. + chemical_potential(float): The chemical potential $\mu$. """ def __init__(self, hermitian_part, @@ -57,18 +57,18 @@ def __init__(self, Initialize the QuadraticHamiltonian class. Args: - hermitian_part(ndarray): The matrix :math:`M`, which represents the + hermitian_part(ndarray): The matrix $M$, which represents the coefficients of the particle-number-conserving terms. This is an `n_qubits` x `n_qubits` numpy array of complex numbers. - antisymmetric_part(ndarray): The matrix :math:`\Delta`, + antisymmetric_part(ndarray): The matrix $\Delta$, which represents the coefficients of the non-particle-number-conserving terms. This is an `n_qubits` x `n_qubits` numpy array of complex numbers. constant(float, optional): A constant term in the operator. chemical_potential(float, optional): The chemical potential - :math:`\mu`. + $\mu$. """ n_qubits = hermitian_part.shape[0] @@ -145,21 +145,21 @@ def majorana_form(self): Any quadratic Hamiltonian can be written in the form - .. math:: - + $$ \frac{i}{2} \sum_{j, k} A_{jk} f_j f_k + \text{constant} + $$ - where the :math:`f_i` are normalized Majorana fermion operators: - - .. math:: + where the $f_i$ are normalized Majorana fermion operators: + $$ f_j = \frac{1}{\sqrt{2}} (a^\dagger_j + a_j) f_{j + N} = \frac{i}{\sqrt{2}} (a^\dagger_j - a_j) + $$ - and :math:`A` is a (2 * `n_qubits`) x (2 * `n_qubits`) real + and $A$ is a (2 * `n_qubits`) x (2 * `n_qubits`) real antisymmetric matrix. This function returns the matrix - :math:`A` and the constant. + $A$ and the constant. """ hermitian_part = self.combined_hermitian_part antisymmetric_part = self.antisymmetric_part @@ -194,18 +194,17 @@ def diagonalizing_bogoliubov_transform(self, spin_sector=None): Any quadratic Hamiltonian can be rewritten in the form - .. math:: - + $$ \sum_{j} \varepsilon_j b^\dagger_j b_j + \text{constant}, + $$ - where the :math:`b^\dagger_j` are a new set fermionic creation + where the $b^\dagger_j$ are a new set fermionic creation operators that satisfy the canonical anticommutation relations. The new creation operators are linear combinations of the original ladder operators. In the most general case, creation and annihilation operators are mixed together: - .. math:: - + $$ \begin{pmatrix} b^\dagger_1 \\ \vdots \\ @@ -220,14 +219,14 @@ def diagonalizing_bogoliubov_transform(self, spin_sector=None): \vdots \\ a_N \end{pmatrix}, + $$ - where :math:`W` is an :math:`N \times (2N)` matrix. + where $W$ is an $N \times (2N)$ matrix. However, if the Hamiltonian conserves particle number then creation operators don't need to be mixed with annihilation operators - and :math:`W` only needs to be an :math:`N \times N` matrix: - - .. math:: + and $W$ only needs to be an $N \times N$ matrix: + $$ \begin{pmatrix} b^\dagger_1 \\ \vdots \\ @@ -239,8 +238,9 @@ def diagonalizing_bogoliubov_transform(self, spin_sector=None): \vdots \\ a^\dagger_N \\ \end{pmatrix}, + $$ - This method returns the matrix :math:`W`. + This method returns the matrix $W$. Args: spin_sector (optional str): An optional integer specifying @@ -253,13 +253,13 @@ def diagonalizing_bogoliubov_transform(self, spin_sector=None): Returns: orbital_energies(ndarray) - A one-dimensional array containing the :math:`\varepsilon_j` + A one-dimensional array containing the $\varepsilon_j$ diagonalizing_unitary (ndarray): - A matrix representing the transformation :math:`W` of the + A matrix representing the transformation $W$ of the fermionic ladder operators. If the Hamiltonian conserves - particle number then this is :math:`N \times N`; otherwise - it is :math:`N \times 2N`. If spin sector is specified, - then `N` here represents the number of spatial orbitals + particle number then this is $N \times N$; otherwise + it is $N \times 2N$. If spin sector is specified, + then $N$ here represents the number of spatial orbitals rather than spin orbitals. constant(float) The constant @@ -354,9 +354,9 @@ def diagonalizing_circuit(self): This circuit performs the transformation to a basis in which the Hamiltonian takes the diagonal form - .. math:: - + $$ \sum_{j} \varepsilon_j b^\dagger_j b_j + \text{constant}. + $$ Returns ------- @@ -366,10 +366,10 @@ def diagonalizing_circuit(self): can be performed in parallel. Each elementary operation is either the string 'pht' indicating a particle-hole transformation on the last fermionic mode, or a tuple of - the form :math:`(i, j, \theta, \varphi)`, + the form $(i, j, \theta, \varphi)$, indicating a Givens rotation - of modes :math:`i` and :math:`j` by angles :math:`\theta` - and :math:`\varphi`. + of modes $i$ and $j$ by angles $\theta$ + and $\varphi$. """ # Adding inline import here to prevent circular issues # TODO: move this out once we have a better solution @@ -418,11 +418,11 @@ def orbital_energies(self, non_negative=False): Any quadratic Hamiltonian is unitarily equivalent to a Hamiltonian of the form - .. math:: - + $$ \sum_{j} \varepsilon_j b^\dagger_j b_j + \text{constant}. + $$ - We call the :math:`\varepsilon_j` the orbital energies. + We call the $\varepsilon_j$ the orbital energies. The eigenvalues of the Hamiltonian are sums of subsets of the orbital energies (up to the additive constant). @@ -435,7 +435,7 @@ def orbital_energies(self, non_negative=False): Returns ------- orbital_energies(ndarray) - A one-dimensional array containing the :math:`\varepsilon_j` + A one-dimensional array containing the $\varepsilon_j$ constant(float) The constant """ diff --git a/src/openfermion/third_party/representability/_dualbasis.py b/src/openfermion/third_party/representability/_dualbasis.py index abbc5eb75..f957ec321 100644 --- a/src/openfermion/third_party/representability/_dualbasis.py +++ b/src/openfermion/third_party/representability/_dualbasis.py @@ -30,8 +30,9 @@ def __init__(self, Define a linear operator on a tensor `A', a bias `b', and a result `c' satisfying - .. math:: + $$ Ax + b = c + $$ Args: tensor_names: Names of tensor subspace to act on. diff --git a/src/openfermion/transforms/opconversions/term_reordering.py b/src/openfermion/transforms/opconversions/term_reordering.py index 6b1e3bdad..105837f80 100644 --- a/src/openfermion/transforms/opconversions/term_reordering.py +++ b/src/openfermion/transforms/opconversions/term_reordering.py @@ -24,9 +24,9 @@ def chemist_ordered(fermion_operator): The normal ordering convention for chemists is different. Rather than ordering the two-body term as physicists do, as - :math:`a^\dagger a^\dagger a a` + $a^\dagger a^\dagger a a$ the chemist ordering of the two-body term is - :math:`a^\dagger a a^\dagger a` + $a^\dagger a a^\dagger a$ TODO: This routine can be made more efficient. diff --git a/src/openfermion/transforms/repconversions/fourier_transforms.py b/src/openfermion/transforms/repconversions/fourier_transforms.py index 836877adb..d0d7dd9de 100644 --- a/src/openfermion/transforms/repconversions/fourier_transforms.py +++ b/src/openfermion/transforms/repconversions/fourier_transforms.py @@ -53,10 +53,10 @@ def _fourier_transform_helper(hamiltonian, grid, spinless, phase_factor, def fourier_transform(hamiltonian, grid, spinless): r"""Apply Fourier transform to change hamiltonian in plane wave basis. - .. math:: - + $$ c^\dagger_v = \sqrt{1/N} \sum_m {a^\dagger_m \exp(-i k_v r_m)} c_v = \sqrt{1/N} \sum_m {a_m \exp(i k_v r_m)} + $$ Args: hamiltonian (FermionOperator): The hamiltonian in plane wave basis. @@ -78,10 +78,10 @@ def inverse_fourier_transform(hamiltonian, grid, spinless): r"""Apply inverse Fourier transform to change hamiltonian in plane wave dual basis. - .. math:: - + $$ a^\dagger_v = \sqrt{1/N} \sum_m {c^\dagger_m \exp(i k_v r_m)} a_v = \sqrt{1/N} \sum_m {c_m \exp(-i k_v r_m)} + $$ Args: hamiltonian (FermionOperator): diff --git a/src/openfermion/transforms/repconversions/qubit_tapering_from_stabilizer.py b/src/openfermion/transforms/repconversions/qubit_tapering_from_stabilizer.py index 3595b261a..fb838734b 100644 --- a/src/openfermion/transforms/repconversions/qubit_tapering_from_stabilizer.py +++ b/src/openfermion/transforms/repconversions/qubit_tapering_from_stabilizer.py @@ -313,19 +313,19 @@ def reduce_number_of_terms(operator, manually indicate the qubits to be fixed. It is possible to reduce the number of terms in a Hamiltonian by - merging Pauli strings :math:`H_1, \, H_2` that are related by a - stabilizer :math:`S` such that :math:`H_1 = H_2 \cdot S`. Given - a stabilizer generator :math:`\pm X \otimes p` this algorithm fixes the + merging Pauli strings $H_1, \, H_2$ that are related by a + stabilizer $S$ such that $H_1 = H_2 \cdot S$. Given + a stabilizer generator $\pm X \otimes p$ this algorithm fixes the first qubit, such that every Pauli string in the Hamiltonian acts with - either :math:`Z` or the identity on it. Where necessary, this is achieved - by multiplications with :math:`\pm X \otimes p`: a string - :math:`Y \otimes h`, for instance, is turned into - :math:`Z \otimes (\mp ih\cdot p)`. Qubits on which a generator acts as - :math:`Y` (:math:`Z`) are constrained to be acted on by the Hamiltonian as - :math:`Z` (:math:`X`) or the identity. Fixing a different qubit for every + either $Z$ or the identity on it. Where necessary, this is achieved + by multiplications with $\pm X \otimes p$: a string + $Y \otimes h$, for instance, is turned into + $Z \otimes (\mp ih\cdot p)$. Qubits on which a generator acts as + $Y$ ($Z$) are constrained to be acted on by the Hamiltonian as + $Z$ ($X$) or the identity. Fixing a different qubit for every stabilizer generator eliminates all redundant strings. The fixed representations are in the end re-expressed as the shortest of the - original strings, :math:`H_1` or :math:`H_2`. + original strings, $H_1$ or $H_2$. Args: @@ -415,16 +415,16 @@ def taper_off_qubits(operator, Qubits can be disregarded from the Hamiltonian when the effect of all its terms on them is rendered trivial. This algorithm employs a stabilizers - like :math:`\pm X \otimes p` to fix the action of every Pauli - string on the first qubit to :math:`Z` or the identity. A string - :math:`X \otimes h` would for instance be multiplied with the stabilizer - to obtain :math:`1 \otimes (\pm h\cdot p)` while a string - :math:`Z \otimes h^\prime` would pass without correction. The first + like $\pm X \otimes p$ to fix the action of every Pauli + string on the first qubit to $Z$ or the identity. A string + $X \otimes h$ would for instance be multiplied with the stabilizer + to obtain $1 \otimes (\pm h\cdot p)$ while a string + $Z \otimes h^\prime$ would pass without correction. The first qubit can subsequently be removed as it must be in the computational basis in Hamiltonian eigenstates. - For stabilizers acting as :math:`Y` (:math:`Z`) on selected qubits, + For stabilizers acting as $Y$ ($Z$) on selected qubits, the algorithm would fix the action of every Hamiltonian string to - :math:`Z` (:math:`X`). Updating also the list of remaining stabilizer + $Z$ ($X$). Updating also the list of remaining stabilizer generators, the algorithm is run iteratively. Args: diff --git a/src/openfermion/utils/rdm_mapping_functions.py b/src/openfermion/utils/rdm_mapping_functions.py index d8af018b3..435e7c136 100644 --- a/src/openfermion/utils/rdm_mapping_functions.py +++ b/src/openfermion/utils/rdm_mapping_functions.py @@ -25,7 +25,7 @@ def map_two_pdm_to_one_pdm(tpdm, particle_number): Args: tpdm (numpy.ndarray): The 2-RDM as a 4-index tensor. Indices follow the internal convention of tpdm[p, q, r, s] == - :math:`a_{p}^{\dagger}a_{q}^{\dagger}a_{r}a_{s}`. + $a_{p}^{\dagger}a_{q}^{\dagger}a_{r}a_{s}$. particle_number (float): number of particles in the system Returns: @@ -41,10 +41,10 @@ def map_two_pdm_to_two_hole_dm(tpdm, opdm): Args: tpdm (numpy.ndarray): The 2-RDM as a 4-index tensor. Indices follow the internal convention of tpdm[p, q, r, s] == - :math:`a_{p}^{\dagger}a_{q}^{\dagger}a_{r}a_{s}`. + $a_{p}^{\dagger}a_{q}^{\dagger}a_{r}a_{s}$. opdm (numpy.ndarray): The 1-RDM as a 2-index tensor. Indices follow the internal convention of opdm[p, q] == - :math:`a_{p}^{\dagger}a_{q}`. + $a_{p}^{\dagger}a_{q}$. Returns: tqdm (numpy.ndarray): The 2-hole matrix. @@ -70,10 +70,10 @@ def map_two_hole_dm_to_two_pdm(tqdm, opdm): Args: tqdm (numpy.ndarray): The 2-hole-RDM as a 4-index tensor. Indices follow the internal convention of tqdm[p, q, r, s] == - :math:`a_{p}a_{q}a_{r}^{\dagger}a_{s}^{\dagger}`. + $a_{p}a_{q}a_{r}^{\dagger}a_{s}^{\dagger}$. opdm (numpy.ndarray): The 1-RDM as a 2-index tensor. Indices follow the internal convention of opdm[p, q] == - :math:`a_{p}^{\dagger}a_{q}`. + $a_{p}^{\dagger}a_{q}$. Returns: tpdm (numpy.ndarray): The 2-RDM matrix. @@ -99,7 +99,7 @@ def map_two_hole_dm_to_one_hole_dm(tqdm, hole_number): Args: tqdm (numpy.ndarray): The 2-hole-RDM as a 4-index tensor. Indices follow the internal convention of tqdm[p, q, r, s] == - :math:`a_{p}a_{q}a_{r}^{\dagger}a_{s}^{\dagger}`. + $a_{p}a_{q}a_{r}^{\dagger}a_{s}^{\dagger}$. hole_number (float): Number of holes in the system. For chemical systems this is usually the number of spin orbitals minus the number of electrons. @@ -117,7 +117,7 @@ def map_one_pdm_to_one_hole_dm(opdm): Args: opdm (numpy.ndarray): The 1-RDM as a 2-index tensor. Indices follow the internal convention of opdm[p, q] == - :math:`a_{p}^{\dagger}a_{q}`. + $a_{p}^{\dagger}a_{q}$. Returns: oqdm (numpy.ndarray): the 1-hole-RDM transformed from a 1-RDM. @@ -133,7 +133,7 @@ def map_one_hole_dm_to_one_pdm(oqdm): Args: oqdm (numpy.ndarray): The 1-hole-RDM as a 2-index tensor. Indices follow the internal convention of oqdm[p, q] == - :math:`a_{p}a_{q}^{\dagger}`. + $a_{p}a_{q}^{\dagger}$. Returns: oqdm (numpy.ndarray): the 1-hole-RDM transformed from a 1-RDM. @@ -149,10 +149,10 @@ def map_two_pdm_to_particle_hole_dm(tpdm, opdm): Args: tpdm (numpy.ndarray): The 2-RDM as a 4-index tensor. Indices follow the internal convention of tpdm[p, q, r, s] == - :math:`a_{p}^{\dagger}a_{q}^{\dagger}a_{r}a_{s}`. + $a_{p}^{\dagger}a_{q}^{\dagger}a_{r}a_{s}$. opdm (numpy.ndarray): The 1-RDM as a 2-index tensor. Indices follow the internal convention of opdm[p, q] == - :math:`a_{p}^{\dagger}a_{q}`. + $a_{p}^{\dagger}a_{q}$. Returns: phdm (numpy.ndarray): The particle-hole matrix. @@ -173,10 +173,10 @@ def map_particle_hole_dm_to_two_pdm(phdm, opdm): Args: phdm (numpy.ndarray): The 2-particle-hole-RDM as a 4-index tensor. Indices follow the internal convention of phdm[p, q, r, s] == - :math:`a_{p}^{\dagger}a_{q}a_{r}^{\dagger}a_{s}`. + $a_{p}^{\dagger}a_{q}a_{r}^{\dagger}a_{s}$. opdm (numpy.ndarray): The 1-RDM as a 2-index tensor. Indices follow the internal convention of opdm[p, q] == - :math:`a_{p}^{\dagger}a_{q}`. + $a_{p}^{\dagger}a_{q}$. Returns: tpdm (numpy.ndarray): The 2-RDM matrix. @@ -197,7 +197,7 @@ def map_particle_hole_dm_to_one_pdm(phdm, num_particles, num_basis_functions): Args: phdm (numpy.ndarray): The 2-particle-hole-RDM as a 4-index tensor. Indices follow the internal convention of phdm[p, q, r, s] == - :math:`a_{p}^{\dagger}a_{q}a_{r}^{\dagger}a_{s}`. + $a_{p}^{\dagger}a_{q}a_{r}^{\dagger}a_{s}$. num_particles: number of particles in the system. num_basis_functions: number of spin-orbitals (usually the number of qubits)