def test_circuit_qnn_without_parameters(self): """Tests CircuitQNN without parameters.""" quantum_instance = self._sv_quantum_instance qc = QuantumCircuit(2) param_y = Parameter("y") qc.ry(param_y, range(2)) qnn = CircuitQNN( circuit=qc, input_params=[param_y], sparse=False, quantum_instance=quantum_instance, input_gradients=True, ) model = TorchConnector(qnn) self._validate_backward_pass(model) qnn = CircuitQNN( circuit=qc, weight_params=[param_y], sparse=False, quantum_instance=quantum_instance, input_gradients=True, ) model = TorchConnector(qnn) self._validate_backward_pass(model)
def _get_qnn(self, sparse, sampling, quantum_instance_type, interpret_id): """Construct QNN from configuration.""" # get quantum instance if quantum_instance_type == STATEVECTOR: quantum_instance = self.quantum_instance_sv elif quantum_instance_type == QASM: quantum_instance = self.quantum_instance_qasm elif quantum_instance_type == CUSTOM_PASS_MANAGERS: quantum_instance = self.quantum_instance_pm else: quantum_instance = None # get interpret setting interpret = None output_shape = None if interpret_id == 1: interpret = self.interpret_1d output_shape = self.output_shape_1d elif interpret_id == 2: interpret = self.interpret_2d output_shape = self.output_shape_2d # construct QNN qnn = CircuitQNN( self.qc, self.input_params, self.weight_params, sparse=sparse, sampling=sampling, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance, ) return qnn
def get_qnn(self, sparse, sampling, statevector, interpret_id): """ Construct QNN from configuration. """ # get quantum instance if statevector: quantum_instance = self.quantum_instance_sv else: quantum_instance = self.quantum_instance_qasm # get interpret setting interpret = None output_shape = None if interpret_id == 1: interpret = self.interpret_1d output_shape = self.output_shape_1d elif interpret_id == 2: interpret = self.interpret_2d output_shape = self.output_shape_2d # construct QNN qnn = CircuitQNN(self.qc, self.input_params, self.weight_params, sparse=sparse, sampling=sampling, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance) return qnn
def __init__(self, h, w, outputs): super(DQN, self).__init__() qi = QuantumInstance(Aer.get_backend('statevector_simulator')) num_inputs=4 feature_map = ZZFeatureMap(num_inputs) ansatz = RealAmplitudes(num_inputs, entanglement='linear', reps=1) qc = QuantumCircuit(num_inputs) qc.append(feature_map, range(num_inputs)) qc.append(ansatz, range(num_inputs)) parity = lambda x: '{:b}'.format(x).count('1') % num_inputs output_shape =num_inputs # parity = 0, 1 qnn = CircuitQNN(qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, interpret=parity, output_shape=output_shape, quantum_instance=qi) #set up PyTorch Module initial_weights = 0.1*(2*np.random.rand(qnn.num_weights) - 1) # Our final network choice. It is wide enough to capture a lot of detail but not too # large to have problems with vanishing gradients on such a small sample size self.conv1 = nn.Conv2d(1, 256, kernel_size=2, stride=1, padding=1) self.bn1 = nn.BatchNorm2d(256) self.fcl1 = nn.Linear(20736,10000) self.fcl2 = nn.Linear(10000, num_inputs) self.qnn = TorchConnector(qnn, initial_weights)
def test_circuit_qnn_sampling(self, interpret): """Test Torch Connector + Circuit QNN for sampling.""" qc = QuantumCircuit(2) # construct simple feature map param_x1, param_x2 = Parameter('x1'), Parameter('x2') qc.ry(param_x1, range(2)) qc.ry(param_x2, range(2)) # construct simple feature map param_y = Parameter('y') qc.ry(param_y, range(2)) qnn = CircuitQNN(qc, [param_x1, param_x2], [param_y], sparse=False, sampling=True, interpret=interpret, output_shape=None, quantum_instance=self.qasm_quantum_instance) model = TorchConnector(qnn) test_data = [Tensor([2, 2]), Tensor([[1, 1], [2, 2]])] for i, x in enumerate(test_data): if i == 0: self.assertEqual(model(x).shape, qnn.output_shape) else: shape = model(x).shape self.assertEqual(shape, (len(x), *qnn.output_shape))
def _create_circuit_qnn(self, quantum_instance: QuantumInstance) -> Tuple[CircuitQNN, int, int]: num_inputs = 2 feature_map = ZZFeatureMap(num_inputs) ansatz = RealAmplitudes(num_inputs, reps=1) # construct circuit qc = QuantumCircuit(num_inputs) qc.append(feature_map, range(2)) qc.append(ansatz, range(2)) # construct qnn def parity(x): return f"{x:b}".count("1") % 2 output_shape = 2 qnn = CircuitQNN( qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, sparse=False, interpret=parity, output_shape=output_shape, quantum_instance=quantum_instance, ) return qnn, num_inputs, ansatz.num_parameters
def test_classifier_with_circuit_qnn_and_cross_entropy(self, config): """ Test Neural Network Classifier with Circuit QNN and Cross Entropy loss.""" opt, q_i = config if q_i == 'statevector': quantum_instance = self.sv_quantum_instance else: quantum_instance = self.qasm_quantum_instance if opt == 'bfgs': optimizer = L_BFGS_B(maxiter=5) else: optimizer = COBYLA(maxiter=25) loss = CrossEntropyLoss() num_inputs = 2 feature_map = ZZFeatureMap(num_inputs) ansatz = RealAmplitudes(num_inputs, reps=1) # construct circuit qc = QuantumCircuit(num_inputs) qc.append(feature_map, range(2)) qc.append(ansatz, range(2)) # construct qnn def parity(x): return '{:b}'.format(x).count('1') % 2 output_shape = 2 qnn = CircuitQNN(qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, sparse=False, interpret=parity, output_shape=output_shape, quantum_instance=quantum_instance) # construct classifier - note: CrossEntropy requires eval_probabilities=True! classifier = NeuralNetworkClassifier(qnn, optimizer=optimizer, loss=loss, one_hot=True) # construct data num_samples = 5 X = np.random.rand(num_samples, num_inputs) # pylint: disable=invalid-name y = 1.0 * (np.sum(X, axis=1) <= 1) y = np.array([y, 1 - y]).transpose() # fit to data classifier.fit(X, y) # score score = classifier.score(X, y) self.assertGreater(score, 0.5)
def test_circuit_qnn_2_4(self, config): """Torch Connector + Circuit QNN with no interpret, dense output, and input/output shape 1/8 .""" from torch import Tensor interpret, output_shape, sparse, q_i = config if sparse and not _optionals.HAS_SPARSE: self.skipTest("sparse library is required to run this test") return if q_i == "sv": quantum_instance = self._sv_quantum_instance else: quantum_instance = self._qasm_quantum_instance qc = QuantumCircuit(2) # construct simple feature map param_x_1, param_x_2 = Parameter("x1"), Parameter("x2") qc.ry(param_x_1, range(2)) qc.ry(param_x_2, range(2)) # construct simple feature map param_y = Parameter("y") qc.ry(param_y, range(2)) qnn = CircuitQNN( qc, [param_x_1, param_x_2], [param_y], sparse=sparse, sampling=False, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance, input_gradients=True, ) model = TorchConnector(qnn) test_data = [ Tensor([1]), Tensor([1, 2]), Tensor([[1], [2]]), Tensor([[1, 2], [3, 4]]), Tensor([[[1], [2]], [[3], [4]]]), ] # test model self._validate_output_shape(model, test_data) if q_i == "sv": self._validate_backward_pass(model)
def test_warning1_circuit_qnn(self, config): """Circuit QNN with no sampling and input/output shape 1/1 .""" interpret, output_shape, sparse, q_i = config if sparse and not _optionals.HAS_SPARSE: self.skipTest("sparse library is required to run this test") return if q_i == "sv": quantum_instance = self.quantum_instance_sv elif q_i == "qasm": quantum_instance = self.quantum_instance_qasm else: raise ValueError("Unsupported quantum instance") qc = QuantumCircuit(1) # construct simple feature map param_x = Parameter("x") qc.ry(param_x, 0) # construct simple feature map param_y = Parameter("y") qc.ry(param_y, 0) # check warning when output_shape defined without interpret with self.assertLogs(level="WARNING") as w_1: CircuitQNN( qc, [param_x], [param_y], sparse=sparse, sampling=False, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance, input_gradients=True, ) self.assertEqual( w_1.output, [ "WARNING:qiskit_machine_learning.neural_networks.circuit_qnn:No " "interpret function given, output_shape will be automatically " "determined as 2^num_qubits." ], )
def test_circuit_qnn_2_4(self, config): """Torch Connector + Circuit QNN with no interpret, dense output, and input/output shape 1/8 .""" interpret, output_shape, sparse, q_i = config if q_i == 'sv': quantum_instance = self.sv_quantum_instance else: quantum_instance = self.qasm_quantum_instance qc = QuantumCircuit(2) # construct simple feature map param_x_1, param_x_2 = Parameter('x1'), Parameter('x2') qc.ry(param_x_1, range(2)) qc.ry(param_x_2, range(2)) # construct simple feature map param_y = Parameter('y') qc.ry(param_y, range(2)) qnn = CircuitQNN(qc, [param_x_1, param_x_2], [param_y], sparse=sparse, sampling=False, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance) try: model = TorchConnector(qnn) test_data = [ Tensor(1), Tensor([1, 2]), Tensor([[1], [2]]), Tensor([[1, 2], [3, 4]]), Tensor([[[1], [2]], [[3], [4]]]) ] # test model self.validate_output_shape(model, test_data) if q_i == 'sv': self.validate_backward_pass(model) except MissingOptionalLibraryError as ex: self.skipTest(str(ex))
def test_warning2_circuit_qnn(self, config): """Torch Connector + Circuit QNN with sampling and input/output shape 1/1 .""" interpret, output_shape, sparse, q_i = config if q_i == "sv": quantum_instance = self.quantum_instance_sv elif q_i == "qasm": quantum_instance = self.quantum_instance_qasm else: raise ValueError("Unsupported quantum instance") qc = QuantumCircuit(2) # construct simple feature map param_x = Parameter("x") qc.ry(param_x, 0) # construct simple feature map param_y = Parameter("y") qc.ry(param_y, 0) # check warning when sampling true with self.assertLogs(level="WARNING") as w_2: CircuitQNN( qc, [param_x], [param_y], sparse=sparse, sampling=True, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance, input_gradients=True, ) self.assertEqual( w_2.output, [ "WARNING:qiskit_machine_learning.neural_networks.circuit_qnn:" "In sampling mode, output_shape will be automatically inferred " "from the number of samples and interpret function, if provided." ], )
def _create_circuit_qnn(self) -> CircuitQNN: output_shape, interpret = 2, lambda x: f"{x:b}".count("1") % 2 num_inputs = 2 feature_map = ZZFeatureMap(num_inputs) ansatz = RealAmplitudes(num_inputs, entanglement="linear", reps=1) qc = QuantumCircuit(num_inputs) qc.append(feature_map, range(num_inputs)) qc.append(ansatz, range(num_inputs)) qnn = CircuitQNN( qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, input_gradients=True, # for hybrid qnn interpret=interpret, output_shape=output_shape, quantum_instance=self._sv_quantum_instance, ) return qnn
def test_circuit_qnn_1_8(self, config): """Torch Connector + Circuit QNN with no interpret, dense output, and input/output shape 1/8 .""" interpret, output_shape, sparse, q_i = config if q_i == 'sv': quantum_instance = self.sv_quantum_instance else: quantum_instance = self.qasm_quantum_instance qc = QuantumCircuit(3) # construct simple feature map param_x = Parameter('x') qc.ry(param_x, range(3)) # construct simple feature map param_y = Parameter('y') qc.ry(param_y, range(3)) qnn = CircuitQNN(qc, [param_x], [param_y], sparse=sparse, sampling=False, interpret=interpret, output_shape=output_shape, quantum_instance=quantum_instance) model = TorchConnector(qnn) test_data = [ Tensor(1), Tensor([1, 2]), Tensor([[1], [2]]), Tensor([[[1], [2]], [[3], [4]]]) ] # test model self.validate_output_shape(model, test_data) if q_i == 'sv': self.validate_backward_pass(model)
def main(args): # Load the data data, __ = titanic() X_train, y_train, X_test, y_test = parse_data_train_vqc( data, split_ratio=args.split_ratio) quantum_instance = QuantumInstance( Aer.get_backend('statevector_simulator'), shots=100) optimizer = COBYLA(maxiter=100) feature_map = ZZFeatureMap(feature_dimension=X_train.shape[1], reps=1) ansatz = RealAmplitudes(num_qubits=feature_map._num_qubits, reps=1) qc = QuantumCircuit(feature_map._num_qubits) qc.compose(feature_map, inplace=True) qc.compose(ansatz, inplace=True) qnn = CircuitQNN(circuit=qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, sparse=False, sampling=False, interpret=parity, output_shape=len(np.unique(y_train, axis=0)), gradient=None, quantum_instance=quantum_instance) cc = NeuralNetworkClassifier(neural_network=qnn, optimizer=optimizer) # Train the model cc.fit(X_train, y_train) # Model accuracy acc_train = cc.score(X_train, y_train) acc_test = cc.score(X_test, y_test) print("Accuracy on training dataset: {}.".format(acc_train)) print("Accuracy on testing dataset: {}.".format(acc_test))
def _verify_qnn( self, qnn: CircuitQNN, quantum_instance_type: str, batch_size: int, ) -> None: """ Verifies that a QNN functions correctly Args: qnn: a QNN to check quantum_instance_type: batch_size: Returns: None. """ # pylint: disable=import-error from sparse import SparseArray input_data = np.zeros((batch_size, qnn.num_inputs)) weights = np.zeros(qnn.num_weights) # if sampling and statevector, make sure it fails if quantum_instance_type == STATEVECTOR and qnn.sampling: with self.assertRaises(QiskitMachineLearningError): qnn.forward(input_data, weights) else: # evaluate QNN forward pass result = qnn.forward(input_data, weights) # make sure forward result is sparse if it should be if qnn.sparse and not qnn.sampling: self.assertTrue(isinstance(result, SparseArray)) else: self.assertTrue(isinstance(result, np.ndarray)) # check forward result shape self.assertEqual(result.shape, (batch_size, *qnn.output_shape)) input_grad, weights_grad = qnn.backward(input_data, weights) if qnn.sampling: self.assertIsNone(input_grad) self.assertIsNone(weights_grad) else: self.assertIsNone(input_grad) self.assertEqual( weights_grad.shape, (batch_size, *qnn.output_shape, qnn.num_weights)) # verify that input gradients are None if turned off qnn.input_gradients = True input_grad, weights_grad = qnn.backward(input_data, weights) if qnn.sampling: self.assertIsNone(input_grad) self.assertIsNone(weights_grad) else: self.assertEqual( input_grad.shape, (batch_size, *qnn.output_shape, qnn.num_inputs)) self.assertEqual( weights_grad.shape, (batch_size, *qnn.output_shape, qnn.num_weights))
def test_circuit_qnn_batch_gradients(self, config): """Test batch gradient computation of CircuitQNN gives the same result as the sum of individual gradients.""" import torch from torch.nn import MSELoss from torch.optim import SGD output_shape, interpret = config num_inputs = 2 feature_map = ZZFeatureMap(num_inputs) ansatz = RealAmplitudes(num_inputs, entanglement="linear", reps=1) qc = QuantumCircuit(num_inputs) qc.append(feature_map, range(num_inputs)) qc.append(ansatz, range(num_inputs)) qnn = CircuitQNN( qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, interpret=interpret, output_shape=output_shape, quantum_instance=self._sv_quantum_instance, ) # set up PyTorch module initial_weights = np.array([0.1] * qnn.num_weights) model = TorchConnector(qnn, initial_weights) model.to(self._device) # random data set x = torch.rand(5, 2) y = torch.rand(5, output_shape) # define optimizer and loss optimizer = SGD(model.parameters(), lr=0.1) f_loss = MSELoss(reduction="sum") sum_of_individual_losses = 0.0 for x_i, y_i in zip(x, y): x_i = x_i.to(self._device) y_i = y_i.to(self._device) output = model(x_i) sum_of_individual_losses += f_loss(output, y_i) optimizer.zero_grad() sum_of_individual_losses.backward() sum_of_individual_gradients = model.weight.grad.detach().cpu() x = x.to(self._device) y = y.to(self._device) output = model(x) batch_loss = f_loss(output, y) optimizer.zero_grad() batch_loss.backward() batch_gradients = model.weight.grad.detach().cpu() self.assertAlmostEqual(np.linalg.norm(sum_of_individual_gradients - batch_gradients), 0.0, places=4) self.assertAlmostEqual( sum_of_individual_losses.detach().cpu().numpy(), batch_loss.detach().cpu().numpy(), places=4, )
def setUp(self): super().setUp() algorithm_globals.random_seed = 1234 qi_sv = QuantumInstance( Aer.get_backend("aer_simulator_statevector"), seed_simulator=algorithm_globals.random_seed, seed_transpiler=algorithm_globals.random_seed, ) # set up quantum neural networks num_qubits = 3 feature_map = ZFeatureMap(feature_dimension=num_qubits, reps=1) ansatz = RealAmplitudes(num_qubits, reps=1) # CircuitQNNs qc = QuantumCircuit(num_qubits) qc.append(feature_map, range(num_qubits)) qc.append(ansatz, range(num_qubits)) def parity(x): return f"{x:b}".count("1") % 2 circuit_qnn_1 = CircuitQNN( qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, interpret=parity, output_shape=2, sparse=False, quantum_instance=qi_sv, ) # qnn2 for checking result without parity circuit_qnn_2 = CircuitQNN( qc, input_params=feature_map.parameters, weight_params=ansatz.parameters, sparse=False, quantum_instance=qi_sv, ) # OpflowQNN observable = PauliSumOp.from_list([("Z" * num_qubits, 1)]) opflow_qnn = TwoLayerQNN( num_qubits, feature_map=feature_map, ansatz=ansatz, observable=observable, quantum_instance=qi_sv, ) self.qnns = { "circuit1": circuit_qnn_1, "circuit2": circuit_qnn_2, "opflow": opflow_qnn } # define sample numbers self.n_list = [ 5000, 8000, 10000, 40000, 60000, 100000, 150000, 200000, 500000, 1000000 ] self.n = 5000