## Example ### Grover Example The Grover search algorithm uses AmplitudeAmplification to find the target specified in the oracle. ``` from spinqit import get_basic_simulator, get_compiler, Circuit, BasicSimulatorConfig from spinqit import AmplitudeAmplification, GateBuilder, RepeatBuilder from spinqit import H, X, Z from spinqit.primitive import MultiControlledGateBuilder from math import pi circ = Circuit() q = circ.allocateQubits(4) hbuilder = RepeatBuilder(H, 4) circ << (hbuilder.to_gate(), q) # Build the oracle for 1100 oracle_builder = GateBuilder(4) oracle_builder.append(X, [2]) oracle_builder.append(X, [3]) mcz_builder = MultiControlledGateBuilder(3, gate=Z) oracle_builder.append(mcz_builder.to_gate(), list(range(4))) oracle_builder.append(X, [2]) oracle_builder.append(X, [3]) grover = AmplitudeAmplification(oracle_builder.to_gate(), q) circ.extend(grover.build()) # Set up the backend and the compiler engine = get_basic_simulator() comp = get_compiler("native") optimization_level = 0 exe = comp.compile(circ, optimization_level) config = BasicSimulatorConfig() config.configure_shots(1024) # Run result = engine.execute(exe, config) print(result.counts) ``` ### HHL Example Systems of linear equations are fundamental to many science and engineering problems, especially in machine learning. The Harrow-Hassidim-Lloyd algorithm (HHL) can be used to solve certain linear systems on a quantum computer. ``` from spinqit.algorithm import HHL from spinqit import get_basic_simulator, BasicSimulatorConfig import numpy as np # Input the linear equations mat = np.array([[2.5, -0.5], [-0.5, 2.5]]) vec = np.array([1, 0]) # Set up the backend engine = get_basic_simulator() config = BasicSimulatorConfig() config.configure_shots(1024) # Run solver = HHL(mat, vec) solver.run(engine, config) print(solver.get_state()) print(solver.get_measurements()) ``` ### VQE Example Variational Quantum Eigensolver (VQE) can be used for quantum chemistry and optimization problems. The following example shows how to use VQE to calculate the ground state of the hydrogen molecule. ``` import numpy as np from spinqit import generate_hamiltonian_matrix from spinqit import Circuit, Rx, Rz, CX from spinqit.algorithm import VQE from spinqit.algorithm.optimizer import TorchOptimizer ham = [("IIII", -0.04207255194749729), ("ZIII", 0.17771358229095718), ("IZII", 0.17771358229095718), ("IIZI", -0.24274501260934922), ("IIIZ", -0.24274501260934922), ("ZIZI", 0.1229333044929736), ("IZIZ", 0.1229333044929736), ("ZIIZ", 0.16768338855598627), ("IZZI", 0.16768338855598627), ("ZZII", 0.1705975927683594), ("IIZZ", 0.17627661394176986), ("YYXX", -0.044750084063012674), ("XXYY", -0.044750084063012674), ("YXXY", 0.044750084063012674), ("XYYX", 0.044750084063012674)] depth = 1 qubit_num = len(ham[0][0]) Iter = 100 lr = 0.1 seed = 1024 np.random.seed(seed) circ = Circuit() qreg = circ.allocateQubits(qubit_num) params = circ.add_params(shape=(depth, qubit_num, 3)) for d in range(depth): for q in range(qubit_num): circ << (Rx, qreg[q], params[d][q][0]) circ << (Rz, qreg[q], params[d][q][1]) circ << (Rx, qreg[q], params[d][q][2]) for q in range(qubit_num - 1): circ.append(CX, [qreg[q], qreg[q + 1]]) circ.append(CX, [qreg[qubit_num - 1], qreg[0]]) optimizer = TorchOptimizer(maxiter=Iter, verbose=False, learning_rate=lr) ham_mat = generate_hamiltonian_matrix(ham) vqe = VQE(ham_mat, optimizer, ansatz=circ, params=(depth, qubit_num, 3)) loss_list = vqe.run(mode='torch', grad_method='backprop') # loss_list = vqe.run(mode='spinq', grad_method='param_shift') # loss_list = vqe.run(mode='spinq', grad_method='adjoint_differentiation') print(loss_list) ``` ### QAOA Example Quantum approximate optimization algorithm (QAOA) is a framework to solve optimization problems. This example shows how to solve the MaxCut problem using QAOA in SpinQit. ``` import numpy as np from spinqit import get_basic_simulator, BasicSimulatorConfig from spinqit import generate_hamiltonian_matrix from spinqit.algorithm.optimizer import ADAM, TorchOptimizer from spinqit.algorithm.qaoa import QAOA vcount = 4 E = [(0,1), (1,2), (2,3), (3,0)] # Build Hamiltonian ham = [] for (u,v) in E: pauli_str = '' for i in range(vcount): if i == u or i == v: pauli_str += 'Z' else: pauli_str += 'I' ham.append((pauli_str, 1.0)) print(ham) # ham = [('ZZII', 1.0), ('IZZI', 1.0), ('IIZZ', 1.0), ('ZIIZ', 1.0), ('IZIZ', 1.0)] qubit_num = vcount depth = 4 iter_num = 30 lr = 0.1 np.random.seed(1024) optimizer = TorchOptimizer(maxiter=iter_num, verbose=True, learning_rate=lr) ham_mat = generate_hamiltonian_matrix(ham) qaoa = QAOA(ham_mat, optimizer, depth) loss = qaoa.run(mode='torch', grad_method='backprop')[-1] result = qaoa.optimized_result print(result.probabilities) ``` ### Quantum Counting Example The example shows how to estimate the number of targets in the input values using the QuantumCounting algorithm. There should be 8 numbers ending with 1 in the example below. ``` from spinqit import get_basic_simulator, BasicSimulatorConfig from spinqit import GateBuilder, RepeatBuilder from spinqit import H, Z from spinqit.algorithm import QuantumCounting from math import pi hbuilder = RepeatBuilder(H, 4) # Build the oracle for ***1 oracle_builder = GateBuilder(4) oracle_builder.append(Z, [3]) # Set up the backend engine = get_basic_simulator() config = BasicSimulatorConfig() config.configure_shots(1024) qc = QuantumCounting(4, 4, hbuilder.to_gate(), oracle_builder.to_gate()) ret = qc.run(engine, config) print(ret) ``` ### QSearching Example The QSearching algorithm searches for the index of the maximum or minimum value in an array. ``` from spinqit.algorithm import QSearching dataset = [2, 3, 1, 4, 5, 6, 7, 15] seed = 330 max_searcher = QSearching(seed=seed) max_idx = max_searcher.search(dataset, show=False) min_searcher = QSearching('min', backend_mode='torch', seed=seed) min_idx = min_searcher.search(dataset, show=False) print(max_idx, min_idx) ``` ### QNN Example The example below shows how to use the PyTorch interface in SpinQit to solve a classification problem. ``` import numpy as np import torch import torch.optim as optim from torch import nn from functools import partial from spinqit import Circuit, Rz, Ry, CX, generate_hamiltonian_matrix from spinqit.primitive import amplitude_encoding from spinqit.interface import to_qlayer, TorchQuantumFunction from spinqit.algorithm.loss import expval #@to_qlayer(backend_mode='nmr', grad_method='param_shift', measure=expval(generate_hamiltonian_matrix([('ZI', 1)])), ip='192.168.2.4', port=59585, account=('user1','123456'), task_name='classifier', task_desc='test') @to_qlayer(backend_mode='torch', grad_method='backprop', measure=expval(generate_hamiltonian_matrix([('ZI', 1)]))) def build_circuit(state, weights_shape, qubit_num, layer_num): circ = Circuit() weight = circ.add_params(shape=weights_shape) q = circ.allocateQubits(qubit_num) state = state.numpy() #encode the input vector ilist = amplitude_encoding(state, q) circ.extend(ilist) for i in range(layer_num): for j in range(qubit_num): circ << (Rz, q[j], weight[i][j][0]) circ << (Ry, q[j], weight[i][j][1]) circ << (Rz, q[j], weight[i][j][2]) circ << (CX, q) return circ def get_data(file_path): data = np.loadtxt(file_path) Xdata = data[:, 0:2] padding = 0.3 * np.ones((len(Xdata), 1)) X_pad = np.c_[np.c_[Xdata, padding], np.zeros((len(Xdata), 1))] normalization = np.sqrt(np.sum(X_pad ** 2, -1)) X_norm = (X_pad.T / normalization).T features = X_norm Y = data[:, -1] np.random.seed(0) num_data = len(Y) n_train = int(0.75 * num_data) index = np.random.permutation(range(num_data)) x_train = torch.tensor(features[index[:n_train]], requires_grad=False).to(torch.float32) y_train = torch.tensor(Y[index[:n_train]], requires_grad=False).to(torch.float32) x_val = torch.tensor(features[index[n_train:]], requires_grad=False).to(torch.float32) y_val = Y[index[n_train:]] return n_train, x_train, y_train, x_val, y_val, def get_model(qubit_num, layer_num, bias=None): weight_shape = (layer_num, qubit_num, 3) class MyQuantumModule(nn.Module): def __init__(self, qlayer_func, weight_shape, bias=None): super(MyQuantumModule, self).__init__() self.w = nn.Parameter(0.01 * torch.randn(weight_shape), requires_grad=True) self.b = bias self.qlayer_func = qlayer_func def forward(self, state): res = torch.zeros(state.size(0)) if len(state.shape) > 1: for i in range(state.size(0)): myqlayer = self.qlayer_func(state[i]) kwargs = dict( qlayer=myqlayer, ) loss = TorchQuantumFunction.apply(kwargs, self.w) res[i] += loss else: myqlayer = self.qlayer_func(state) kwargs = dict( qlayer=myqlayer, ) res = TorchQuantumFunction.apply(kwargs, self.w) if self.b is not None: return res + self.b return res qlayer_func = partial(build_circuit, weights_shape = weight_shape, qubit_num=qubit_num, layer_num=layer_num) model = MyQuantumModule(qlayer_func, weight_shape, bias) return model def test_train(model, num_train, features_train, labels_train, features_val, labels_val): optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True) loss_fn = nn.MSELoss() iter = 45 batch_size = 10 print('-----begin train----------') for i in range(iter): batch_index = np.random.randint(0, num_train, (batch_size,)) feats_train_batch = features_train[batch_index] Y_train_batch = labels_train[batch_index] optimizer.zero_grad() pred = model(feats_train_batch) loss = loss_fn(pred, Y_train_batch) print(f'Loss : {loss.item()}') loss.backward() optimizer.step() print('---------begin predict--------------') total_error = 0 with torch.no_grad(): for k in range(len(features_val)): test_x = features_val[k].reshape(-1) pred = model(test_x) print(pred, labels_val[k]) if abs(labels_val[k] - np.sign(pred.item())) > 1e-5: total_error = total_error + 1 print(total_error) assert np.allclose(total_error, 0) if __name__ == '__main__': file_path = "resource/iris_classes_data.txt" qubit_num, layer_num = 2, 6 seed = 1024 np.random.seed(seed) torch.random.manual_seed(seed) bias = nn.Parameter(torch.tensor(0.0), requires_grad=True) model = get_model(qubit_num, layer_num, bias=bias) num_train, features_train, labels_train, features_val, labels_val, = get_data(file_path) test_train(model, num_train, features_train, labels_train, features_val, labels_val, ) ``` The example below shows how to use the TensorFlow interface in SpinQit to solve the same classification problem. ``` import numpy as np import tensorflow as tf from tensorflow.keras import Sequential, losses, optimizers from spinqit import Circuit, Rz, Ry, CX, generate_hamiltonian_matrix, StateVector from spinqit.interface import to_qlayer from spinqit.interface.tf_interface import QuantumLayer from spinqit.algorithm.loss import expval @to_qlayer(backend_mode='torch', grad_method='backprop', measure=expval(generate_hamiltonian_matrix([('ZI', 1)]))) def build_circuit(state_shape, weights_shape, qubit_num, layer_num): circ = Circuit() state = circ.add_params(shape=state_shape) weight = circ.add_params(shape=weights_shape) q = circ.allocateQubits(qubit_num) circ << (StateVector, q, state[:]) for i in range(layer_num): for j in range(qubit_num): circ << (Rz, q[j], weight[i][j][0]) circ << (Ry, q[j], weight[i][j][1]) circ << (Rz, q[j], weight[i][j][2]) circ << (CX, q) return circ def get_data(file_path): data = np.loadtxt(file_path) Xdata = data[:, 0:2] padding = 0.3 * np.ones((len(Xdata), 1)) X_pad = np.c_[np.c_[Xdata, padding], np.zeros((len(Xdata), 1))] normalization = np.sqrt(np.sum(X_pad ** 2, -1)) X_norm = (X_pad.T / normalization).T features = X_norm Y = data[:, -1] np.random.seed(0) num_data = len(Y) n_train = int(0.75 * num_data) index = np.random.permutation(range(num_data)) x_train = tf.Variable(features[index[:n_train]], trainable=False) y_train = tf.Variable(Y[index[:n_train]], trainable=False) x_val = tf.Variable(features[index[n_train:]], trainable=False) y_val = Y[index[n_train:]] return n_train, x_train, y_train, x_val, y_val, def get_model(qubit_num, layer_num, bias=None): weight_shape = (layer_num, qubit_num, 3) state_shape = (2 ** qubit_num) qlayer = build_circuit(state_shape, weight_shape, qubit_num, layer_num) ql = QuantumLayer(qlayer, weight_shape, bias) model = Sequential() model.add(ql) return model def test_train(model, num_train, features_train, labels_train, features_val, labels_val): optimizer = optimizers.SGD(learning_rate=0.01, momentum=0.9, nesterov=True) loss_fn = losses.MeanSquaredError() iter = 55 batch_size = 10 print('-----begin train----------') for i in range(iter): batch_index = np.random.randint(0, num_train, (batch_size,)) feats_train_batch = tf.gather(features_train, batch_index) Y_train_batch = tf.gather(labels_train, batch_index) with tf.GradientTape() as tape: pred = model(feats_train_batch, training=True) loss = loss_fn(Y_train_batch, pred) gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) print(f'Loss : {loss.numpy()}') print('---------begin predict--------------') total_error = 0 for k in range(features_val.shape[0]): test_x = features_val[k] pred = model(test_x) print(pred, labels_val[k]) if abs(labels_val[k] - np.sign(pred.numpy())) > 1e-5: total_error = total_error + 1 print(total_error) assert np.allclose(total_error, 0) if __name__ == '__main__': file_path = "resource/iris_classes_data.txt" qubit_num, layer_num = 2, 6 seed = 1024 np.random.seed(seed) tf.random.set_seed(seed) bias = tf.Variable(0.0, trainable=True) model = get_model(qubit_num, layer_num, bias=bias) num_train, features_train, labels_train, features_val, labels_val, = get_data(file_path) test_train(model, num_train, features_train, labels_train, features_val, labels_val) ```