TernaryTreeEncoding

class TernaryTreeEncoding(tree, mode_map=None)

Bases: object

A ternary tree compiled into the Pauli images of its Majorana operators.

Pairing the tree’s legs into Majorana operators and building their Pauli strings is done once, here, rather than per mapped operator. Pass one of these to the ternary-tree mappers.

The pairing follows Algorithm 1 of the Bonsai paper [1]: from each node, descend its X link and then Z links until reaching a leg, which carries \(\gamma\) of that node’s mode; the Y link likewise gives \(\gamma'\). This is what makes \(|0\cdots0\rangle\) the fermionic vacuum, so it is not an adjustable convention.

Usage

>>> from qiskit_fermions.mappers import TernaryTree, TernaryTreeEncoding
>>> enc = TernaryTreeEncoding(TernaryTree.breadth_first(4, 3))
>>> enc.num_modes, enc.max_weight
(4, 2)

The encoding is built once and reused across every operator mapped through it, so the pairing and string construction are not repeated per call. The qubit count of a mapped operator is given to the mapper rather than fixed here, so one encoding serves registers of any width from num_modes upwards.

Attributes

max_weight

The largest Pauli weight of any single Majorana operator.

num_modes

The number of fermionic modes this encoding covers.

Methods

majorana_image(majorana)

Return the Pauli image of Majorana operator majorana, as (qubit, label) pairs.

The index is 2 * mode + is_prime, matching gamma(). The string is ordered by ascending qubit.

>>> from qiskit_fermions.mappers import TernaryTree, TernaryTreeEncoding
>>> enc = TernaryTreeEncoding(TernaryTree.chain(3, "Z"))
>>> enc.majorana_image(4)
[(0, 'Z'), (1, 'Z'), (2, 'X')]

The coefficient is always exactly \(1\): a leg’s string is a product of Paulis on distinct qubits, so it is Hermitian and squares to the identity with no phase to correct.

total_parity(num_qubits)

Build the total fermionic parity observable of this encoding.

A tree’s \(2N+1\) legs pair into \(2N\) Majorana operators, leaving exactly one over, and that leftover is the total parity \(\prod_j (1 - 2 n_j)\).

Parameters:

num_qubits – the number of qubits for the resulting observable. Must be at least the encoding’s mode count; a larger value pads with the identity.

Returns:

a single Pauli string with a \(\pm 1\) coefficient.

Return type:

The total-parity observable

Raises:

ValueError – if num_qubits is below the encoding’s mode count.

An observable to measure, not a constraint to impose

Both eigenvalues of this operator are physical, so do not project a state onto the \(+1\) sector. A ternary-tree encoding spends one qubit per mode and is a unitary isomorphism onto the whole \(2^N\)-dimensional space: both parity sectors are represented, each with multiplicity \(2^{N-1}\), and there is no unphysical subspace to project away.

Local encodings that add ancilla qubits do work that way. The flow sets in the 1D and 2D Fermi-Hubbard guides use more qubits than modes, which makes the extra dimensions unphysical and a stabilizer subspace the thing that keeps the encoding faithful. Ternary trees add no such qubits and have no such subspace.

Note also that the string is not in general the all-\(Z\) string. It is the leg reached by the all-\(Z\) path, which carries the identity on every node not on that path.

Usage

On the Jordan-Wigner chain it is the full \(Z\) string:

>>> from qiskit_fermions.mappers import TernaryTree, TernaryTreeEncoding
>>> TernaryTreeEncoding(TernaryTree.chain(4, "Z")).total_parity(4)
<SparseObservable with 1 term on 4 qubits: (1+0j)(Z_3 Z_2 Z_1 Z_0)>

On the balanced tree at four modes it is not:

>>> TernaryTreeEncoding(TernaryTree.breadth_first(4, 3)).total_parity(4)
<SparseObservable with 1 term on 4 qubits: (1+0j)(Z_3 Z_0)>

The coefficient is \(\pm 1\) and depends on the tree, so it is computed rather than assumed.