monoprop

Interface

How a problem is expressed as input — operator formats, the reference state, and the circuit.

This page describes how a problem is expressed as input to monoprop: the operator formats you can start from, the reference state, and how a circuit is bundled for simulation. For the exact constructors and arguments, see Python API.

Operator formats

A problem is defined by an operator together with a reference state. monoprop accepts operators in three forms, each suited to a different starting point; see Notation for how they relate.

Fermionic operators

The natural starting point for quantum chemistry and lattice models. An operator is given as a weighted sum of products of fermionic creation and annihilation operators on numbered modes (for example c0c1c_0^\dagger c_1); use MajoranaPropagator.

from monoprop import MajoranaPropagator, Circuit, ExpGate
from monoprop.fermi import FermiOperator

# Observable: the hopping term c_0^† c_1 + c_1^† c_0 on 4 modes
# ('+' = creation, '-' = annihilation).
observable = FermiOperator(
    [[(0, "+"), (1, "-")], [(1, "+"), (0, "-")]],
    [1.0, 1.0],
    num_modes=4,
)
# One fermionic rotation generated by the number operator n_0 = c_0^† c_0,
# with the Hartree-Fock reference |0, 1> (modes 0 and 1 occupied).
generator = FermiOperator([[(0, "+"), (0, "-")]], [1.0], num_modes=4)
circuit = Circuit(initial_state=[0, 1], gates=[ExpGate(generator)], parameters=[0.3])

mp = MajoranaPropagator.from_circuit(circuit, observable, cutoff=4)
print(sorted(mp.evolved_operator().items()))  # keyed by Majorana indices

Qubit (Pauli) operators

The natural starting point when you already hold a qubit Hamiltonian. An operator is given as a real-coefficient sum of Paulis (tensor products of I,X,Y,ZI, X, Y, Z). For qubit problems, use PauliPropagator, which accepts Pauli operators and gates directly and truncates by Pauli weight.

from monoprop import PauliPropagator, Circuit, ExpGate, Pauli
from monoprop.pauli import PauliOperator

# Observable: Z⊗Z + 0.5 X⊗X on two qubits.
observable = PauliOperator({"ZZ": 1.0, "XX": 0.5}, num_qubits=2)
# One Pauli rotation exp(-i θ/2 · X_0) acting on qubit 0.
gate = ExpGate(PauliOperator({Pauli("X", 0): 1.0}, num_qubits=2))
circuit = Circuit(gates=[gate], parameters=[0.3])

mp = PauliPropagator.from_circuit(circuit, observable, cutoff=4)
print(sorted(mp.evolved_operator().items()))  # keys are Majorana indices

Majorana operators

In this case the monomials are specified explicitly as sets of Majorana mode indices. The coefficient is the one multiplying the bare Majorana product, so it must keep the monomial Hermitian: real for length-4 monomials, imaginary for length-2 (since Mν=i(ν2)mi1minM_\nu = i^{\binom{|\nu|}{2}} m_{i_1}\cdots m_{i_n}, see Notation).

from monoprop import MajoranaPropagator, Circuit, ExpGate, MajoranaOperator

# Observable given directly as the Hermitian Majorana monomial on indices
# (0, 1, 2, 3) - a length-4 monomial, so a real coefficient.
observable = MajoranaOperator({(0, 1, 2, 3): 1.0}, 4)
# Generator i·m_3 m_4 - a length-2 monomial, so an imaginary coefficient.
generator = MajoranaOperator({(3, 4): 1j}, 4)
circuit = Circuit(initial_state=[0, 1], gates=[ExpGate(generator)], parameters=[0.3])

mp = MajoranaPropagator.from_circuit(circuit, observable, cutoff=4)
print(sorted(mp.evolved_operator().items()))  # keys are Majorana indices

The reference state

A simulation also needs a reference state, given either as a Slater determinant (a list of occupied orbitals) or a computational-basis state. Expectation values are computed against it; in the Schrödinger picture it is the state that is evolved.

The circuit

A circuit is a sequence of gates, each a Majorana rotation described by:

  • the generator — the Majorana monomial MγM_\gamma defining the rotation eiθMγ/2e^{-i\theta M_\gamma/2};
  • a parameter — the angle θ\theta; and
  • a parameter index — which entry of the parameter vector supplies that angle, so several gates can share one parameter.

From input to result

  1. Express the operator (fermionic, qubit, or Majorana) and choose a reference state.
  2. Construct a simulator from the operator, reference state, and truncation settings.
  3. Propagate the circuit to build the reusable propagated operator.
  4. Evaluate expectation values or gradients across many parameter values.

Steps 1-2 happen once; steps 3-4 form the inner loop of a variational optimisation, covered in Propagation Algorithm with the concrete API in Python API.

On this page