Fermion-Qubit Encoding#

Tools for converting fermionic operators and Hamiltonians into Pauli representations.

You can also decode() measurements in Z-basis back into Fock states.

ferrmion.encode.BravyiKitaev(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

The Bravyi-Kitaev encoding.

Parameters:
  • n_modes (int) – The number of fermionic modes.

  • n_qubits (int | None) – Optional number of qubits; defaults to n_modes.

Returns:

The Bravyi-Kitaev encoding.

Return type:

TernaryTree

Example

>>> from ferrmion.encode.ternary_tree import BravyiKitaev
>>> bk = BravyiKitaev(3)
ferrmion.encode.JKMN(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

The JKMN encoding.

The JKMN encoding gives a ternary tree with the minimum Pauli-weight.

Parameters:
  • n_modes (int) – The number of fermionic modes.

  • n_qubits (int | None) – Optional number of qubits; defaults to n_modes.

Returns:

The JKMN encoding.

Return type:

TernaryTree

Example

>>> from ferrmion.encode.ternary_tree import JKMN
>>> min_height = JKMN(3)
ferrmion.encode.JordanWigner(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

The Jordan-Wigner encoding.

Parameters:
  • n_modes (int) – The number of fermionic modes.

  • n_qubits (int | None) – Optional number of qubits; defaults to n_modes.

Returns:

The Jordan-Wigner encoding.

Return type:

TernaryTree

Example

>>> from ferrmion.encode.ternary_tree import JordanWigner
>>> jw = JordanWigner(3)
class ferrmion.encode.MajoranaEncoding(ipowers, symplectics, vacuum_state=None)[source]#

Bases: object

A fermion-to-qubit encoding defined by its Majorana operator representations, backed by the Rust [MajoranaEncoding] type.

anneal_enumeration(fham, temperature=None, initial_guess=None, coefficient_weighted=False, seed=None)#

Optimise the mode enumeration via simulated annealing without encoding.

Parameters:
  • fham – The fermionic Hamiltonian whose Pauli weight drives the search.

  • temperature – Initial annealing temperature. Defaults to n_modes.

  • initial_guess – Starting permutation. Defaults to identity.

  • coefficient_weighted – If True, minimise coefficient-weighted Pauli weight.

  • seed – Seed for the RNG driving permutation moves. Defaults to 1017.

Returns:

Tuple of (best_cost, MajoranaEncoding).

apply_mode_enumeration(mode_op_map)#

Return a new encoding with the fermionic modes reordered.

batch_pauli_weights(fham, permutations)#

Compute plain and coefficient-weighted Pauli weights for a batch of mode permutations in a single parallelised call.

Parameters:
  • fham – The fermionic Hamiltonian to weigh.

  • permutations – 2D uint array of shape (n_perms, n_modes).

Returns:

Tuple (plain, weighted) of two 1D float64 arrays.

static bravyi_kitaev(n_modes, n_qubits=None)#

The Bravyi-Kitaev encoding for n_modes fermionic modes.

decode(states)#

Decode an ensemble of Z-basis states into fermionic occupation vectors.

Parameters:

states – 2D boolean array of shape (n_states, n_qubits).

Returns:

2D boolean array of shape (n_states, n_modes).

Raises:

ValueError – if any state cannot be decoded for this encoding.

edge_operator(edge_indices, coeff=Ellipsis, with_conjugate=False)#

The encoded edge operator of a pair of modes.

encode(fham)#

Encode a fermionic Hamiltonian into a qubit Hamiltonian.

encode_annealed(fham, temperature=None, initial_guess=None, coefficient_weighted=True, seed=None)#

Encode a Hamiltonian, optimising mode enumeration via simulated annealing.

Parameters:
  • fham – The fermionic Hamiltonian to encode.

  • temperature – Initial annealing temperature. Defaults to n_modes // 2.

  • initial_guess – Starting permutation. Defaults to identity.

  • coefficient_weighted – If True, minimise coefficient-weighted Pauli weight.

  • seed – Seed for the RNG driving permutation moves. Defaults to 1017.

Returns:

Tuple of (QubitHamiltonian, MajoranaEncoding) — the encoded Hamiltonian and the encoding with the optimised mode enumeration.

encode_fermion_product(signature, mode_indices, coeff=Ellipsis, with_conjugate=False)#

Encode a single product of ladder operators.

Parameters:
  • signature – The fermionic operator signature, composed of “+” and “-“.

  • mode_indices – The mode index for each ladder operator.

  • coeff – The operator coefficient.

  • with_conjugate – Also add the reversed-index hermitian conjugate.

static from_flatpack(flatpack, n_qubits=None)#

Build an encoding from a ternary-tree flatpack representation.

Parameters:
  • flatpack – List of (qubit_index, (x_child, y_child, z_child)) tuples.

  • n_qubits – Optional number of qubits; defaults to the number of tree nodes.

static from_json(data)#

Reconstruct an encoding from the dictionary produced by to_json.

hartree_fock_state(fermionic_hf_state, mode_op_map=None)#

Compute the Hartree-Fock state in the encoded basis.

Parameters:
  • fermionic_hf_state – 1D boolean array of mode occupations.

  • mode_op_map – Optional permutation mapping modes to operator pairs.

Returns:

1D boolean array — the qubit Hartree-Fock state in the Z basis.

interaction_operator(mode_indices, coeff=Ellipsis, physicist_notation=True, with_conjugate=False)#

The encoded interaction operator of four modes.

ipowers#

Phase exponents (mod 4) for each Majorana operator row.

static jkmn(n_modes, n_qubits=None)#

The JKMN (minimum-height ternary tree) encoding for n_modes modes.

static jordan_wigner(n_modes, n_qubits=None)#

The Jordan-Wigner encoding for n_modes fermionic modes.

static maxnto(n_modes)#

The MaxNTO k-NTO encoding for n_modes fermionic modes.

Requires n_modes - 1 to be odd.

n_modes#

Number of fermionic modes.

n_qubits#

Number of qubits.

number_operator(mode, coeff=Ellipsis)#

The encoded number operator of a mode.

static parity(n_modes, n_qubits=None)#

The parity encoding for n_modes fermionic modes.

symplectic_matrix#

The symplectic matrix in [x_block | z_block] layout, of shape (2*n_modes, 2*n_qubits).

to_json()#

Serialise the encoding to a JSON-compatible dictionary with "ipowers", "symplectics" and "vacuum_state" keys.

vacuum_state#

The vacuum state in the Z basis.

ferrmion.encode.MaxNTO(n_modes: int) MajoranaEncoding[source]#

The MaxNTO k-NTO encoding for n_modes fermionic modes.

Alias for ferrmion.core.MajoranaEncoding.maxnto().

ferrmion.encode.Parity(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

The parity encoding.

Parameters:
  • n_modes (int) – The number of fermionic modes.

  • n_qubits (int | None) – Optional number of qubits; defaults to n_modes.

Returns:

The parity encoding.

Return type:

TernaryTree

Example

>>> from ferrmion.encode.ternary_tree import Parity
>>> parity = Parity(3)
class ferrmion.encode.TernaryTree(n_modes: int, n_qubits: None | int = None, root_node: TTNode | None = None)[source]#

Bases: object

Builder for ternary tree encodings of fermionic operators.

n_modes#

The number of fermionic modes to be encoded.

Type:

int

n_qubits#

The number of qubits in encoded operators.

Type:

int

root_node#

The root node of the tree.

Type:

TTNode

enumeration_scheme#

The enumeration scheme.

Type:

dict[str, tuple[int, int]] | None

build_encoding()[source]#

Build the Rust-backed MajoranaEncoding for the tree.

default_mode_op_map()#

Create a default mode operator map for the tree.

default_enumeration_scheme()[source]#

Create a default enumeration scheme for the tree.

as_dict()[source]#

Return the tree structure as a dictionary.

add_node(node_string

str): Add a node to the tree.

branch_pauli_map()#

Create a map from each branch string to a Pauli string.

string_pairs()#

Return the pair of branch strings which correspond to each node.

Simple Example:
>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> tree = TernaryTree(4)
>>> tree.add_node('x')
>>> tree.enumeration_scheme = tree.default_enumeration_scheme()
>>> tree.as_dict()
Advanced Usage:
>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> jw_tree = TernaryTree.jordan_wigner(4)
>>> encoding = jw_tree.build_encoding()
classmethod BK(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Alias for the Bravyi-Kitaev encoding.

classmethod BravyiKitaev(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Create a Bravyi-Kitaev encoding tree.

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> bk_tree = TernaryTree.bravyi_kitaev(3)
classmethod JKMN(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Create a JKMN (minimum-height) encoding tree.

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> min_height_tree = TernaryTree.jkmn(3)
classmethod JW(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Alias for the Jordan-Wigner encoding.

classmethod JordanWigner(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Create a Jordan-Wigner encoding tree.

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> jw_tree = TernaryTree.jordan_wigner(3)
classmethod PE(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Create a parity encoding with this tree’s number of modes.

classmethod Parity(n_modes: int, n_qubits: int | None = None) TernaryTree[source]#

Create a parity encoding tree.

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> parity_tree = TernaryTree.parity(3)
add_node(node_string: str) TernaryTree[source]#

Add a node to the tree.

Parameters:

node_string (str) – The string representation of the node.

Returns:

The tree with the node added.

Return type:

TernaryTree

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> tree = TernaryTree(3)
>>> tree.add_node('x')
as_dict()[source]#

Return the tree structure as a dictionary.

property branch_pauli_map: dict[str, str]#

Create a map from each branch string to a Pauli string.

Returns:

A dictionary of all branch strings with their corresponding Pauli strings.

Return type:

dict[str, str]

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> tree = TernaryTree(3)
>>> tree.add_node('x')
>>> tree.add_node('xz')
>>> tree.branch_pauli_map
{'xx': 'XXI',
'xzx': 'XZX',
'y': 'YII',
'xy': 'XYI',
'xzz': 'XZZ',
'xzy': 'XZY',
'z': 'ZII'}
build_encoding(mode_enumeration: list[int] | None = None) MajoranaEncoding[source]#

Build the Rust-backed encoding for the tree.

The tree’s default_mode_op_map is applied, so mode i of the returned encoding corresponds to the tree node enumerated as mode default_mode_op_map[i].

Returns:

The encoding represented by this tree.

Return type:

MajoranaEncoding

decode(states: ndarray[tuple[int, ...], dtype[bool]]) ndarray[tuple[int, ...], dtype[bool]][source]#

Decode Z-basis states into fermionic occupation vectors.

Parameters:

states – 2D boolean array of shape (n_states, n_qubits).

Returns:

2D boolean array of shape (n_states, n_modes).

Raises:

ValueError – if any state cannot be decoded for this encoding.

default_enumeration_scheme() dict[str, tuple[int, int]][source]#

Create a default enumeration scheme for the tree.

Note

The tuple is organised as (modes, qubits).

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> tree = TernaryTree.jordan_wigner(3)
>>> tree.default_enumeration_scheme()
{"": (0,0), "z": (1,1), "zz": (2,2)}
edge_operator(edge_indices: tuple[int, int], coeff: complex | float = 1.0, with_conjugate: bool = False) QubitHamiltonian[source]#

Return the edge operator of a pair of modes for this encoding.

encode(fham: FermionHamiltonian, parallelize: bool = True, heuristic: str = 'min_weight', seed: int | None = None, backend: str = 'dense_transpose') QubitHamiltonian[source]#

Encode a Hamiltonian, using TOPP-HATT optimisation.

Parameters:
  • fham – The FermionHamiltonian to encode.

  • parallelize – Whether to parallelize the encoding.

  • heuristic – Node-selection strategy. One of "min_weight" (evaluate every active node and keep the lowest Pauli weight), "x_first" (lowest-indexed active node), "z_first" (highest-indexed active node), or "random" (uniformly random active node using seed).

  • seed – RNG seed for heuristic="random". Ignored otherwise; defaults to 0 when omitted.

  • backend – Term-store backend driving the optimisation, "sparse" (default), "dense_transpose" (transposed bit-vector layout) or "sparse_transpose" (sparse inverted index: a sorted list of term indices per Majorana). The transposed backends do no term deduplication and may produce a different but valid encoding; they are provided for performance comparison.

Returns:

The encoded qubit Hamiltonian.

Return type:

QubitHamiltonian

encode_annealed(fham: FermionHamiltonian, temperature: int | None = None, initial_guess: list[int] | None = None, coefficient_weighted: bool = True, seed: int | None = None) QubitHamiltonian[source]#

Encode a Hamiltonian, optimising mode enumeration via simulated annealing.

Parameters:
  • fham (FermionHamiltonian) – The fermionic Hamiltonian to encode.

  • temperature (int | None) – Initial annealing temperature. Defaults to n_modes // 2.

  • initial_guess (list[int] | None) – Starting permutation. Defaults to identity.

  • coefficient_weighted (bool) – If True, minimise coefficient-weighted Pauli weight.

  • seed (int | None) – Seed for the RNG driving permutation moves. Defaults to 1017 when omitted.

Returns:

The encoded qubit Hamiltonian.

Return type:

QubitHamiltonian

encode_naive(fham: FermionHamiltonian) QubitHamiltonian[source]#

Encode a fermionic Hamiltonian into a qubit Hamiltonian.

Parameters:

fham (FermionHamiltonian) – The fermionic Hamiltonian to encode.

Returns:

The encoded qubit Hamiltonian.

Return type:

QubitHamiltonian

property enumeration_scheme: dict[str, tuple[int, int]]#

Get the enumeration scheme for the tree.

Note

The tuple is organised as (modes, qubits).

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> tree = TernaryTree.jordan_wigner(3)
>>> tree.enumeration_scheme
{"": (0,0), "z": (1,1), "zz": (2,2)}
flatpack() TTFlatpack[source]#

Create a TTFlatpack from the tree, which can be saved or passed to rust functions.

Node children are represented by their qubit index (an int that also appears as the first element of some flatpack entry). Leaf children with a known Majorana index are encoded as majorana_index + max_node_index + 1, which is strictly greater than every node qubit index (including when the Majorana index is 0) and can therefore never be confused with a node. Leaves without a known Majorana index are represented as None.

Returns:

list[tuple[int, tuple[int | None, int | None, int | None]]]

Example

>>> TernaryTree.jordan_wigner(4).flatpack()
>>> [(0, (4, 5,1)), (1, (6,7,2)), (2, (8,9,3)), (3, (10,11,4))]
classmethod from_flatpack(flatpack: TTFlatpack) TernaryTree[source]#

Construct a TernaryTree from a TTFlatpack.

Parameters:

flatpack – The flatpack representation of the tree.

Returns:

A new TernaryTree instance.

Raises:

TypeError – If the flatpack is invalid.

hartree_fock_state(fermionic_hf_state: ndarray[tuple[int, ...], dtype[bool]], mode_op_map: ndarray[tuple[int, ...], dtype[uint64]] | list[int] | None = None) ndarray[tuple[int, ...], dtype[bool]][source]#

Find the Hartree-Fock state of a majorana string encoding.

Parameters:
  • fermionic_hf_state (NDArray[int]) – An array of mode occupations.

  • mode_op_map (dict[int, int]) – An array mapping modes to pairs of majorana strings mode_op_map[i]=j => i -> (2j,2j+1)

Returns:

The Hartree-Fock ground state in computational basis.

Return type:

NDArray

interaction_operator(mode_indices: tuple[int, int, int, int], coeff: complex | float = 1.0, physicist_notation: bool = True, with_conjugate: bool = False) QubitHamiltonian[source]#

Return an interaction operator of four modes for this encoding.

number_operator(mode: int, coeff: complex | float = 1.0) QubitHamiltonian[source]#

Return the number operator of a mode for this encoding.

property string_pairs: dict[str | int, tuple[str, str]]#

Return the pair of branch strings which correspond to each node.

Returns:

A dictionary of all node labels, j, with branch strings (2j, 2j+1).

Return type:

dict[str, tuple(str,str)]

Example

>>> from ferrmion.encode.ternary_tree import TernaryTree
>>> tree = TernaryTree(3)
>>> tree.add_node('x')
>>> tree.add_node('xz')
>>> tree.string_pairs
{'': ('xzz', 'y'), 'x': ('xx', 'xy'), 'xz': ('xzx', 'xzy')}
to_json() dict[source]#

Serialise the encoding represented by this tree.

Returns:

Dictionary with "ipowers", "symplectics" and "vacuum_state" keys.

Return type:

dict

topphatt(fham: FermionHamiltonian, parallelize: bool = True, heuristic: str = 'min_weight', seed: int | None = None, backend: str = 'dense_transpose') MajoranaEncoding[source]#

Encode a Hamiltonian, using TOPP-HATT optimisation.

Parameters:
  • fham – The FermionHamiltonian to encode.

  • parallelize – Whether to parallelize the encoding.

  • heuristic – Node-selection strategy. One of "min_weight" (evaluate every active node and keep the lowest Pauli weight), "x_first" (lowest-indexed active node), "z_first" (highest-indexed active node), or "random" (uniformly random active node using seed).

  • seed – RNG seed for heuristic="random". Ignored otherwise; defaults to 0 when omitted.

  • backend – Term-store backend driving the optimisation, "sparse" (default), "dense_transpose" (transposed bit-vector layout) or "sparse_transpose" (sparse inverted index: a sorted list of term indices per Majorana). The transposed backends do no term deduplication and may produce a different but valid encoding; they are provided for performance comparison.

Returns:

The encoded qubit Hamiltonian.

Return type:

QubitHamiltonian

property vacuum_state: ndarray[tuple[int, ...], dtype[bool]]#

The vacuum state of the encoding represented by this tree.