1from typing import Union, overload
2from qiskit.quantum_info import SparsePauliOp
3
4
[docs]
5class Equation:
6 @overload
7 def __init__(self, size: int): ...
8 @overload
9 def __init__(self, hamiltonian: SparsePauliOp): ...
10 @overload
11 def __init__(self, sparse_list: list[tuple], size: int): ...
12
13 def __init__(self, argument, *args):
14 if isinstance(argument, int):
15 self.size = argument
16 self._hamiltonian = SparsePauliOp.from_sparse_list([('I', [], 0)], argument)
17 elif isinstance(argument, SparsePauliOp):
18 self.size = argument.num_qubits
19 self._hamiltonian = argument
20 elif isinstance(argument, list) and len(args) > 0 and isinstance(args[0], int):
21 self.size = args[0]
22 self._hamiltonian = SparsePauliOp.from_sparse_list(argument, args[0])
23 else:
24 raise TypeError('Wrong arguments!')
25
[docs]
26 def get_variable(self, index: int) -> "Variable":
27 assert isinstance(index, int), "Index needs to be an integer"
28 obj = Variable(index, self)
29 return obj
30
31 @property
32 def hamiltonian(self) -> SparsePauliOp:
33 return self._hamiltonian.simplify()
34
35 @hamiltonian.setter
36 def hamiltonian(self, new_hamiltonian: SparsePauliOp):
37 self._hamiltonian = new_hamiltonian
38
[docs]
39 def to_sparse_pauli_op(self) -> SparsePauliOp:
40 return self.hamiltonian
41
[docs]
42 def get_order(self) -> int:
43 equation_order = 0
44 for Z_term in self.hamiltonian.paulis:
45 equation_order = max(equation_order, str(Z_term).count('Z'))
46 return equation_order
47
[docs]
48 def is_quadratic(self) -> bool:
49 return all(term.z.sum() <= 2 for term in self.hamiltonian.paulis)
50
51 def __or__(self, other: Union["Variable", "Equation"], /) -> "Equation":
52 if isinstance(other, Variable):
53 other = other.to_equation()
54
55 return Equation(self.hamiltonian + other.hamiltonian - self.hamiltonian.compose(other.hamiltonian))
56
57 def __and__(self, other: Union["Variable", "Equation"], /) -> "Equation":
58 if isinstance(other, Variable):
59 other = other.to_equation()
60
61 return Equation(self.hamiltonian.compose(other.hamiltonian))
62
63 def __xor__(self, other: Union["Variable", "Equation"], /) -> "Equation":
64 if isinstance(other, Variable):
65 other = other.to_equation()
66
67 return Equation(self.hamiltonian + other.hamiltonian - (2 * self.hamiltonian.compose(other.hamiltonian)))
68
69 def __invert__(self) -> "Equation":
70 I = ('I', [], 1)
71 identity = SparsePauliOp.from_sparse_list([I], self.size)
72 return Equation(identity - self.hamiltonian)
73
74 def __getitem__(self, variable_number: int):
75 return self.get_variable(variable_number)
76
77 def __eq__(self, other: "Equation") -> bool:
78 if isinstance(other, Variable):
79 other = other.to_equation()
80
81 return self.hamiltonian == other.hamiltonian
82
83 def __add__(self, other: Union["Equation", "Variable"]) -> "Equation":
84 if isinstance(other, Variable):
85 other = other.to_equation()
86
87 return Equation(self.hamiltonian + other.hamiltonian)
88
89 def __radd__(self, other: "Equation") -> "Equation":
90 if isinstance(other, Variable):
91 other = other.to_equation()
92
93 return Equation(self.hamiltonian + other.hamiltonian)
94
95 def __mul__(self, other: Union["Equation", float]) -> "Equation":
96 if isinstance(other, Variable):
97 other = other.to_equation()
98 if isinstance(other, float) or isinstance(other, int):
99 return Equation(float(other) * self.hamiltonian)
100 return Equation(self.hamiltonian.compose(other.hamiltonian))
101
102 def __rmul__(self, other: Union["Equation", float]) -> "Equation":
103 if isinstance(other, Variable):
104 other = other.to_equation()
105 if isinstance(other, float) or isinstance(other, int):
106 return Equation(float(other) * self.hamiltonian)
107 return Equation(self.hamiltonian.compose(other.hamiltonian))
108
109
[docs]
110class Variable:
111 def __init__(self, index: int, parent: Equation):
112 self.index = index
113 self.size = parent.size
114
115 def __xor__(self, other: Union["Variable", Equation], /) -> Equation:
116 if isinstance(other, Equation):
117 return self.to_equation() ^ other
118
119 I = ('I', [], 0.5)
120 Z_term = ('ZZ', [self.index, other.index], -0.5)
121 eq = Equation(SparsePauliOp.from_sparse_list([I, Z_term], self.size))
122 return eq
123
124 def __or__(self, other: Union["Variable", Equation], /) -> Equation:
125 if isinstance(other, Equation):
126 return self.to_equation() | other
127
128 I_term = ('I', [], 0.75)
129 Z1_term = ('Z', [self.index], -0.25)
130 Z2_term = ('Z', [other.index], -0.25)
131 ZZ_term = ('ZZ', [self.index, other.index], -0.25)
132 eq = Equation([I_term, Z1_term, Z2_term, ZZ_term], self.size)
133 return eq
134
135 def __and__(self, other: Union["Variable", Equation], /) -> Equation:
136
137 if isinstance(other, Equation):
138 return self.to_equation() & other
139
140 I_term = ('I', [], 0.25)
141 Z1_term = ('Z', [self.index], -0.25)
142 Z2_term = ('Z', [other.index], -0.25)
143 ZZ_term = ('ZZ', [self.index, other.index], 0.25)
144 eq = Equation([I_term, Z1_term, Z2_term, ZZ_term], self.size)
145 return eq
146
147 def __invert__(self) -> Equation:
148 I_term = ('I', [], 0.5)
149 Z_term = ('Z', [self.index], 0.5)
150 return Equation([I_term, Z_term], self.size)
151
[docs]
152 def to_equation(self) -> Equation:
153 I_term = ('I', [], 0.5)
154 Z_term = ('Z', [self.index], -0.5)
155 return Equation([I_term, Z_term], self.size)