def validate_output_shape(self, qnn: OpflowQNN, test_data: List[np.ndarray]) -> None: """ Asserts that the opflow qnn returns results of the correct output shape. Args: qnn: QNN to be tested test_data: list of test input arrays Raises: QiskitMachineLearningError: Invalid input. """ # get weights weights = np.random.rand(qnn.num_weights) # iterate over test data and validate behavior of model for x in test_data: # evaluate network forward_shape = qnn.forward(x, weights).shape grad = qnn.backward(x, weights) backward_shape_input = grad[0].shape backward_shape_weights = grad[1].shape # derive batch shape form input batch_shape = x.shape[:-len(qnn.output_shape)] if len(batch_shape) == 0: batch_shape = (1,) # compare results and assert that the behavior is equal self.assertEqual(forward_shape, (*batch_shape, *qnn.output_shape)) self.assertEqual(backward_shape_input, (*batch_shape, *qnn.output_shape, qnn.num_inputs)) self.assertEqual(backward_shape_weights, (*batch_shape, *qnn.output_shape, qnn.num_weights))
def setUp(self): super().setUp() # specify "run configuration" backend = Aer.get_backend('statevector_simulator') quantum_instance = QuantumInstance(backend) # specify how to evaluate expected values and gradients expval = PauliExpectation() gradient = Gradient() # construct parametrized circuit params = [Parameter('input1'), Parameter('weight1')] qc = QuantumCircuit(1) qc.h(0) qc.ry(params[0], 0) qc.rx(params[1], 0) qc_sfn = StateFn(qc) # construct cost operator cost_operator = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)])) # combine operator and circuit to objective function op = ~cost_operator @ qc_sfn # define QNN self.qnn = OpflowQNN(op, [params[0]], [params[1]], expval, gradient, quantum_instance=quantum_instance)
def test_opflow_qnn_2_2(self, config): """Test Opflow QNN with input/output dimension 2/2.""" q_i, input_grad_required = config if q_i == STATEVECTOR: quantum_instance = self.sv_quantum_instance elif q_i == QASM: quantum_instance = self.qasm_quantum_instance else: quantum_instance = None # construct parametrized circuit params_1 = [Parameter("input1"), Parameter("weight1")] qc_1 = QuantumCircuit(1) qc_1.h(0) qc_1.ry(params_1[0], 0) qc_1.rx(params_1[1], 0) qc_sfn_1 = StateFn(qc_1) # construct cost operator h_1 = StateFn(PauliSumOp.from_list([("Z", 1.0), ("X", 1.0)])) # combine operator and circuit to objective function op_1 = ~h_1 @ qc_sfn_1 # construct parametrized circuit params_2 = [Parameter("input2"), Parameter("weight2")] qc_2 = QuantumCircuit(1) qc_2.h(0) qc_2.ry(params_2[0], 0) qc_2.rx(params_2[1], 0) qc_sfn_2 = StateFn(qc_2) # construct cost operator h_2 = StateFn(PauliSumOp.from_list([("Z", 1.0), ("X", 1.0)])) # combine operator and circuit to objective function op_2 = ~h_2 @ qc_sfn_2 op = ListOp([op_1, op_2]) qnn = OpflowQNN( op, [params_1[0], params_2[0]], [params_1[1], params_2[1]], quantum_instance=quantum_instance, ) qnn.input_gradients = input_grad_required test_data = [np.array([1, 2]), np.array([[1, 2], [3, 4]])] # test model self.validate_output_shape(qnn, test_data) # test the qnn after we set a quantum instance if quantum_instance is None: qnn.quantum_instance = self.qasm_quantum_instance self.validate_output_shape(qnn, test_data)
def test_opflow_qnn_2_1(self, config): """Test Opflow QNN with input/output dimension 2/1.""" q_i, input_grad_required = config # construct QNN if q_i == STATEVECTOR: quantum_instance = self.sv_quantum_instance elif q_i == QASM: quantum_instance = self.qasm_quantum_instance else: quantum_instance = None # specify how to evaluate expected values and gradients expval = PauliExpectation() gradient = Gradient() # construct parametrized circuit params = [ Parameter("input1"), Parameter("input2"), Parameter("weight1"), Parameter("weight2"), ] qc = QuantumCircuit(2) qc.h(0) qc.ry(params[0], 0) qc.ry(params[1], 1) qc.rx(params[2], 0) qc.rx(params[3], 1) qc_sfn = StateFn(qc) # construct cost operator cost_operator = StateFn(PauliSumOp.from_list([("ZZ", 1.0), ("XX", 1.0)])) # combine operator and circuit to objective function op = ~cost_operator @ qc_sfn # define QNN qnn = OpflowQNN( op, params[:2], params[2:], expval, gradient, quantum_instance=quantum_instance, ) qnn.input_gradients = input_grad_required test_data = [np.array([1, 2]), np.array([[1, 2]]), np.array([[1, 2], [3, 4]])] # test model self.validate_output_shape(qnn, test_data) # test the qnn after we set a quantum instance if quantum_instance is None: qnn.quantum_instance = self.qasm_quantum_instance self.validate_output_shape(qnn, test_data)
class TestOpflowQNN(QiskitMachineLearningTestCase): """Opflow QNN Tests.""" def setUp(self): super().setUp() # specify "run configuration" backend = Aer.get_backend('statevector_simulator') quantum_instance = QuantumInstance(backend) # specify how to evaluate expected values and gradients expval = PauliExpectation() gradient = Gradient() # construct parametrized circuit params = [Parameter('input1'), Parameter('weight1')] qc = QuantumCircuit(1) qc.h(0) qc.ry(params[0], 0) qc.rx(params[1], 0) qc_sfn = StateFn(qc) # construct cost operator cost_operator = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)])) # combine operator and circuit to objective function op = ~cost_operator @ qc_sfn # define QNN self.qnn = OpflowQNN(op, [params[0]], [params[1]], expval, gradient, quantum_instance=quantum_instance) def test_opflow_qnn1(self): """ Opflow QNN Test """ input_data = np.zeros(self.qnn.num_inputs) weights = np.zeros(self.qnn.num_weights) # test forward pass result = self.qnn.forward(input_data, weights) self.assertEqual(result.shape, (1, *self.qnn.output_shape)) # test backward pass result = self.qnn.backward(input_data, weights) self.assertEqual(result[0].shape, (1, self.qnn.num_inputs, *self.qnn.output_shape)) self.assertEqual(result[1].shape, (1, self.qnn.num_weights, *self.qnn.output_shape))
def test_opflow_qnn_2_2(self, q_i): """Test Torch Connector + Opflow QNN with input/output dimension 2/2.""" from torch import Tensor if q_i == "sv": quantum_instance = self._sv_quantum_instance else: quantum_instance = self._qasm_quantum_instance # construct parametrized circuit params_1 = [Parameter("input1"), Parameter("weight1")] qc_1 = QuantumCircuit(1) qc_1.h(0) qc_1.ry(params_1[0], 0) qc_1.rx(params_1[1], 0) qc_sfn_1 = StateFn(qc_1) # construct cost operator h_1 = StateFn(PauliSumOp.from_list([("Z", 1.0), ("X", 1.0)])) # combine operator and circuit to objective function op_1 = ~h_1 @ qc_sfn_1 # construct parametrized circuit params_2 = [Parameter("input2"), Parameter("weight2")] qc_2 = QuantumCircuit(1) qc_2.h(0) qc_2.ry(params_2[0], 0) qc_2.rx(params_2[1], 0) qc_sfn_2 = StateFn(qc_2) # construct cost operator h_2 = StateFn(PauliSumOp.from_list([("Z", 1.0), ("X", 1.0)])) # combine operator and circuit to objective function op_2 = ~h_2 @ qc_sfn_2 op = ListOp([op_1, op_2]) qnn = OpflowQNN( op, [params_1[0], params_2[0]], [params_1[1], params_2[1]], 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]]), ] # test model self._validate_output_shape(model, test_data) if q_i == "sv": self._validate_backward_pass(model)
def test_opflow_qnn_2_2(self, q_i): """ Test Torch Connector + Opflow QNN with input/output dimension 2/2.""" if q_i == 'sv': quantum_instance = self.sv_quantum_instance else: quantum_instance = self.qasm_quantum_instance # construct parametrized circuit params_1 = [Parameter('input1'), Parameter('weight1')] qc_1 = QuantumCircuit(1) qc_1.h(0) qc_1.ry(params_1[0], 0) qc_1.rx(params_1[1], 0) qc_sfn_1 = StateFn(qc_1) # construct cost operator h_1 = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)])) # combine operator and circuit to objective function op_1 = ~h_1 @ qc_sfn_1 # construct parametrized circuit params_2 = [Parameter('input2'), Parameter('weight2')] qc_2 = QuantumCircuit(1) qc_2.h(0) qc_2.ry(params_2[0], 0) qc_2.rx(params_2[1], 0) qc_sfn_2 = StateFn(qc_2) # construct cost operator h_2 = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)])) # combine operator and circuit to objective function op_2 = ~h_2 @ qc_sfn_2 op = ListOp([op_1, op_2]) qnn = OpflowQNN(op, [params_1[0], params_2[0]], [params_1[1], params_2[1]], quantum_instance=quantum_instance) try: 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) except MissingOptionalLibraryError as ex: self.skipTest(str(ex))
def test_opflow_qnn_2_1(self, q_i): """ Test Opflow QNN with input/output dimension 2/1.""" # construct QNN if q_i == 'sv': quantum_instance = self.sv_quantum_instance else: quantum_instance = self.qasm_quantum_instance # specify how to evaluate expected values and gradients expval = PauliExpectation() gradient = Gradient() # construct parametrized circuit params = [ Parameter('input1'), Parameter('input2'), Parameter('weight1'), Parameter('weight2') ] qc = QuantumCircuit(2) qc.h(0) qc.ry(params[0], 0) qc.ry(params[1], 1) qc.rx(params[2], 0) qc.rx(params[3], 1) qc_sfn = StateFn(qc) # construct cost operator cost_operator = StateFn( PauliSumOp.from_list([('ZZ', 1.0), ('XX', 1.0)])) # combine operator and circuit to objective function op = ~cost_operator @ qc_sfn # define QNN qnn = OpflowQNN(op, params[:2], params[2:], expval, gradient, quantum_instance=quantum_instance) test_data = [ np.array([1, 2]), np.array([[1, 2]]), np.array([[1, 2], [3, 4]]) ] # test model self.validate_output_shape(qnn, test_data)
def test_opflow_qnn_2_2(self, q_i): """ Test Opflow QNN with input/output dimension 2/2.""" if q_i == 'sv': quantum_instance = self.sv_quantum_instance else: quantum_instance = self.qasm_quantum_instance # construct parametrized circuit params_1 = [Parameter('input1'), Parameter('weight1')] qc_1 = QuantumCircuit(1) qc_1.h(0) qc_1.ry(params_1[0], 0) qc_1.rx(params_1[1], 0) qc_sfn_1 = StateFn(qc_1) # construct cost operator h_1 = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)])) # combine operator and circuit to objective function op_1 = ~h_1 @ qc_sfn_1 # construct parametrized circuit params_2 = [Parameter('input2'), Parameter('weight2')] qc_2 = QuantumCircuit(1) qc_2.h(0) qc_2.ry(params_2[0], 0) qc_2.rx(params_2[1], 0) qc_sfn_2 = StateFn(qc_2) # construct cost operator h_2 = StateFn(PauliSumOp.from_list([('Z', 1.0), ('X', 1.0)])) # combine operator and circuit to objective function op_2 = ~h_2 @ qc_sfn_2 op = ListOp([op_1, op_2]) qnn = OpflowQNN(op, [params_1[0], params_2[0]], [params_1[1], params_2[1]], quantum_instance=quantum_instance) test_data = [ np.array([1, 2]), np.array([[1, 2], [3, 4]]) ] # test model self.validate_output_shape(qnn, test_data)
def test_composed_op(self): """Tests OpflowQNN with ComposedOp as an operator.""" qc = QuantumCircuit(1) param = Parameter("param") qc.rz(param, 0) h_1 = PauliSumOp.from_list([("Z", 1.0)]) h_2 = PauliSumOp.from_list([("Z", 1.0)]) h_op = ListOp([h_1, h_2]) op = ~StateFn(h_op) @ StateFn(qc) # initialize QNN qnn = OpflowQNN(op, [], [param]) # create random data and weights for testing input_data = np.random.rand(2, qnn.num_inputs) weights = np.random.rand(qnn.num_weights) qnn.forward(input_data, weights) qnn.backward(input_data, weights)