Source code for qlauncher.problems.problem_formulations.hamiltonian

  1""" Hamiltonian formulation of problems """
  2from itertools import product
  3import numpy as np
  4
  5from qiskit import QuantumCircuit
  6from qiskit.quantum_info import SparsePauliOp
  7from qiskit_optimization.converters import QuadraticProgramToQubo
  8from qiskit_optimization.translators import from_ising
  9
 10from qlauncher.base import formatter
 11from qlauncher.base.adapter_structure import adapter
 12import qlauncher.problems.problem_initialization as problems
 13import qlauncher.hampy as hampy
 14from qlauncher.hampy import Equation, Variable
 15from qlauncher.problems.problem_formulations.hamiltonians.tsp import problem_to_hamiltonian as tsp_to_hamiltonian
 16
 17
[docs] 18@adapter('hamiltonian', 'qubo', onehot='quadratic') 19def hamiltonian_to_qubo(hamiltonian): 20 qp = from_ising(hamiltonian) 21 conv = QuadraticProgramToQubo() 22 qubo = conv.convert(qp).objective 23 return qubo.quadratic.to_array(), 0
24 25
[docs] 26@adapter("qubo", "hamiltonian") 27def qubo_to_hamiltonian(qubo: np.ndarray) -> SparsePauliOp: 28 q_matrix, offset = qubo 29 num_vars = q_matrix.shape[0] 30 pauli = 0 31 for i, col in enumerate(q_matrix): 32 for j, entry in enumerate(col): 33 if entry == 0: 34 continue 35 if i == j: 36 pauli += SparsePauliOp.from_sparse_list([('I', [0], .5), ('Z', [i], -.5)], num_vars)*entry 37 else: 38 pauli += SparsePauliOp.from_sparse_list([('I', [0], .25), ('Z', [i], -.25), 39 ('Z', [j], -.25), ('ZZ', [i, j], .25)], num_vars)*entry 40 pauli += SparsePauliOp.from_sparse_list([('I', [], offset)], num_vars) 41 return pauli
42 43
[docs] 44def ring_ham(ring: set, n): 45 total = None 46 ring = list(ring) 47 for index in range(len(ring) - 1): 48 sparse_list = [] 49 sparse_list.append((("XX", [ring[index], ring[index + 1]], 1))) 50 sparse_list.append((("YY", [ring[index], ring[index + 1]], 1))) 51 sp = SparsePauliOp.from_sparse_list(sparse_list, n) 52 if total is None: 53 total = sp 54 else: 55 total += sp 56 sparse_list = [] 57 sparse_list.append((("XX", [ring[-1], ring[0]], 1))) 58 sparse_list.append((("YY", [ring[-1], ring[0]], 1))) 59 sp = SparsePauliOp.from_sparse_list(sparse_list, n) 60 total += sp 61 return SparsePauliOp(total)
62 63 64@formatter(problems.EC, 'hamiltonian') 65class ECQiskit: 66 def __call__(self, problem: problems.EC, onehot='exact') -> SparsePauliOp: 67 """ generating hamiltonian""" 68 elements = set().union(*problem.instance) 69 onehots = [] 70 for ele in elements: 71 ohs = set() 72 for i, subset in enumerate(problem.instance): 73 if ele in subset: 74 ohs.add(i) 75 onehots.append(ohs) 76 hamiltonian = None 77 for ohs in onehots: 78 if onehot == 'exact': 79 part = (~hampy.one_in_n(list(ohs), len(problem.instance))).hamiltonian 80 elif onehot == 'quadratic': 81 part = hampy.one_in_n(list(ohs), len(problem.instance), quadratic=True).hamiltonian 82 83 if hamiltonian is None: 84 hamiltonian = part 85 else: 86 hamiltonian += part 87 return hamiltonian.simplify() 88 89 def get_mixer_hamiltonian(self, problem: problems.EC, amount_of_rings=None): 90 """ generates mixer hamiltonian """ 91 def get_main_set(): 92 main_set = [] 93 for element_set in problem.instance: 94 for elem in element_set: 95 if elem not in main_set: 96 main_set.append(elem) 97 return main_set 98 99 def get_constraints(): 100 constraints, main_set = [], get_main_set() 101 for element in main_set: 102 element_set = set() 103 for index, _ in enumerate(problem.instance): 104 if element in problem.instance[index]: 105 element_set.add(index) 106 if len(element_set) > 0 and element_set not in constraints: 107 constraints.append(element_set) 108 109 return constraints 110 111 # creating mixer hamiltonians for all qubits that aren't in rings (in other words applying X gate to them) 112 def x_gate_ham(x_gate: list): 113 total = None 114 for elem in x_gate: 115 sparse_list = [] 116 sparse_list.append((("X", [elem], 1))) 117 sp = SparsePauliOp.from_sparse_list( 118 sparse_list, len(problem.instance)) 119 if total is None: 120 total = sp 121 else: 122 total += sp 123 return SparsePauliOp(total) 124 125 # looking for all rings in a data and creating a list with them 126 ring, x_gate, constraints = [], [], get_constraints() 127 128 ring.append(max(constraints, key=len)) 129 130 ring_qubits = set.union(*ring) 131 132 for set_ in constraints: 133 if len(set_.intersection(ring_qubits)) == 0: 134 ring.append(set_) 135 ring_qubits.update(set_) 136 137 if amount_of_rings is not None: 138 max_amount_of_rings, user_rings = len(ring), [] 139 if amount_of_rings > max_amount_of_rings: 140 raise ValueError( 141 f"Too many rings. Maximum amount is {max_amount_of_rings}") 142 elif amount_of_rings == 0: 143 ring_qubits = [] 144 else: 145 current_qubits = ring[0] 146 for index in range(amount_of_rings): 147 user_rings.append(ring[index]) 148 current_qubits = current_qubits.union(ring[index]) 149 ring_qubits = current_qubits 150 x_gate.extend(id for id, _ in enumerate( 151 problem.instance) if id not in ring_qubits) 152 153 # connecting all parts of mixer hamiltonian together 154 mix_ham = None 155 for set_ in ring: 156 if mix_ham is None: 157 mix_ham = ring_ham(set_, len(problem.instance)) 158 else: 159 mix_ham += ring_ham(set_, len(problem.instance)) 160 161 if mix_ham is None: 162 mix_ham = x_gate_ham(x_gate) 163 else: 164 mix_ham += x_gate_ham(x_gate) 165 166 return mix_ham 167 168 169@formatter(problems.JSSP, 'hamiltonian') 170def get_qiskit_hamiltonian(problem: problems.JSSP) -> SparsePauliOp: 171 if problem.optimization_problem: 172 return problem.h_o 173 else: 174 return problem.h_d 175 176 177@formatter(problems.MaxCut, 'hamiltonian') 178def get_qiskit_hamiltonian(problem: problems.MaxCut): 179 ham = None 180 n = problem.instance.number_of_nodes() 181 for edge in problem.instance.edges(): 182 if ham is None: 183 ham = ~hampy.one_in_n(edge, n) 184 else: 185 ham += ~hampy.one_in_n(edge, n) 186 return ham.hamiltonian.simplify() 187 188 189@formatter(problems.QATM, 'hamiltonian') 190class QATMQiskit: 191 def __call__(self, problem: problems.QATM, onehot='exact') -> SparsePauliOp: 192 cm = problem.instance['cm'] 193 aircrafts = problem.instance['aircrafts'] 194 195 onehot_hamiltonian = None 196 for plane, manouvers in aircrafts.groupby(by='aircraft'): 197 if onehot == 'exact': 198 h = (~hampy.one_in_n(manouvers.index.values.tolist(), len(cm))).hamiltonian 199 elif onehot == 'quadratic': 200 h = hampy.one_in_n(manouvers.index.values.tolist(), len(cm), quadratic=True).hamiltonian 201 elif onehot == 'xor': 202 total = None 203 eq = Equation(len(cm)) 204 for part in manouvers.index.values.tolist(): 205 if total is None: 206 total = eq[part].to_equation() 207 continue 208 total ^= eq[part] 209 h = (~total).hamiltonian 210 if onehot_hamiltonian is not None: 211 onehot_hamiltonian += h 212 else: 213 onehot_hamiltonian = h 214 215 triu = np.triu(cm, k=1) 216 conflict_hamiltonian = None 217 for p1, p2 in zip(*np.where(triu == 1)): 218 eq = Equation(len(cm)) 219 partial_hamiltonian = (eq[int(p1)] & eq[int(p2)]).hamiltonian 220 if conflict_hamiltonian is not None: 221 conflict_hamiltonian += partial_hamiltonian 222 else: 223 conflict_hamiltonian = partial_hamiltonian 224 225 hamiltonian = onehot_hamiltonian + conflict_hamiltonian 226 227 if problem.optimization_problem: 228 goal_hamiltonian = None 229 for i, (maneuver, ac) in problem.instance['aircrafts'].iterrows(): 230 if maneuver != ac: 231 eq = Equation(len(aircrafts)) 232 h = Variable(i, eq).to_equation() 233 if goal_hamiltonian is None: 234 goal_hamiltonian = h 235 else: 236 goal_hamiltonian += h 237 goal_hamiltonian /= sum(sum(cm)) 238 hamiltonian += goal_hamiltonian 239 240 return hamiltonian.simplify() 241 242 def get_mixer_hamiltonian(self, problem: problems.QATM) -> SparsePauliOp: 243 cm = problem.instance['cm'] 244 aircrafts = problem.instance['aircrafts'] 245 246 mixer_hamiltonian = None 247 for plane, manouvers in aircrafts.groupby(by='aircraft'): 248 h = ring_ham(manouvers.index.values.tolist(), len(cm)) 249 if mixer_hamiltonian is None: 250 mixer_hamiltonian = h 251 else: 252 mixer_hamiltonian += h 253 return mixer_hamiltonian 254 255 def get_QAOAAnsatz_initial_state(self, problem: problems.QATM) -> QuantumCircuit: 256 aircrafts = problem.instance['aircrafts'] 257 qc = QuantumCircuit(len(aircrafts)) 258 for plane, manouvers in aircrafts.groupby(by='aircraft'): 259 qc.x(manouvers.index.values.tolist()[0]) 260 return qc 261 262 263@formatter(problems.Raw, 'hamiltonian') 264def get_qiskit_hamiltonian(problem: problems.Raw) -> SparsePauliOp: 265 return problem.instance 266 267 268@formatter(problems.TSP, 'hamiltonian') 269def get_qiskit_hamiltonian(problem: problems.TSP, onehot='exact', constraints_weight=1, costs_weight=1) -> SparsePauliOp: 270 return tsp_to_hamiltonian( 271 problem, 272 onehot=onehot, 273 constraints_weight=constraints_weight, 274 costs_weight=costs_weight 275 ) 276 277
[docs] 278@formatter(problems.GraphColoring, 'hamiltonian') 279def get_qiskit_hamiltonian(problem: problems.GraphColoring, constraints_weight=1, costs_weight=1): 280 color_bit_length = int(np.ceil(np.log2(problem.num_colors))) 281 num_qubits = problem.instance.number_of_nodes() * color_bit_length 282 eq = Equation(num_qubits) 283 # Penalty for assigning the same colors to neighboring vertices 284 for node1, node2 in problem.instance.edges: 285 for ind, comb in enumerate(product(range(2), repeat=color_bit_length)): 286 if ind >= problem.num_colors: 287 break 288 eq2 = None 289 for i in range(color_bit_length): 290 qubit1 = eq[node1 * color_bit_length + i] 291 qubit2 = eq[node2 * color_bit_length + i] 292 if comb[i]: 293 exp = qubit1 & qubit2 294 else: 295 exp = ~qubit1 & ~qubit2 296 if eq2 is None: 297 eq2 = exp 298 else: 299 eq2 &= exp 300 eq += eq2 301 eq *= costs_weight 302 # Penalty for using excessive colors 303 for node in problem.instance.nodes: 304 for ind, comb in enumerate(product(range(2), repeat=color_bit_length)): 305 if ind < problem.num_colors: 306 continue 307 eq2 = None 308 for i in range(color_bit_length): 309 qubit = eq[node * color_bit_length + i] 310 exp = qubit if comb[i] else ~qubit 311 if eq2 is None: 312 eq2 = exp 313 else: 314 eq2 &= exp 315 eq += eq2 316 eq *= constraints_weight 317 return eq.hamiltonian