Hamiltonian Adaptive Ternary Tree#
HATT is an algorithm for the construction of Ternary-Tree encodings which are optimised for the Hamiltonian of interest.
This notebook shows how to reproduce the results of: Y. Liu et al., “HATT: Hamiltonian Adaptive Ternary Tree for Optimizing Fermion-to-Qubit Mapping,” 2025 IEEE International Symposium on High Performance Computer Architecture (HPCA), Las Vegas, NV, USA, 2025, pp. 143-157, doi: 10.1109/HPCA61900.2025.00022.
Preprocess Hamiltonian#
The first thing we need to do for HATT is to process our fermionic operator into a majorana operator.
There’s a function to do this in ferrmion.utils if you need it, and the details aren’t so important. We’ll start with the small example given in the original paper.
from ferrmion.core import fermionic_to_sparse_majorana
import numpy as np
n_modes = 3
ones = np.zeros((n_modes, n_modes))
twos = np.zeros((n_modes, n_modes, n_modes, n_modes))
ones[0, 0] = 1
twos[1, 2, 1, 2] = 2
majorana_ham = fermionic_to_sparse_majorana(signatures=["+-","++--"], coeffs=[ones,twos],constant_energy=0)
majorana_ham
{(0, 1): 0.5j, (4, 5): -0.5j, (2, 3): -0.5j, (2, 3, 4, 5): (0.5+0j)}
By rerodering the modes using \(\{\gamma_i,\gamma_j\}=2\delta_{i,j}\) you can simplify this down to the following:
majorana_ham = {(0, 1): 0.5j, (2, 3): -0.5j, (4, 5): -0.5j, (2, 3, 4, 5): 0.5}
n_modes = 3
Algorithm 3#
There are three algorithms presented, each iteratively adding some improvements. We’ll jump straight to algorithm 3, which caches some information about the tree as we build it.
from itertools import permutations
from typing import Iterable
from ferrmion.encode import TernaryTree
from ferrmion.encode.ternary_tree_node import TTNode
def _qubit_term_weight(term: Iterable, comb: tuple[int, int, int]) -> int:
"""Find the single-qubit Pauli-weight of majorana terms.
If any pauli term is found an even number f times, we obtain I, weight = 0.
If we find all three pauli terms, return I (with an imaginary ccoefficient), weight = 0
If we find either one pauli or two then the weight = 1.
Args:
term (Iterable): Indices of term in our majorana-hamiltonian.
comb (tuple[int, int, int]): Combination of indices to weigh (x,y,z).
Returns:
int: Weight of the term.
"""
term_array = np.array([t for t in term])
odd_parity_paulis = np.array(
[np.count_nonzero(np.array(term_array - index)) % 2 for index in comb]
)
print(f"{term_array=}")
print(f"{comb=}")
print([np.array(term_array - index) for index in comb])
non_commuting = np.sum(odd_parity_paulis) % 3
weight = int(non_commuting != 0)
print(f"{weight=}")
return weight
def _reduce_hamiltonian(
majorana_ham: dict[tuple[int, ...], float],
parent_index: int,
selection: tuple[int, int, int],
) -> dict[tuple[int, ...], float]:
"""Simplify the Hamiltonian.
As we increase the qubit number, we iteratively remove majoranas
which act trivially on the remaining qubits.
We replace them with the index of their parent string
as going forward they are identical to the parent string.
Args:
majorana_ham (dict[tuple[int,...],float]): Current Hamiltonian.
parent_index (int): Qubit index of the parent node.
selection (tuple[int, int, int]): Indices of the majoranas to be replaced.
Returns:
dict[tuple[int,...],float]: Reduced Hamiltonian.
"""
new_ham = {}
for term, coeff in majorana_ham.items():
new_term = [i if i not in selection else parent_index for i in term]
if len(set(new_term)) != 1:
new_ham[tuple(new_term)] = coeff
return new_ham
We’ll first set up for iteratively building the tree.
We need:
a set of leaves and nodes
a way to keep track of which ones have been used already
maps from each node to its furthest child and parent which can be found by taking only z branches
# We need 2*M +1 leaves and M nodes.
nodes = {i: None for i in range(2 * n_modes + 1)}
for i in range(n_modes):
nodes[2 * n_modes + 1 + i] = TTNode(qubit_label=i)
# Start with all the leaves unassigned
unassigned = {*range(2 * n_modes + 1)}
# We create two maps, of z_ancestors and z_descendants
ancestor_map = {i: i for i in nodes}
descendant_map = {i: i for i in nodes}
# We can also total up the pauli-weight as we go to save effort later
total_weight = 0
The next step is to iteratively update the tree, using a greedy method to explicitly check which combination of possible child nodes gives us the smallest Pauli-weight.
for i in range(n_modes):
parent_index = 2 * n_modes + 1 + i
parent = nodes[parent_index]
min = np.inf
selection = None
for comb in permutations(unassigned, 2):
small_y = None
small_x = None
x_index, z_index = comb
small_x = descendant_map[x_index]
# discard this combination
if small_x == 2 * n_modes:
continue
if small_x % 2 == 0:
small_y = small_x + 1
else:
small_y = small_x - 1
# We can't use this index for y a
# it has been used in the combination already
# so we'd be replacing our x or z!
if small_y in comb:
# print(comb, small_y)
# print("small y in comb")
continue
y_index = ancestor_map[small_y]
if y_index in comb:
# print(comb, y_index)
# print("y index in comb")
continue
if small_x % 2 == 0:
comb = np.array([x_index, y_index, z_index], dtype=np.uint)
else:
comb = np.array([y_index, x_index, z_index], dtype=np.uint)
comb = [int(i) for i in comb]
weight = np.sum(
[_qubit_term_weight(term, comb) for term in majorana_ham.keys()]
)
print(f"This weight {weight=}\n")
if weight < min:
print(f"NEW MIN {weight=}")
print(f"NEW COMB {comb=}\n")
min = weight
selection = comb
total_weight += min
print(f"{comb=}")
print(f"{total_weight=}")
# Now find the Y pair of the x-node
for i, char in zip(selection, ["x", "y", "z"]):
if i in unassigned:
unassigned.remove(i)
if isinstance(nodes.get(i, None), TTNode):
parent.add_child(which_child=char, child_node=nodes.get(i))
else:
parent.leaf_majorana_indices[char] = i
z_index = selection[2]
z_desc = descendant_map[z_index]
descendant_map[parent_index] = z_desc
ancestor_map[z_index] = parent_index
ancestor_map[z_desc] = parent_index
unassigned.add(parent_index)
# See the docstring for this function above
print(f"Ham {majorana_ham=}")
majorana_ham = _reduce_hamiltonian(majorana_ham, parent_index, selection)
print(f"Reduced Ham {majorana_ham=}")
if len(unassigned) != 1:
raise ValueError("Not all nodes assigned by HATT.")
print(unassigned, total_weight)
root = nodes[unassigned.pop()]
term_array=array([0, 1])
comb=[0, 1, 2]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 2]
[array([2, 3]), array([1, 2]), array([0, 1])]
weight=1
term_array=array([4, 5])
comb=[0, 1, 2]
[array([4, 5]), array([3, 4]), array([2, 3])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[0, 1, 2]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([0, 1, 2, 3])]
weight=1
This weight weight=np.int64(3)
NEW MIN weight=np.int64(3)
NEW COMB comb=[0, 1, 2]
term_array=array([0, 1])
comb=[0, 1, 3]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 3]
[array([2, 3]), array([1, 2]), array([-1, 0])]
weight=1
term_array=array([4, 5])
comb=[0, 1, 3]
[array([4, 5]), array([3, 4]), array([1, 2])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[0, 1, 3]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-1, 0, 1, 2])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 4]
[array([0, 1]), array([-1, 0]), array([-4, -3])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 4]
[array([2, 3]), array([1, 2]), array([-2, -1])]
weight=0
term_array=array([4, 5])
comb=[0, 1, 4]
[array([4, 5]), array([3, 4]), array([0, 1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[0, 1, 4]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-2, -1, 0, 1])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 5]
[array([0, 1]), array([-1, 0]), array([-5, -4])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 5]
[array([2, 3]), array([1, 2]), array([-3, -2])]
weight=0
term_array=array([4, 5])
comb=[0, 1, 5]
[array([4, 5]), array([3, 4]), array([-1, 0])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[0, 1, 5]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-3, -2, -1, 0])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 6]
[array([0, 1]), array([-1, 0]), array([-6, -5])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 6]
[array([2, 3]), array([1, 2]), array([-4, -3])]
weight=0
term_array=array([4, 5])
comb=[0, 1, 6]
[array([4, 5]), array([3, 4]), array([-2, -1])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[0, 1, 6]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-4, -3, -2, -1])]
weight=0
This weight weight=np.int64(1)
NEW MIN weight=np.int64(1)
NEW COMB comb=[0, 1, 6]
term_array=array([0, 1])
comb=[0, 1, 2]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 2]
[array([2, 3]), array([1, 2]), array([0, 1])]
weight=1
term_array=array([4, 5])
comb=[0, 1, 2]
[array([4, 5]), array([3, 4]), array([2, 3])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[0, 1, 2]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([0, 1, 2, 3])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 3]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 3]
[array([2, 3]), array([1, 2]), array([-1, 0])]
weight=1
term_array=array([4, 5])
comb=[0, 1, 3]
[array([4, 5]), array([3, 4]), array([1, 2])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[0, 1, 3]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-1, 0, 1, 2])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 4]
[array([0, 1]), array([-1, 0]), array([-4, -3])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 4]
[array([2, 3]), array([1, 2]), array([-2, -1])]
weight=0
term_array=array([4, 5])
comb=[0, 1, 4]
[array([4, 5]), array([3, 4]), array([0, 1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[0, 1, 4]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-2, -1, 0, 1])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 5]
[array([0, 1]), array([-1, 0]), array([-5, -4])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 5]
[array([2, 3]), array([1, 2]), array([-3, -2])]
weight=0
term_array=array([4, 5])
comb=[0, 1, 5]
[array([4, 5]), array([3, 4]), array([-1, 0])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[0, 1, 5]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-3, -2, -1, 0])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[0, 1, 6]
[array([0, 1]), array([-1, 0]), array([-6, -5])]
weight=1
term_array=array([2, 3])
comb=[0, 1, 6]
[array([2, 3]), array([1, 2]), array([-4, -3])]
weight=0
term_array=array([4, 5])
comb=[0, 1, 6]
[array([4, 5]), array([3, 4]), array([-2, -1])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[0, 1, 6]
[array([2, 3, 4, 5]), array([1, 2, 3, 4]), array([-4, -3, -2, -1])]
weight=0
This weight weight=np.int64(1)
term_array=array([0, 1])
comb=[2, 3, 0]
[array([-2, -1]), array([-3, -2]), array([0, 1])]
weight=1
term_array=array([2, 3])
comb=[2, 3, 0]
[array([0, 1]), array([-1, 0]), array([2, 3])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 0]
[array([2, 3]), array([1, 2]), array([4, 5])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 0]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([2, 3, 4, 5])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[2, 3, 1]
[array([-2, -1]), array([-3, -2]), array([-1, 0])]
weight=1
term_array=array([2, 3])
comb=[2, 3, 1]
[array([0, 1]), array([-1, 0]), array([1, 2])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 1]
[array([2, 3]), array([1, 2]), array([3, 4])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 1]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([1, 2, 3, 4])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[2, 3, 4]
[array([-2, -1]), array([-3, -2]), array([-4, -3])]
weight=0
term_array=array([2, 3])
comb=[2, 3, 4]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 4]
[array([2, 3]), array([1, 2]), array([0, 1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 4]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-2, -1, 0, 1])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[2, 3, 5]
[array([-2, -1]), array([-3, -2]), array([-5, -4])]
weight=0
term_array=array([2, 3])
comb=[2, 3, 5]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 5]
[array([2, 3]), array([1, 2]), array([-1, 0])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 5]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-3, -2, -1, 0])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[2, 3, 6]
[array([-2, -1]), array([-3, -2]), array([-6, -5])]
weight=0
term_array=array([2, 3])
comb=[2, 3, 6]
[array([0, 1]), array([-1, 0]), array([-4, -3])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 6]
[array([2, 3]), array([1, 2]), array([-2, -1])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 6]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-4, -3, -2, -1])]
weight=1
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[2, 3, 0]
[array([-2, -1]), array([-3, -2]), array([0, 1])]
weight=1
term_array=array([2, 3])
comb=[2, 3, 0]
[array([0, 1]), array([-1, 0]), array([2, 3])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 0]
[array([2, 3]), array([1, 2]), array([4, 5])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 0]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([2, 3, 4, 5])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[2, 3, 1]
[array([-2, -1]), array([-3, -2]), array([-1, 0])]
weight=1
term_array=array([2, 3])
comb=[2, 3, 1]
[array([0, 1]), array([-1, 0]), array([1, 2])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 1]
[array([2, 3]), array([1, 2]), array([3, 4])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 1]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([1, 2, 3, 4])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[2, 3, 4]
[array([-2, -1]), array([-3, -2]), array([-4, -3])]
weight=0
term_array=array([2, 3])
comb=[2, 3, 4]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 4]
[array([2, 3]), array([1, 2]), array([0, 1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 4]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-2, -1, 0, 1])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[2, 3, 5]
[array([-2, -1]), array([-3, -2]), array([-5, -4])]
weight=0
term_array=array([2, 3])
comb=[2, 3, 5]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 5]
[array([2, 3]), array([1, 2]), array([-1, 0])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 5]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-3, -2, -1, 0])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[2, 3, 6]
[array([-2, -1]), array([-3, -2]), array([-6, -5])]
weight=0
term_array=array([2, 3])
comb=[2, 3, 6]
[array([0, 1]), array([-1, 0]), array([-4, -3])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 6]
[array([2, 3]), array([1, 2]), array([-2, -1])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 6]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-4, -3, -2, -1])]
weight=1
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[4, 5, 0]
[array([-4, -3]), array([-5, -4]), array([0, 1])]
weight=1
term_array=array([2, 3])
comb=[4, 5, 0]
[array([-2, -1]), array([-3, -2]), array([2, 3])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 0]
[array([0, 1]), array([-1, 0]), array([4, 5])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 0]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([2, 3, 4, 5])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[4, 5, 1]
[array([-4, -3]), array([-5, -4]), array([-1, 0])]
weight=1
term_array=array([2, 3])
comb=[4, 5, 1]
[array([-2, -1]), array([-3, -2]), array([1, 2])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 1]
[array([0, 1]), array([-1, 0]), array([3, 4])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 1]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([1, 2, 3, 4])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[4, 5, 2]
[array([-4, -3]), array([-5, -4]), array([-2, -1])]
weight=0
term_array=array([2, 3])
comb=[4, 5, 2]
[array([-2, -1]), array([-3, -2]), array([0, 1])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 2]
[array([0, 1]), array([-1, 0]), array([2, 3])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 2]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([0, 1, 2, 3])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[4, 5, 3]
[array([-4, -3]), array([-5, -4]), array([-3, -2])]
weight=0
term_array=array([2, 3])
comb=[4, 5, 3]
[array([-2, -1]), array([-3, -2]), array([-1, 0])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 3]
[array([0, 1]), array([-1, 0]), array([1, 2])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 3]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-1, 0, 1, 2])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[4, 5, 6]
[array([-4, -3]), array([-5, -4]), array([-6, -5])]
weight=0
term_array=array([2, 3])
comb=[4, 5, 6]
[array([-2, -1]), array([-3, -2]), array([-4, -3])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 6]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 6]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-4, -3, -2, -1])]
weight=1
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[4, 5, 0]
[array([-4, -3]), array([-5, -4]), array([0, 1])]
weight=1
term_array=array([2, 3])
comb=[4, 5, 0]
[array([-2, -1]), array([-3, -2]), array([2, 3])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 0]
[array([0, 1]), array([-1, 0]), array([4, 5])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 0]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([2, 3, 4, 5])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[4, 5, 1]
[array([-4, -3]), array([-5, -4]), array([-1, 0])]
weight=1
term_array=array([2, 3])
comb=[4, 5, 1]
[array([-2, -1]), array([-3, -2]), array([1, 2])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 1]
[array([0, 1]), array([-1, 0]), array([3, 4])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 1]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([1, 2, 3, 4])]
weight=1
This weight weight=np.int64(3)
term_array=array([0, 1])
comb=[4, 5, 2]
[array([-4, -3]), array([-5, -4]), array([-2, -1])]
weight=0
term_array=array([2, 3])
comb=[4, 5, 2]
[array([-2, -1]), array([-3, -2]), array([0, 1])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 2]
[array([0, 1]), array([-1, 0]), array([2, 3])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 2]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([0, 1, 2, 3])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[4, 5, 3]
[array([-4, -3]), array([-5, -4]), array([-3, -2])]
weight=0
term_array=array([2, 3])
comb=[4, 5, 3]
[array([-2, -1]), array([-3, -2]), array([-1, 0])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 3]
[array([0, 1]), array([-1, 0]), array([1, 2])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 3]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-1, 0, 1, 2])]
weight=0
This weight weight=np.int64(2)
term_array=array([0, 1])
comb=[4, 5, 6]
[array([-4, -3]), array([-5, -4]), array([-6, -5])]
weight=0
term_array=array([2, 3])
comb=[4, 5, 6]
[array([-2, -1]), array([-3, -2]), array([-4, -3])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 6]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 6]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-4, -3, -2, -1])]
weight=1
This weight weight=np.int64(2)
comb=(6, 5)
total_weight=np.int64(1)
Ham majorana_ham={(0, 1): 0.5j, (2, 3): (-0-0.5j), (4, 5): (-0-0.5j), (2, 3, 4, 5): 0.5}
Reduced Ham majorana_ham={(2, 3): (-0-0.5j), (4, 5): (-0-0.5j), (2, 3, 4, 5): 0.5}
term_array=array([2, 3])
comb=[2, 3, 4]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 4]
[array([2, 3]), array([1, 2]), array([0, 1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 4]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-2, -1, 0, 1])]
weight=0
This weight weight=np.int64(2)
NEW MIN weight=np.int64(2)
NEW COMB comb=[2, 3, 4]
term_array=array([2, 3])
comb=[2, 3, 5]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 5]
[array([2, 3]), array([1, 2]), array([-1, 0])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 5]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-3, -2, -1, 0])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[2, 3, 7]
[array([0, 1]), array([-1, 0]), array([-5, -4])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 7]
[array([2, 3]), array([1, 2]), array([-3, -2])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 7]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-5, -4, -3, -2])]
weight=1
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[2, 3, 4]
[array([0, 1]), array([-1, 0]), array([-2, -1])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 4]
[array([2, 3]), array([1, 2]), array([0, 1])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 4]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-2, -1, 0, 1])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[2, 3, 5]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 5]
[array([2, 3]), array([1, 2]), array([-1, 0])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[2, 3, 5]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-3, -2, -1, 0])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[2, 3, 7]
[array([0, 1]), array([-1, 0]), array([-5, -4])]
weight=1
term_array=array([4, 5])
comb=[2, 3, 7]
[array([2, 3]), array([1, 2]), array([-3, -2])]
weight=0
term_array=array([2, 3, 4, 5])
comb=[2, 3, 7]
[array([0, 1, 2, 3]), array([-1, 0, 1, 2]), array([-5, -4, -3, -2])]
weight=1
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[4, 5, 2]
[array([-2, -1]), array([-3, -2]), array([0, 1])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 2]
[array([0, 1]), array([-1, 0]), array([2, 3])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 2]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([0, 1, 2, 3])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[4, 5, 3]
[array([-2, -1]), array([-3, -2]), array([-1, 0])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 3]
[array([0, 1]), array([-1, 0]), array([1, 2])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 3]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-1, 0, 1, 2])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[4, 5, 7]
[array([-2, -1]), array([-3, -2]), array([-5, -4])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 7]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 7]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-5, -4, -3, -2])]
weight=1
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[4, 5, 2]
[array([-2, -1]), array([-3, -2]), array([0, 1])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 2]
[array([0, 1]), array([-1, 0]), array([2, 3])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 2]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([0, 1, 2, 3])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[4, 5, 3]
[array([-2, -1]), array([-3, -2]), array([-1, 0])]
weight=1
term_array=array([4, 5])
comb=[4, 5, 3]
[array([0, 1]), array([-1, 0]), array([1, 2])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 3]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-1, 0, 1, 2])]
weight=0
This weight weight=np.int64(2)
term_array=array([2, 3])
comb=[4, 5, 7]
[array([-2, -1]), array([-3, -2]), array([-5, -4])]
weight=0
term_array=array([4, 5])
comb=[4, 5, 7]
[array([0, 1]), array([-1, 0]), array([-3, -2])]
weight=1
term_array=array([2, 3, 4, 5])
comb=[4, 5, 7]
[array([-2, -1, 0, 1]), array([-3, -2, -1, 0]), array([-5, -4, -3, -2])]
weight=1
This weight weight=np.int64(2)
comb=(7, 5)
total_weight=np.int64(3)
Ham majorana_ham={(2, 3): (-0-0.5j), (4, 5): (-0-0.5j), (2, 3, 4, 5): 0.5}
Reduced Ham majorana_ham={(8, 5): (-0-0.5j), (8, 8, 8, 5): 0.5}
term_array=array([8, 5])
comb=[8, 5, 7]
[array([ 0, -3]), array([3, 0]), array([ 1, -2])]
weight=1
term_array=array([8, 8, 8, 5])
comb=[8, 5, 7]
[array([ 0, 0, 0, -3]), array([3, 3, 3, 0]), array([ 1, 1, 1, -2])]
weight=1
This weight weight=np.int64(2)
NEW MIN weight=np.int64(2)
NEW COMB comb=[8, 5, 7]
term_array=array([8, 5])
comb=[8, 5, 7]
[array([ 0, -3]), array([3, 0]), array([ 1, -2])]
weight=1
term_array=array([8, 8, 8, 5])
comb=[8, 5, 7]
[array([ 0, 0, 0, -3]), array([3, 3, 3, 0]), array([ 1, 1, 1, -2])]
weight=1
This weight weight=np.int64(2)
comb=[8, 5, 7]
total_weight=np.int64(5)
Ham majorana_ham={(8, 5): (-0-0.5j), (8, 8, 8, 5): 0.5}
Reduced Ham majorana_ham={}
{9} 5
Finally, we can build the tree from our root node, which must be the only node without a parent.
tree = TernaryTree(n_modes=n_modes, root_node=root)
tree.enumeration_scheme = tree.default_enumeration_scheme()
tree.pauli_weight = total_weight
tree.as_dict()
{'x': {'x': 2, 'y': 3, 'z': 4}, 'y': 5, 'z': {'x': 0, 'y': 1, 'z': 6}}
Inbuilt function#
ferrmion has an inbuilt function which works in just the same way.
import json
from ferrmion.encode import TernaryTree, MaxNTO
from pathlib import Path
from ferrmion.hamiltonians import molecular_hamiltonian
folder = Path.cwd().joinpath(Path("../../../python/tests/"))
with open(folder.joinpath("./data/h2o_sto-3g.json"), "rb") as file:
data = json.load(file)
ones = np.array(data["ones"])
twos = np.array(data["twos"])
fham = molecular_hamiltonian(ones, twos, 0.0)
mham = fermionic_to_sparse_majorana(signatures=["+-","++--"], coeffs=[ones,twos],constant_energy=0)
from ferrmion.visualise import draw_tt
from ferrmion.optimize.hatt import hamiltonian_adaptive_ternary_tree
hatt = hamiltonian_adaptive_ternary_tree(fham, fham.n_modes)
print(hatt.pauli_weight)
hatt.as_dict()
draw_tt(hatt, enumeration_scheme=hatt.default_enumeration_scheme())
4668
hatt.flatpack()
[(13, (5, 6, 12)),
(5, (34, 35, 18)),
(6, (26, 27, 19)),
(12, (11, 39, 2)),
(11, (10, 8, 38)),
(2, (14, 15, 1)),
(10, (3, 9, 7)),
(8, (22, 23, 37)),
(1, (32, 33, 0)),
(3, (16, 17, 24)),
(9, (40, 41, 4)),
(7, (20, 21, 36)),
(0, (30, 31, None)),
(4, (28, 29, 25))]