Source code for qlauncher.hampy.debug
1"""Module with functionalities for debugging Hamiltonians and checking their boolean properties"""
2
3from itertools import product
4
5import matplotlib.pyplot as plt
6import numpy as np
7from qiskit.quantum_info import SparsePauliOp
8
9from qlauncher.hampy.object import Equation
10
11
[docs]
12class TruthTable:
13 """
14 Generates and analyzes a full truth table for a Hamiltonian represented as
15 either an :class:`Equation` or a :class:`qiskit.quantum_info.SparsePauliOp`.
16
17 The class evaluates the Hamiltonian on all possible bitstrings of length
18 ``size`` and exposes utilities for debugging, visualizing value
19 distributions, and checking boolean properties (e.g., whether the
20 Hamiltonian is binary-valued).
21
22 Parameters
23 ----------
24 equation : Equation or SparsePauliOp
25 Hamiltonian to evaluate. If an :class:`Equation` is provided, its
26 simplified SparsePauliOp is used. If a SparsePauliOp is provided,
27 the number of qubits is inferred.
28 return_int : bool, optional
29 Whether to cast diagonal values to integers. Defaults to ``True``.
30
31 Examples
32 --------
33 From an Equation:
34 >>> eq = Equation(2)
35 >>> x0, x1 = eq[0], eq[1]
36 >>> tt = TruthTable(x0 ^ x1)
37 >>> tt.truth_table
38 {'00': 0, '01': 1, '10': 1, '11': 0}
39
40 From a SparsePauliOp:
41 >>> from qiskit.quantum_info import SparsePauliOp
42 >>> H = SparsePauliOp.from_sparse_list([('Z', [0], 1.0)], 1)
43 >>> tt = TruthTable(H)
44 >>> tt['0'], tt['1']
45 (1, -1)
46
47 Get solutions for a given value:
48 >>> tt.get_solutions(tt.lowest_value)
49 ['1']
50
51 Check if the Hamiltonian is binary-valued:
52 >>> tt.check_if_binary() # Hamiltonian is binary if all energy values ar either 0 or 1
53 False
54
55 Plot the distribution of output energies:
56 >>> tt.plot_distribution()
57
58 Access row by integer or bitstring:
59 >>> tt[0]
60 1
61 >>> tt['1']
62 -1
63 """
64
65 def __init__(self, equation: Equation | SparsePauliOp, return_int: bool = True):
66 if isinstance(equation, SparsePauliOp):
67 hamiltonian = equation
68 size = hamiltonian.num_qubits
69 if not isinstance(size, int):
70 raise TypeError('Cannot read number of qubits from provided SparsePauliOp')
71 elif isinstance(equation, Equation):
72 hamiltonian = equation.hamiltonian
73 size = equation.size
74 self.size = size
75 self.return_int = return_int
76 self.truth_table = self._ham_to_truth(hamiltonian)
77 self.lowest_value = min(self.truth_table.values())
78
[docs]
79 def count(self, value: int) -> int:
80 return list(self.truth_table.values()).count(value)
81
[docs]
82 def get_solutions(self, value: int) -> list[str]:
83 return list(filter(lambda x: self.truth_table[x] == value, self.truth_table.keys()))
84
[docs]
85 def count_min_value_solutions(self) -> int:
86 return self.count(self.lowest_value)
87
[docs]
88 def get_min_value_solutions(self) -> list[str]:
89 return self.get_solutions(self.lowest_value)
90
[docs]
91 def check_if_binary(self) -> bool:
92 return all((value == 0 or value == 1) for value in self.truth_table.values())
93
[docs]
94 def plot_distribution(self) -> None:
95 values = list(self.truth_table.values())
96 counts, bins = np.histogram(values, max(values) + 1)
97 plt.stairs(counts, bins)
98 plt.show()
99
100 def _ham_to_truth(self, hamiltonian: SparsePauliOp) -> dict[str, int]:
101 return {
102 ''.join(reversed(bitstring)): value
103 for bitstring, value in zip(
104 product(('0', '1'), repeat=self.size),
105 (int(x.real) for x in hamiltonian.to_matrix().diagonal()) if self.return_int else hamiltonian.to_matrix().diagonal(),
106 strict=True,
107 )
108 }
109
110 def __getitem__(self, index: str | int):
111 if isinstance(index, int):
112 index = bin(index)[2:].zfill(self.size)
113 return self.truth_table[index]