def __init__(self, layers, domain, ansatz, function): self.function = function super().__init__(layers, domain, ansatz) self.target = self.function(self.domain) self.target = 2 * (self.target - np.min(self.target)) / ( np.max(self.target) - np.min(self.target)) - 1 self.H = Hamiltonian(1, matrices._Z) self.classical = globals()[f"classical_real_{self.ansatz}"]
def __init__(self, layers, domain, function, ansatz): self.function = function super().__init__(layers, domain, ansatz) self.target = np.array(list(self.function(self.domain))) self.target = 2 * (self.target - np.min(self.target)) / ( np.max(self.target) - np.min(self.target)) - 1 self.H = Hamiltonian(1, matrices._Z) self.classical = classical_real_Weighted_2D
def test_trotter_hamiltonian_initialization_errors(): """Test errors in initialization of ``TrotterHamiltonian``.""" # Wrong type of terms with pytest.raises(TypeError): ham = TrotterHamiltonian({(0, 1): "abc"}) # Wrong type of parts with pytest.raises(TypeError): ham = TrotterHamiltonian([(0, 1)]) # Wrong number of target qubits with pytest.raises(ValueError): ham = TrotterHamiltonian({(0, 1): TFIM(nqubits=3, numpy=True)}) # Same targets multiple times h = TFIM(nqubits=2, numpy=True) with pytest.raises(ValueError): ham = TrotterHamiltonian({(0, 1): h}, {(0, 1): h}) # Different term matrix types h2 = Hamiltonian(2, np.eye(4, dtype=np.float32), numpy=True) with pytest.raises(TypeError): ham = TrotterHamiltonian({(0, 1): h, (1, 2): h2}) # ``from_twoqubit_term`` initialization with nqubits < 0 with pytest.raises(ValueError): ham = TrotterHamiltonian.from_twoqubit_term(-2, h) # ``from_twoqubit_term`` initialization with more than 2 targets h = TFIM(nqubits=3, numpy=True) with pytest.raises(ValueError): ham = TrotterHamiltonian.from_twoqubit_term(4, h)
def qpdf_hamiltonian(nqubits, z_qubit=0): """Precomputes Hamiltonian. Args: nqubits (int): number of qubits. z_qubit (int): qubit where the Z measurement is applied, must be z_qubit < nqubits Returns: An Hamiltonian object. """ eye = matrices.I if z_qubit == 0: h = matrices.Z for _ in range(nqubits - 1): h = np.kron(eye, h) elif z_qubit == nqubits - 1: h = eye for _ in range(nqubits - 2): h = np.kron(eye, h) h = np.kron(matrices.Z, h) else: h = eye for _ in range(nqubits - 1): if _ + 1 == z_qubit: h = np.kron(matrices.Z, h) else: h = np.kron(eye, h) return Hamiltonian(nqubits, h)
def from_symbolic(cls, symbolic_hamiltonian, symbol_map, ground_state=None): """Creates a ``TrotterHamiltonian`` from a symbolic Hamiltonian. We refer to the :ref:`How to define custom Hamiltonians using symbols? <symbolicham-example>` example for more details. Args: symbolic_hamiltonian (sympy.Expr): The full Hamiltonian written with symbols. symbol_map (dict): Dictionary that maps each symbol that appears in the Hamiltonian to a pair of (target, matrix). ground_state (Callable): Optional callable with no arguments that returns the ground state of this ``TrotterHamiltonian``. See :class:`qibo.base.hamiltonians.TrotterHamiltonian` for more details. Returns: A :class:`qibo.base.hamiltonians.TrotterHamiltonian` object that implements the given symbolic Hamiltonian. """ from qibo.hamiltonians import Hamiltonian terms, constant = _SymbolicHamiltonian( symbolic_hamiltonian, symbol_map).trotter_terms() terms = {k: Hamiltonian(len(k), v, numpy=True) for k, v in terms.items()} return cls.from_dictionary(terms, ground_state=ground_state) + constant
def construct_terms(terms): """Helper method for `from_symbolic`. Constructs the term dictionary by using the same :class:`qibo.abstractions.hamiltonians.Hamiltonian` object for terms that have equal matrix representation. This is done for efficiency during the exponentiation of terms. Args: terms (dict): Dictionary that maps tuples of targets to the matrix that acts on these on targets. Returns: terms (dict): Dictionary that maps tuples of targets to the Hamiltonian term that acts on these on targets. """ from qibo.hamiltonians import Hamiltonian unique_matrices = [] hterms = {} for targets, matrix in terms.items(): flag = True for m, h in unique_matrices: if K.np.array_equal(matrix, m): ham = h flag = False break if flag: ham = Hamiltonian(len(targets), matrix, numpy=True) unique_matrices.append((matrix, ham)) hterms[targets] = ham return hterms
def test_from_symbolic_application_hamiltonian(): """Check ``from_symbolic`` for a specific four-qubit Hamiltonian.""" import sympy z1, z2, z3, z4 = sympy.symbols("z1 z2 z3 z4") symmap = {z: (i, matrices.Z) for i, z in enumerate([z1, z2, z3, z4])} symham = (z1 * z2 - 0.5 * z1 * z3 + 2 * z2 * z3 + 0.35 * z2 + 0.25 * z3 * z4 + 0.5 * z3 + z4 - z1) # Check that Trotter dense matrix agrees will full Hamiltonian matrix fham = Hamiltonian.from_symbolic(symham, symmap) tham = TrotterHamiltonian.from_symbolic(symham, symmap) np.testing.assert_allclose(tham.dense.matrix, fham.matrix) # Check that no one-qubit terms exist in the Trotter Hamiltonian # (this means that merging was successful) first_targets = set() for part in tham.parts: for targets, term in part.items(): first_targets.add(targets[0]) assert len(targets) == 2 assert term.nqubits == 2 assert first_targets == set(range(4)) # Check making an ``X`` Hamiltonian compatible with ``tham`` xham = X(nqubits=4, trotter=True) cxham = tham.make_compatible(xham) assert not tham.is_compatible(xham) assert tham.is_compatible(cxham) np.testing.assert_allclose(xham.dense.matrix, cxham.dense.matrix)
def __init__(self, layers, domain, ansatz, real, imag): self.f_real = real self.f_imag = imag # self.function.name = lambda:self.real.name + '_' + self.imag.name super().__init__(layers, domain, ansatz) self.real = self.f_real(self.domain) self.real = 2 * (self.real - np.min(self.real)) / ( np.max(self.real) - np.min(self.real)) - 1 self.imag = self.f_imag(self.domain) self.imag = 2 * (self.imag - np.min(self.imag)) / ( np.max(self.imag) - np.min(self.imag)) - 1 self.target = self.real + 1j * self.imag self.target /= np.max(np.abs(self.target)) self.H = [Hamiltonian(1, matrices._X), Hamiltonian(1, matrices._Y)] self.classical = globals()[f"classical_complex_{self.ansatz}"]
def test_trotter_hamiltonian_make_compatible_onequbit_terms(): """Check ``make_compatible`` when the two-qubit Hamiltonian has one-qubit terms.""" term1 = Hamiltonian(1, matrices.Z, numpy=True) term2 = Hamiltonian(2, np.kron(matrices.Z, matrices.Z), numpy=True) terms = { (0, 1): term2, (0, 2): -0.5 * term2, (1, 2): 2 * term2, (1, ): 0.35 * term1, (2, 3): 0.25 * term2, (2, ): 0.5 * term1, (3, ): term1 } tham = TrotterHamiltonian.from_dictionary(terms) + 1.5 xham = X(nqubits=4, trotter=True) cxham = tham.make_compatible(xham) assert not tham.is_compatible(xham) assert tham.is_compatible(cxham) np.testing.assert_allclose(xham.dense.matrix, cxham.dense.matrix)
def test_trotter_hamiltonian_three_qubit_term(backend): """Test creating ``TrotterHamiltonian`` with three qubit term.""" import qibo from scipy.linalg import expm original_backend = qibo.get_backend() qibo.set_backend(backend) m1 = utils.random_numpy_hermitian(3) m2 = utils.random_numpy_hermitian(2) m3 = utils.random_numpy_hermitian(1) term1 = Hamiltonian(3, m1, numpy=True) term2 = Hamiltonian(2, m2, numpy=True) term3 = Hamiltonian(1, m3, numpy=True) parts = [{(0, 1, 2): term1}, {(2, 3): term2, (1, ): term3}] trotter_h = TrotterHamiltonian(*parts) # Test that the `TrotterHamiltonian` dense matrix is correct eye = np.eye(2, dtype=m1.dtype) mm1 = np.kron(m1, eye) mm2 = np.kron(np.kron(eye, eye), m2) mm3 = np.kron(np.kron(eye, m3), np.kron(eye, eye)) target_h = Hamiltonian(4, mm1 + mm2 + mm3) np.testing.assert_allclose(trotter_h.dense.matrix, target_h.matrix) dt = 1e-2 initial_state = utils.random_numpy_state(4) if backend == "custom": with pytest.raises(NotImplementedError): circuit = trotter_h.circuit(dt=dt) else: circuit = trotter_h.circuit(dt=dt) final_state = circuit(np.copy(initial_state)) u = [expm(-0.5j * dt * m) for m in [mm1, mm2, mm3]] target_state = u[2].dot(u[1].dot(u[0])).dot(initial_state) target_state = u[0].dot(u[1].dot(u[2])).dot(target_state) np.testing.assert_allclose(final_state, target_state) qibo.set_backend(original_backend)
def test_hamiltonian_initialization(): """Testing hamiltonian initialization errors.""" import tensorflow as tf dtype = K.dtypes('DTYPECPX') with pytest.raises(TypeError): H = Hamiltonian(2, "test") H1 = Hamiltonian(2, np.eye(4)) H1 = Hamiltonian(2, np.eye(4), numpy=True) H1 = Hamiltonian(2, tf.eye(4, dtype=dtype)) H1 = Hamiltonian(2, tf.eye(4, dtype=dtype), numpy=True) with pytest.raises(ValueError): H1 = Hamiltonian(-2, np.eye(4)) with pytest.raises(RuntimeError): H2 = Hamiltonian(np.eye(2), np.eye(4)) with pytest.raises(ValueError): H3 = Hamiltonian(4, np.eye(10))
def test_x_hamiltonian_from_symbols(nqubits, trotter): """Check creating sum(X) Hamiltonian using sympy.""" import sympy x_symbols = sympy.symbols(" ".join((f"X{i}" for i in range(nqubits)))) symham = -sum(x_symbols) symmap = {x: (i, matrices.X) for i, x in enumerate(x_symbols)} target_matrix = X(nqubits).matrix if trotter: trotter_ham = TrotterHamiltonian.from_symbolic(symham, symmap) final_matrix = trotter_ham.dense.matrix else: full_ham = Hamiltonian.from_symbolic(symham, symmap) final_matrix = full_ham.matrix np.testing.assert_allclose(final_matrix, target_matrix)
def test_tfim_hamiltonian_from_symbols(nqubits, trotter): """Check creating TFIM Hamiltonian using sympy.""" import sympy h = 0.5 z_symbols = sympy.symbols(" ".join((f"Z{i}" for i in range(nqubits)))) x_symbols = sympy.symbols(" ".join((f"X{i}" for i in range(nqubits)))) symham = sum(z_symbols[i] * z_symbols[i + 1] for i in range(nqubits - 1)) symham += z_symbols[0] * z_symbols[-1] symham += h * sum(x_symbols) symmap = {z: (i, matrices.Z) for i, z in enumerate(z_symbols)} symmap.update({x: (i, matrices.X) for i, x in enumerate(x_symbols)}) target_matrix = TFIM(nqubits, h=h).matrix if trotter: trotter_ham = TrotterHamiltonian.from_symbolic(-symham, symmap) final_matrix = trotter_ham.dense.matrix else: full_ham = Hamiltonian.from_symbolic(-symham, symmap) final_matrix = full_ham.matrix np.testing.assert_allclose(final_matrix, target_matrix)
def test_three_qubit_term_hamiltonian_from_symbols(trotter): """Check creating Hamiltonian with three-qubit interaction using sympy.""" import sympy from qibo import matrices x_symbols = sympy.symbols(" ".join((f"X{i}" for i in range(4)))) y_symbols = sympy.symbols(" ".join((f"Y{i}" for i in range(4)))) z_symbols = sympy.symbols(" ".join((f"Z{i}" for i in range(4)))) symmap = {x: (i, matrices.X) for i, x in enumerate(x_symbols)} symmap.update({x: (i, matrices.Y) for i, x in enumerate(y_symbols)}) symmap.update({x: (i, matrices.Z) for i, x in enumerate(z_symbols)}) symham = x_symbols[0] * y_symbols[1] * z_symbols[2] symham += 0.5 * y_symbols[0] * z_symbols[1] * x_symbols[3] symham += z_symbols[0] * x_symbols[2] symham += -3 * x_symbols[1] * y_symbols[3] symham += y_symbols[2] symham += 1.5 * z_symbols[1] symham -= 2 target_matrix = np.kron(np.kron(matrices.X, matrices.Y), np.kron(matrices.Z, matrices.I)) target_matrix += 0.5 * np.kron(np.kron(matrices.Y, matrices.Z), np.kron(matrices.I, matrices.X)) target_matrix += np.kron(np.kron(matrices.Z, matrices.I), np.kron(matrices.X, matrices.I)) target_matrix += -3 * np.kron(np.kron(matrices.I, matrices.X), np.kron(matrices.I, matrices.Y)) target_matrix += np.kron(np.kron(matrices.I, matrices.I), np.kron(matrices.Y, matrices.I)) target_matrix += 1.5 * np.kron(np.kron(matrices.I, matrices.Z), np.kron(matrices.I, matrices.I)) target_matrix -= 2 * np.eye(2**4, dtype=target_matrix.dtype) if trotter: trotter_ham = TrotterHamiltonian.from_symbolic(symham, symmap) final_matrix = trotter_ham.dense.matrix else: full_ham = Hamiltonian.from_symbolic(symham, symmap) final_matrix = full_ham.matrix np.testing.assert_allclose(final_matrix, target_matrix)
def test_from_symbolic_with_power(trotter): """Check ``from_symbolic`` when the expression contains powers.""" import sympy z = sympy.symbols(" ".join((f"Z{i}" for i in range(3)))) symham = z[0]**2 - z[1]**2 + 3 * z[1] - 2 * z[0] * z[2] + +1 matrix = utils.random_numpy_hermitian(1) symmap = {x: (i, matrix) for i, x in enumerate(z)} if trotter: ham = TrotterHamiltonian.from_symbolic(symham, symmap) final_matrix = ham.dense.matrix else: ham = Hamiltonian.from_symbolic(symham, symmap) final_matrix = ham.matrix matrix2 = matrix.dot(matrix) eye = np.eye(2, dtype=matrix.dtype) target_matrix = np.kron(np.kron(matrix2, eye), eye) target_matrix -= np.kron(np.kron(eye, matrix2), eye) target_matrix += 3 * np.kron(np.kron(eye, matrix), eye) target_matrix -= 2 * np.kron(np.kron(matrix, eye), matrix) target_matrix += np.eye(8, dtype=matrix.dtype) np.testing.assert_allclose(final_matrix, target_matrix)
def expectation(self, state, normalize=False): return Hamiltonian.expectation(self, state, normalize)
class Approximant_real_2D(Approximant): def __init__(self, layers, domain, function, ansatz): self.function = function super().__init__(layers, domain, ansatz) self.target = np.array(list(self.function(self.domain))) self.target = 2 * (self.target - np.min(self.target)) / ( np.max(self.target) - np.min(self.target)) - 1 self.H = Hamiltonian(1, matrices._Z) self.classical = classical_real_Weighted_2D def name_folder(self, quantum=True): folder = self.ansatz + '/' + self.function.name + '/%s_layers' % ( self.layers) if quantum: folder = 'quantum/' + folder else: folder = 'classical/' + folder folder = 'results/' + folder import os try: l = os.listdir(folder) l = [int(_) for _ in l] l.sort() trial = int(l[-1]) + 1 except: trial = 0 os.makedirs(folder) fold_name = folder + '/%s' % (trial) os.makedirs(fold_name) return fold_name, trial def cf_one_point(self, x, f): state = self.get_state(x) o = self.H.expectation(state) cf = (o - f)**2 return cf def paint_representation_2D(self, name): fig = plt.figure() axs = fig.gca(projection='3d') axs.plot_trisurf(self.domain[:, 0], self.domain[:, 1], self.target, color='black', label='Target Function', alpha=0.5) outcomes = np.zeros_like(self.target) for j, x in enumerate(self.domain): state = self.get_state(x) outcomes[j] = self.H.expectation(state) axs.scatter(self.domain[:, 0], self.domain[:, 1], outcomes, color='C1', label='Quantum ' + self.ansatz + ' model') fig.savefig(name) plt.close(fig) def paint_representation_2D_classical(self, prediction, name): fig = plt.figure() axs = fig.gca(projection='3d') prediction = np.array(list(prediction)) axs.plot_trisurf(self.domain[:, 0], self.domain[:, 1], self.target, color='black', label='Target Function', alpha=0.5) axs.scatter(self.domain[:, 0], self.domain[:, 1], prediction, color='C0', label='Classical ' + self.ansatz + ' model') fig.savefig(name) #plt.show() plt.close(fig)
class Approximant_real(Approximant): def __init__(self, layers, domain, ansatz, function): self.function = function super().__init__(layers, domain, ansatz) self.target = self.function(self.domain) self.target = 2 * (self.target - np.min(self.target)) / ( np.max(self.target) - np.min(self.target)) - 1 self.H = Hamiltonian(1, matrices._Z) self.classical = globals()[f"classical_real_{self.ansatz}"] def name_folder(self, quantum=True): folder = self.ansatz + '/' + self.function.name + '/%s_layers' % ( self.layers) if quantum: folder = 'quantum/' + folder else: folder = 'classical/' + folder folder = 'results/' + folder import os try: l = os.listdir(folder) l = [int(_) for _ in l] l.sort() trial = int(l[-1]) + 1 except: trial = 0 os.makedirs(folder) fold_name = folder + '/%s' % (trial) os.makedirs(fold_name) return fold_name, trial def cf_one_point(self, x, f): state = self.get_state(x) o = self.H.expectation(state) cf = (o - f)**2 return cf def derivative_cf_one_point(self, x, f): self.theta_with_x(x) derivatives = np.zeros_like(self.params) index = 0 ch_index = 0 for l in range(self.layers - 1): cir_params_ = self.cir_params.copy() delta = np.zeros_like(cir_params_) delta[ch_index] = np.pi / 2 self.C.set_parameters(cir_params_ + delta) state = self.C() z1 = self.H.expectation(state) self.C.set_parameters(cir_params_ - delta) state = self.C() z2 = self.H.expectation(state) derivatives[index + 1] = 0.5 * ((z1**2 - z2**2) - 2 * f * (z1 - z2)) derivatives[index] = x * derivatives[index + 1] index += 2 ch_index += 1 cir_params_ = self.cir_params.copy() delta = np.zeros_like(cir_params_) delta[ch_index] = np.pi / 2 self.C.set_parameters(cir_params_ + delta) state = self.C() z1 = self.H.expectation(state) self.C.set_parameters(cir_params_ - delta) state = self.C() z2 = self.H.expectation(state) derivatives[index] = 0.5 * ((z1**2 - z2**2) - 2 * f * (z1 - z2)) index += 1 ch_index += 1 cir_params_ = self.cir_params.copy() delta = np.zeros_like(cir_params_) delta[ch_index] = np.pi / 2 self.C.set_parameters(cir_params_ + delta) state = self.C() z1 = self.H.expectation(state) self.C.set_parameters(cir_params_ - delta) state = self.C() z2 = self.H.expectation(state) derivatives[index + 1] = 0.5 * ((z1**2 - z2**2) - 2 * f * (z1 - z2)) derivatives[index] = x * derivatives[index + 1] index += 2 ch_index += 1 return derivatives def derivative_cf(self, params): try: params = params.flatten() except: pass self.set_parameters(params) derivatives = np.zeros_like(params) for x, t in zip(self.domain, self.target): derivatives += self.derivative_cf_one_point(x, t) derivatives /= len(self.domain) return derivatives def paint_representation_1D(self, name): fig, axs = plt.subplots() axs.plot(self.domain, self.target, color='black', label='Target Function') outcomes = np.zeros_like(self.domain) for j, x in enumerate(self.domain): state = self.get_state(x) outcomes[j] = self.H.expectation(state) axs.plot(self.domain, outcomes, color='C1', label='Quantum ' + self.ansatz + ' model') axs.legend() fig.savefig(name) plt.close(fig) def paint_representation_1D_classical(self, prediction, name): fig, axs = plt.subplots() axs.plot(self.domain, self.target, color='black', label='Target Function') axs.plot(self.domain, prediction, color='C0', label='Classical ' + self.ansatz + ' model') axs.legend() fig.savefig(name) plt.close(fig) def paint_representation_2D(self): from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.add_subplot(111, projection='3d') if self.num_functions == 1: ax = fig.gca(projection='3d') print('shape', self.target[0].shape) ax.plot_trisurf(self.domain[:, 0], self.domain[:, 1], self.target[:, 0], label='Target Function', linewidth=0.2, antialiased=True) outcomes = np.zeros_like(self.domain) for j, x in enumerate(self.domain): C = self.circuit(x) state = C.execute() outcomes[j] = self.hamiltonian[0].expectation(state) ax.plot_trisurf(self.domain[:, 0], self.domain[:, 1], outcomes[:, 0] + 0.1, label='Approximation', linewidth=0.2, antialiased=True) #ax.legend() else: for i in range(self.num_functions): ax = fig.add_subplot(1, 1, i + 1, projection='3d') ax.plot(self.domain, self.functions[i](self.domain).flatten(), color='black') outcomes = np.zeros_like(self.domain) for j, x in enumerate(self.domain): C = self.circuit(x) state = C.execute() outcomes[j] = self.hamiltonian[i].expectation(state) ax.scatter(self.domain[:, 0], self.domain[:, 1], outcomes, color='C0', label=self.measurements[i]) ax.legend() fig.savefig(name) plt.close(fig) def paint_historical(self, name): import matplotlib.pyplot as plt fig, axs = plt.subplots(nrows=2) axs[0].plot(np.arange(len(self.hist_chi)), self.hist_chi) axs[0].set(yscale='log', ylabel=r'$\chi^2$') hist_params = np.array(self.hist_params) for i in range(len(self.params)): axs[1].plot(np.arange(len(self.hist_chi)), hist_params[:, i], 'C%s' % i) axs[1].set(ylabel='Parameter', xlabel='Function evaluation') fig.suptitle('Historical behaviour', fontsize=16) fig.savefig(name)