Source code for qlauncher.hampy.object

  1from typing import overload
  2
  3from qiskit.quantum_info import SparsePauliOp
  4
  5
[docs] 6class Equation: 7 """## Hampy Equation 8 Represents a binary or general-purpose Hamiltonian equation built on 9 :class:`qiskit.quantum_info.SparsePauliOp`. 10 The class provides logical-style operators (AND, OR, XOR, NOT) interpreted 11 as operations on Hamiltonians with binary energies {0, 1}, along with basic 12 arithmetic operations and utilities for analyzing the resulting operator. 13 14 Examples 15 -------- 16 Create an empty equation of size 3: 17 >>> eq = Equation(3) 18 >>> eq.hamiltonian 19 SparsePauliOp(['III'], coeffs=[0.]) 20 21 Create from a SparsePauliOp: 22 >>> from qiskit.quantum_info import SparsePauliOp 23 >>> H = SparsePauliOp.from_sparse_list([('Z', [0], 1.0)], 1) 24 >>> eq = Equation(H) 25 >>> eq.get_order() 26 1 27 28 Use variables and logical operators: 29 >>> eq = Equation(2) 30 >>> x0 = eq[0] 31 >>> x1 = eq[1] 32 33 XOR: 34 >>> h_xor = x0 ^ x1 35 >>> h_xor.hamiltonian 36 SparsePauliOp([...]) 37 38 OR: 39 >>> h_or = x0 | x1 40 41 AND: 42 >>> h_and = x0 & x1 43 44 Negation: 45 >>> h_not = ~x0 46 47 Combine equations arithmetically: 48 >>> h_sum = (x0 ^ x1) + (x0 & x1) 49 >>> h_scaled = 2.0 * h_sum 50 >>> h_divided = h_sum / 3 51 52 Export back to SparsePauliOp: 53 >>> H = h_sum.hamiltonian 54 """ 55 56 @overload 57 def __init__(self, size: int, /): ... 58 @overload 59 def __init__(self, hamiltonian: SparsePauliOp, /): ... 60 @overload 61 def __init__(self, sparse_list: list[tuple], size: int, /): ... 62 63 def __init__(self, argument, *args): 64 self.size: int 65 if isinstance(argument, int): 66 self.size = argument 67 self._hamiltonian = SparsePauliOp.from_sparse_list([('I', [], 0)], argument) 68 elif isinstance(argument, SparsePauliOp): 69 if not isinstance(argument.num_qubits, int): 70 raise TypeError('Cannot read number of qubits from provided SparsePauliOp') 71 self.size = argument.num_qubits 72 self._hamiltonian = argument 73 elif isinstance(argument, list) and len(args) > 0 and isinstance(args[0], int): 74 self.size = args[0] 75 self._hamiltonian = SparsePauliOp.from_sparse_list(argument, args[0]) 76 else: 77 raise TypeError('Wrong arguments!') 78
[docs] 79 def get_variable(self, index: int) -> 'Variable': 80 return Variable(index, self.size)
81 82 @property 83 def hamiltonian(self) -> SparsePauliOp: 84 return self._hamiltonian.simplify() 85 86 @hamiltonian.setter 87 def hamiltonian(self, new_hamiltonian: SparsePauliOp) -> None: 88 self._hamiltonian = new_hamiltonian 89
[docs] 90 def to_sparse_pauli_op(self) -> SparsePauliOp: 91 return self.hamiltonian
92
[docs] 93 def get_order(self) -> int: 94 equation_order = 0 95 for Z_term in self.hamiltonian.paulis: 96 equation_order = max(equation_order, str(Z_term).count('Z')) 97 return equation_order
98
[docs] 99 def is_quadratic(self) -> bool: 100 return all(term.z.sum() <= 2 for term in self.hamiltonian.paulis)
101 102 def __or__(self, other: 'Variable | Equation', /) -> 'Equation': 103 if isinstance(other, Variable): 104 other = other.to_equation() 105 106 return Equation(self.hamiltonian + other.hamiltonian - self.hamiltonian.compose(other.hamiltonian)) 107 108 def __and__(self, other: 'Variable | Equation', /) -> 'Equation': 109 if isinstance(other, Variable): 110 other = other.to_equation() 111 112 return self * other 113 114 def __xor__(self, other: 'Variable | Equation', /) -> 'Equation': 115 if isinstance(other, Variable): 116 other = other.to_equation() 117 118 return Equation(self.hamiltonian + other.hamiltonian - (2 * self.hamiltonian.compose(other.hamiltonian))) 119 120 def __invert__(self) -> 'Equation': 121 I_term = ('I', [], 1) 122 identity = SparsePauliOp.from_sparse_list([I_term], self.size) 123 return Equation(identity - self.hamiltonian) 124 125 def __getitem__(self, variable_number: int): 126 return self.get_variable(variable_number) 127 128 def __eq__(self, other: object) -> bool: 129 if not isinstance(other, (Variable, Equation)): 130 raise TypeError(f'Cannot compare hampy.Equation to {type(other)}') 131 if isinstance(other, Variable): 132 other = other.to_equation() 133 134 return self.hamiltonian == other.hamiltonian 135 136 def __add__(self, other: 'Variable | Equation | SparsePauliOp') -> 'Equation': 137 if isinstance(other, SparsePauliOp): 138 other = Equation(other) 139 elif isinstance(other, Variable): 140 other = other.to_equation() 141 142 return Equation(self.hamiltonian + other.hamiltonian) 143 144 def __radd__(self, other: 'Equation') -> 'Equation': 145 if isinstance(other, Variable): 146 other = other.to_equation() 147 148 return Equation(self.hamiltonian + other.hamiltonian) 149 150 def __mul__(self, other: 'Equation | float') -> 'Equation': 151 if isinstance(other, Variable): 152 other = other.to_equation() 153 if isinstance(other, (float, int)): 154 return Equation(float(other) * self.hamiltonian) 155 return Equation(self.hamiltonian.compose(other.hamiltonian)) 156 157 def __rmul__(self, other: 'Equation | float') -> 'Equation': 158 if isinstance(other, Variable): 159 other = other.to_equation() 160 if isinstance(other, (float, int)): 161 return Equation(float(other) * self.hamiltonian) 162 return Equation(self.hamiltonian.compose(other.hamiltonian)) 163 164 def __truediv__(self, other: float) -> 'Equation': 165 return Equation(self.hamiltonian / other)
166 167
[docs] 168class Variable: 169 def __init__(self, index: int, size: int): 170 self.index = index 171 self.size = size 172 173 def __xor__(self, other: 'Equation | Variable', /) -> Equation: 174 if isinstance(other, Equation): 175 return self.to_equation() ^ other 176 177 I_term = ('I', [], 0.5) 178 Z_term = ('ZZ', [self.index, other.index], -0.5) 179 return Equation(SparsePauliOp.from_sparse_list([I_term, Z_term], self.size)) 180 181 def __or__(self, other: 'Variable | Equation', /) -> Equation: 182 if isinstance(other, Equation): 183 return self.to_equation() | other 184 185 I_term = ('I', [], 0.75) 186 Z1_term = ('Z', [self.index], -0.25) 187 Z2_term = ('Z', [other.index], -0.25) 188 ZZ_term = ('ZZ', [self.index, other.index], -0.25) 189 return Equation([I_term, Z1_term, Z2_term, ZZ_term], self.size) 190 191 def __and__(self, other: 'Variable | Equation', /) -> Equation: 192 if isinstance(other, Equation): 193 return self.to_equation() & other 194 195 I_term = ('I', [], 0.25) 196 Z1_term = ('Z', [self.index], -0.25) 197 Z2_term = ('Z', [other.index], -0.25) 198 ZZ_term = ('ZZ', [self.index, other.index], 0.25) 199 return Equation([I_term, Z1_term, Z2_term, ZZ_term], self.size) 200 201 def __invert__(self) -> Equation: 202 I_term = ('I', [], 0.5) 203 Z_term = ('Z', [self.index], 0.5) 204 return Equation([I_term, Z_term], self.size) 205
[docs] 206 def to_equation(self) -> Equation: 207 I_term = ('I', [], 0.5) 208 Z_term = ('Z', [self.index], -0.5) 209 return Equation([I_term, Z_term], self.size)