def test_coder_operators(self): """Test runtime encoder and decoder for operators.""" x = Parameter("x") y = x + 1 qc = QuantumCircuit(1) qc.h(0) coeffs = np.array([1, 2, 3, 4, 5, 6]) table = PauliTable.from_labels( ["III", "IXI", "IYY", "YIZ", "XYZ", "III"]) op = (2.0 * I ^ I) z2_symmetries = Z2Symmetries( [Pauli("IIZI"), Pauli("ZIII")], [Pauli("IIXI"), Pauli("XIII")], [1, 3], [-1, 1]) isqrt2 = 1 / np.sqrt(2) sparse = scipy.sparse.csr_matrix([[0, isqrt2, 0, isqrt2]]) subtests = ( PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), coeff=3), PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1]), coeff=y), PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[1 + 2j]), coeff=3 - 2j), PauliSumOp.from_list([("II", -1.052373245772859), ("IZ", 0.39793742484318045)]), PauliSumOp(SparsePauliOp(table, coeffs), coeff=10), MatrixOp(primitive=np.array([[0, -1j], [1j, 0]]), coeff=x), PauliOp(primitive=Pauli("Y"), coeff=x), CircuitOp(qc, coeff=x), EvolvedOp(op, coeff=x), TaperedPauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]), z2_symmetries), StateFn(qc, coeff=x), CircuitStateFn(qc, is_measurement=True), DictStateFn("1" * 3, is_measurement=True), VectorStateFn(np.ones(2**3, dtype=complex)), OperatorStateFn(CircuitOp(QuantumCircuit(1))), SparseVectorStateFn(sparse), Statevector([1, 0]), CVaRMeasurement(Z, 0.2), ComposedOp([(X ^ Y ^ Z), (Z ^ X ^ Y ^ Z).to_matrix_op()]), SummedOp([X ^ X * 2, Y ^ Y], 2), TensoredOp([(X ^ Y), (Z ^ I)]), (Z ^ Z) ^ (I ^ 2), ) for op in subtests: with self.subTest(op=op): encoded = json.dumps(op, cls=RuntimeEncoder) self.assertIsInstance(encoded, str) decoded = json.loads(encoded, cls=RuntimeDecoder) self.assertEqual(op, decoded)
def test_circuit_sampler2(self, method): """Test the probability gradient with the circuit sampler dp0/da = cos(a)sin(b) / 2 dp1/da = - cos(a)sin(b) / 2 dp0/db = sin(a)cos(b) / 2 dp1/db = - sin(a)cos(b) / 2 """ a = Parameter("a") b = Parameter("b") params = [a, b] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = CircuitStateFn(primitive=qc, coeff=1.0) shots = 8000 if method == "fin_diff": np.random.seed(8) prob_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.0)).convert( operator=op, params=params ) else: prob_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [ {a: [np.pi / 4], b: [0]}, {params[0]: [np.pi / 4], params[1]: [np.pi / 4]}, {params[0]: [np.pi / 2], params[1]: [np.pi]}, ] correct_values = [ [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], [[1 / 4, -1 / 4], [1 / 4, -1 / 4]], [[0, 0], [-1 / 2, 1 / 2]], ] backend = BasicAer.get_backend("qasm_simulator") q_instance = QuantumInstance(backend=backend, shots=shots) for i, value_dict in enumerate(values_dict): sampler = CircuitSampler(backend=q_instance).convert(prob_grad, params=value_dict) result = sampler.eval()[0] self.assertTrue(np.allclose(result[0].toarray(), correct_values[i][0], atol=0.1)) self.assertTrue(np.allclose(result[1].toarray(), correct_values[i][1], atol=0.1))
def test_state_hessian_custom_combo_fn(self, method): """Test the state Hessian with on an operator which includes a user-defined combo_fn. Tr(|psi><psi|Z) = sin(a)sin(b) Tr(|psi><psi|X) = cos(a) d^2<H>/da^2 = - 0.5 cos(a) + 1 sin(a)sin(b) d^2<H>/dbda = - 1 cos(a)cos(b) d^2<H>/dbda = - 1 cos(a)cos(b) d^2<H>/db^2 = + 1 sin(a)sin(b) """ ham = 0.5 * X - 1 * Z a = Parameter('a') b = Parameter('b') params = [(a, a), (a, b), (b, b)] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(b, q[0]) op = ListOp([~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.)], combo_fn=lambda x: x[0]**3 + 4 * x[0]) state_hess = Hessian(hess_method=method).convert(operator=op, params=params) values_dict = [{ a: np.pi / 4, b: np.pi }, { a: np.pi / 4, b: np.pi / 4 }, { a: np.pi / 2, b: np.pi / 4 }] correct_values = [[-1.28163104, 2.56326208, 1.06066017], [-0.04495626, -2.40716991, 1.8125], [2.82842712, -1.5, 1.76776695]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_hess.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def test_circuit_sampler(self, method): """Test the gradient with circuit sampler Tr(|psi><psi|Z) = sin(a)sin(b) Tr(|psi><psi|X) = cos(a) d<H>/da = - 0.5 sin(a) - 1 cos(a)sin(b) d<H>/db = - 1 sin(a)cos(b) """ ham = 0.5 * X - 1 * Z a = Parameter("a") b = Parameter("b") params = [a, b] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) shots = 8000 if method == "fin_diff": np.random.seed(8) state_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.0)).convert( operator=op ) else: state_grad = Gradient(grad_method=method).convert(operator=op) values_dict = [ {a: np.pi / 4, b: np.pi}, {params[0]: np.pi / 4, params[1]: np.pi / 4}, {params[0]: np.pi / 2, params[1]: np.pi / 4}, ] correct_values = [ [-0.5 / np.sqrt(2), 1 / np.sqrt(2)], [-0.5 / np.sqrt(2) - 0.5, -1 / 2.0], [-0.5, -1 / np.sqrt(2)], ] backend = BasicAer.get_backend("qasm_simulator") q_instance = QuantumInstance(backend=backend, shots=shots) for i, value_dict in enumerate(values_dict): sampler = CircuitSampler(backend=q_instance).convert( state_grad, params={k: [v] for k, v in value_dict.items()} ) np.testing.assert_array_almost_equal(sampler.eval()[0], correct_values[i], decimal=1)
def test_state_gradient1(self, method): """Test the state gradient Tr(|psi><psi|Z) = sin(a)sin(b) Tr(|psi><psi|X) = cos(a) d<H>/da = - 0.5 sin(a) - 1 cos(a)sin(b) d<H>/db = - 1 sin(a)cos(b) """ ham = 0.5 * X - 1 * Z a = Parameter("a") b = Parameter("b") params = [a, b] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) state_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [ { a: np.pi / 4, b: np.pi }, { params[0]: np.pi / 4, params[1]: np.pi / 4 }, { params[0]: np.pi / 2, params[1]: np.pi / 4 }, ] correct_values = [ [-0.5 / np.sqrt(2), 1 / np.sqrt(2)], [-0.5 / np.sqrt(2) - 0.5, -1 / 2.0], [-0.5, -1 / np.sqrt(2)], ] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def test_prob_grad(self, method): """Test the probability gradient dp0/da = cos(a)sin(b) / 2 dp1/da = - cos(a)sin(b) / 2 dp0/db = sin(a)cos(b) / 2 dp1/db = - sin(a)cos(b) / 2 """ a = Parameter("a") b = Parameter("b") params = [a, b] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = CircuitStateFn(primitive=qc, coeff=1.0) prob_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [ { a: np.pi / 4, b: 0 }, { params[0]: np.pi / 4, params[1]: np.pi / 4 }, { params[0]: np.pi / 2, params[1]: np.pi }, ] correct_values = [ [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], [[1 / 4, -1 / 4], [1 / 4, -1 / 4]], [[0, 0], [-1 / 2, 1 / 2]], ] for i, value_dict in enumerate(values_dict): for j, prob_grad_result in enumerate( prob_grad.assign_parameters(value_dict).eval()): np.testing.assert_array_almost_equal(prob_grad_result, correct_values[i][j], decimal=1)
def construct_expectation( self, parameter: Union[List[float], List[Parameter], np.ndarray], operator: OperatorBase, ) -> OperatorBase: r""" Generate the ansatz circuit and expectation value measurement, and return their runnable composition. Args: parameter: Parameters for the ansatz circuit. operator: Qubit operator of the Observable Returns: The Operator equalling the measurement of the ansatz :class:`StateFn` by the Observable's expectation :class:`StateFn`. Raises: AlgorithmError: If no operator has been provided. """ if operator is None: raise AlgorithmError("The operator was never provided.") operator = self._check_operator(operator) if isinstance(self.var_form, QuantumCircuit): param_dict = dict(zip(self._var_form_params, parameter)) # type: Dict wave_function = self.var_form.assign_parameters(param_dict) else: wave_function = self.var_form.construct_circuit(parameter) # Expectation was never created , try to create one if self._expectation is None: self._try_set_expectation_value_from_factory(operator) # If setting the expectation failed, raise an Error: if self._expectation is None: raise AlgorithmError( 'No expectation set and could not automatically set one, please ' 'try explicitly setting an expectation or specify a backend so it ' 'can be chosen automatically.') observable_meas = self.expectation.convert( StateFn(operator, is_measurement=True)) ansatz_circuit_op = CircuitStateFn(wave_function) return observable_meas.compose(ansatz_circuit_op).reduce()
def test_gradient_wrapper(self, backend_type): """Test the gradient wrapper for probability gradients dp0/da = cos(a)sin(b) / 2 dp1/da = - cos(a)sin(b) / 2 dp0/db = sin(a)cos(b) / 2 dp1/db = - sin(a)cos(b) / 2 """ method = 'param_shift' a = Parameter('a') b = Parameter('b') params = [a, b] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = CircuitStateFn(primitive=qc, coeff=1.) shots = 8000 backend = BasicAer.get_backend(backend_type) q_instance = QuantumInstance(backend=backend, shots=shots) if method == 'fin_diff': np.random.seed(8) prob_grad = Gradient(grad_method=method, epsilon=shots**(-1 / 6.)).gradient_wrapper( operator=op, bind_params=params, backend=q_instance) else: prob_grad = Gradient(grad_method=method).gradient_wrapper( operator=op, bind_params=params, backend=q_instance) values = [[np.pi / 4, 0], [np.pi / 4, np.pi / 4], [np.pi / 2, np.pi]] correct_values = [[[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], [[1 / 4, -1 / 4], [1 / 4, -1 / 4]], [[0, 0], [-1 / 2, 1 / 2]]] for i, value in enumerate(values): result = prob_grad(value) if backend_type == 'qasm_simulator': # sparse result result = [result[0].toarray(), result[1].toarray()] self.assertTrue( np.allclose(result[0], correct_values[i][0], atol=0.1)) self.assertTrue( np.allclose(result[1], correct_values[i][1], atol=0.1))
def test_operator_coefficient_hessian(self, method): """Test the operator coefficient hessian <Z> = Tr( | psi > < psi | Z) = sin(a)sin(b) <X> = Tr( | psi > < psi | X) = cos(a) d<H>/dc_0 = 2 * c_0 * <X> + c_1 * <Z> d<H>/dc_1 = c_0 * <Z> d^2<H>/dc_0^2 = 2 * <X> d^2<H>/dc_0dc_1 = <Z> d^2<H>/dc_1dc_0 = <Z> d^2<H>/dc_1^2 = 0 """ a = Parameter('a') b = Parameter('b') q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(b, q[0]) coeff_0 = Parameter('c_0') coeff_1 = Parameter('c_1') ham = coeff_0 * coeff_0 * X + coeff_1 * coeff_0 * Z op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) gradient_coeffs = [(coeff_0, coeff_0), (coeff_0, coeff_1), (coeff_1, coeff_1)] coeff_grad = Hessian(hess_method=method).convert(op, gradient_coeffs) values_dict = [{ coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi }, { coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi / 4 }] correct_values = [[2 / np.sqrt(2), 0, 0], [2 / np.sqrt(2), 1 / 2, 0]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( coeff_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def test_gradient_ryy(self, method): """Test the state gradient for YY rotation """ ham = Y ^ Y a = Parameter('a') q = QuantumRegister(2) qc = QuantumCircuit(q) qc.ryy(a, q[0], q[1]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) state_grad = Gradient(grad_method=method).convert(operator=op, params=a) values_dict = [{a: np.pi / 8}, {a: np.pi}] correct_values = [[0], [0]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal(state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def _eval_aux_ops(self, threshold=1e-12): # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(self.quantum_instance) aux_op_meas = self.expectation.convert(StateFn(ListOp(self.aux_operators), is_measurement=True)) aux_op_expect = aux_op_meas.compose(CircuitStateFn(self.get_optimal_circuit())) values = np.real(sampler.convert(aux_op_expect).eval()) # Discard values below threshold aux_op_results = (values * (np.abs(values) > threshold)) # Deal with the aux_op behavior where there can be Nones or Zero qubit Paulis in the list self._ret['aux_ops'] = [None if is_none else [result] for (is_none, result) in zip(self._aux_op_nones, aux_op_results)] # As this has mixed types, since it can included None, it needs to explicitly pass object # data type to avoid numpy 1.19 warning message about implicit conversion being deprecated self._ret['aux_ops'] = np.array([self._ret['aux_ops']], dtype=object)
def test_state_hessian(self, method): """Test the state Hessian Tr(|psi><psi|Z) = sin(a)sin(b) Tr(|psi><psi|X) = cos(a) d^2<H>/da^2 = - 0.5 cos(a) + 1 sin(a)sin(b) d^2<H>/dbda = - 1 cos(a)cos(b) d^2<H>/dbda = - 1 cos(a)cos(b) d^2<H>/db^2 = + 1 sin(a)sin(b) """ ham = 0.5 * X - 1 * Z a = Parameter('a') b = Parameter('b') params = [(a, a), (a, b), (b, b)] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(b, q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) state_hess = Hessian(hess_method=method).convert(operator=op, params=params) values_dict = [{ a: np.pi / 4, b: np.pi }, { a: np.pi / 4, b: np.pi / 4 }, { a: np.pi / 2, b: np.pi / 4 }] correct_values = [[-0.5 / np.sqrt(2), 1 / np.sqrt(2), 0], [-0.5 / np.sqrt(2) + 0.5, -1 / 2., 0.5], [1 / np.sqrt(2), 0, 1 / np.sqrt(2)]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_hess.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def test_prob_hess(self, method): """Test the probability Hessian using linear combination of unitaries method d^2p0/da^2 = - sin(a)sin(b) / 2 d^2p1/da^2 = sin(a)sin(b) / 2 d^2p0/dadb = cos(a)cos(b) / 2 d^2p1/dadb = - cos(a)cos(b) / 2 """ a = Parameter("a") b = Parameter("b") params = [(a, a), (a, b)] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(b, q[0]) op = CircuitStateFn(primitive=qc, coeff=1.0) prob_hess = Hessian(hess_method=method).convert(operator=op, params=params) values_dict = [{ a: np.pi / 4, b: 0 }, { a: np.pi / 4, b: np.pi / 4 }, { a: np.pi / 2, b: np.pi }] correct_values = [ [[0, 0], [1 / (2 * np.sqrt(2)), -1 / (2 * np.sqrt(2))]], [[-1 / 4, 1 / 4], [1 / 4, -1 / 4]], [[0, 0], [0, 0]], ] for i, value_dict in enumerate(values_dict): for j, prob_hess_result in enumerate( prob_hess.assign_parameters(value_dict).eval()): np.testing.assert_array_almost_equal(prob_hess_result, correct_values[i][j], decimal=1)
def test_circuit_sampler2(self, method): """Test the probability gradient with the circuit sampler dp0/da = cos(a)sin(b) / 2 dp1/da = - cos(a)sin(b) / 2 dp0/db = sin(a)cos(b) / 2 dp1/db = - sin(a)cos(b) / 2 """ a = Parameter('a') b = Parameter('b') params = [a, b] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = CircuitStateFn(primitive=qc, coeff=1.) shots = 8000 if method == 'fin_diff': np.random.seed(8) prob_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.)).convert( operator=op, params=params) else: prob_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [{a: [np.pi / 4], b: [0]}, {params[0]: [np.pi / 4], params[1]: [np.pi / 4]}, {params[0]: [np.pi / 2], params[1]: [np.pi]}] correct_values = [[[0, 0], [1 / (2 * np.sqrt(2)), - 1 / (2 * np.sqrt(2))]], [[1 / 4, -1 / 4], [1 / 4, - 1 / 4]], [[0, 0], [- 1 / 2, 1 / 2]]] backend = BasicAer.get_backend('qasm_simulator') q_instance = QuantumInstance(backend=backend, shots=shots) for i, value_dict in enumerate(values_dict): sampler = CircuitSampler(backend=q_instance).convert(prob_grad, params=value_dict) result = sampler.eval() np.testing.assert_array_almost_equal(result[0], correct_values[i], decimal=1)
def test_state_hessian(self, method): """Test the state Hessian Tr(|psi><psi|Z) = sin(a)sin(b) Tr(|psi><psi|X) = cos(a) d^2<H>/da^2 = - 0.5 cos(a) + 1 sin(a)sin(b) d^2<H>/dbda = - 1 cos(a)cos(b) d^2<H>/dbda = - 1 cos(a)cos(b) d^2<H>/db^2 = + 1 sin(a)sin(b) """ ham = 0.5 * X - 1 * Z params = ParameterVector("a", 2) q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) state_hess = Hessian(hess_method=method).convert(operator=op) values_dict = [ { params[0]: np.pi / 4, params[1]: np.pi }, { params[0]: np.pi / 4, params[1]: np.pi / 4 }, ] correct_values = [ [[-0.5 / np.sqrt(2), 1 / np.sqrt(2)], [1 / np.sqrt(2), 0]], [[-0.5 / np.sqrt(2) + 0.5, -1 / 2.0], [-1 / 2.0, 0.5]], ] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_hess.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def _construct_circuit(self, parameters) -> QuantumCircuit: """Construct a parameterized circuit.""" if not len(parameters) == self._num_parameters: raise ValueError( 'Incorrect number of angles: expecting {}, but {} given.'. format(self._num_parameters, len(parameters))) # local imports to avoid circular imports from qiskit.opflow import CircuitStateFn from qiskit.opflow import CircuitOp, EvolutionFactory from qiskit.opflow import OperatorBase circuit_op = CircuitStateFn(self.initial_state) # iterate over layers for idx in range(self._reps): # the first [:self._reps] parameters are used for the cost operator, # so we apply them here circuit_op = (self._cost_operator * parameters[idx]).exp_i().compose(circuit_op) mixer = self.mixer_operator if isinstance(mixer, OperatorBase): mixer = cast(OperatorBase, mixer) # we apply beta parameter in case of operator based mixer. circuit_op = ( mixer * parameters[idx + self._reps]).exp_i().compose(circuit_op) else: # mixer as a quantum circuit that can be parameterized mixer = cast(QuantumCircuit, mixer) num_params = mixer.num_parameters # the remaining [self._p:] parameters are used for the mixer, # there may be multiple layers, so parameters are grouped by layers. param_values = parameters[self._reps + num_params * idx:self._reps + num_params * (idx + 1)] param_dict = dict(zip(mixer.parameters, param_values)) mixer = mixer.assign_parameters(param_dict) circuit_op = CircuitOp(mixer).compose(circuit_op) evolution = EvolutionFactory.build(self._cost_operator) circuit_op = evolution.convert(circuit_op) return circuit_op.to_circuit()
def test_gradient_rzz(self, method): """Test the state gradient for ZZ rotation""" ham = Z ^ X a = Parameter("a") q = QuantumRegister(2) qc = QuantumCircuit(q) qc.h(q[0]) qc.rzz(a, q[0], q[1]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) params = [a] state_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [{a: np.pi / 4}, {a: np.pi / 2}] correct_values = [[-0.707], [-1.0]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1 )
def test_gradient_wrapper2(self, backend_type, atol): """Test the gradient wrapper for gradients checking that statevector and qasm gives the same results dp0/da = cos(a)sin(b) / 2 dp1/da = - cos(a)sin(b) / 2 dp0/db = sin(a)cos(b) / 2 dp1/db = - sin(a)cos(b) / 2 """ method = "lin_comb" a = Parameter("a") b = Parameter("b") params = [a, b] qc = QuantumCircuit(2) qc.h(1) qc.h(0) qc.sdg(1) qc.cz(0, 1) qc.ry(params[0], 0) qc.rz(params[1], 0) qc.h(1) obs = (Z ^ X) - (Y ^ Y) op = StateFn(obs, is_measurement=True) @ CircuitStateFn(primitive=qc) shots = 8192 if backend_type == "qasm_simulator" else 1 values = [[0, np.pi / 2], [np.pi / 4, np.pi / 4], [np.pi / 3, np.pi / 9]] correct_values = [[-4.0, 0], [-2.0, -4.82842712], [-0.68404029, -7.01396121]] for i, value in enumerate(values): backend = BasicAer.get_backend(backend_type) q_instance = QuantumInstance(backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2) grad = NaturalGradient(grad_method=method).gradient_wrapper( operator=op, bind_params=params, backend=q_instance) result = grad(value) self.assertTrue(np.allclose(result, correct_values[i], atol=atol))
def test_operator_coefficient_gradient(self, method): """Test the operator coefficient gradient Tr( | psi > < psi | Z) = sin(a)sin(b) Tr( | psi > < psi | X) = cos(a) """ a = Parameter("a") b = Parameter("b") q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(b, q[0]) coeff_0 = Parameter("c_0") coeff_1 = Parameter("c_1") ham = coeff_0 * X + coeff_1 * Z op = StateFn(ham, is_measurement=True) @ CircuitStateFn(primitive=qc, coeff=1.0) gradient_coeffs = [coeff_0, coeff_1] coeff_grad = Gradient(grad_method=method).convert(op, gradient_coeffs) values_dict = [ { coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi }, { coeff_0: 0.5, coeff_1: -1, a: np.pi / 4, b: np.pi / 4 }, ] correct_values = [[1 / np.sqrt(2), 0], [1 / np.sqrt(2), 1 / 2]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( coeff_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def gradient_function(current_point): """ Gradient function Args: current_point (np.ndarray): Current values for the variational parameters. Returns: np.ndarray: array of partial derivatives of the loss function w.r.t. the variational parameters. """ free_params = self._free_parameters generated_data, _ = self.get_output(quantum_instance, params=current_point, shots=self._shots) prediction_generated = discriminator.get_label(generated_data, detach=True) op = ~CircuitStateFn(primitive=self.generator_circuit) grad_object = gradient_object.convert(operator=op, params=free_params) value_dict = {free_params[i]: current_point[i] for i in range(len(free_params))} analytical_gradients = np.array(grad_object.assign_parameters(value_dict).eval()) loss_gradients = self.loss(prediction_generated, analytical_gradients).real return loss_gradients
def test_state_gradient5(self, method): """Test the state gradient Tr(|psi><psi|Z) = sin(a0)sin(a1) Tr(|psi><psi|X) = cos(a0) d<H>/da0 = - 0.5 sin(a0) - 1 cos(a0)sin(a1) d<H>/da1 = - 1 sin(a0)cos(a1) """ ham = 0.5 * X - 1 * Z a = ParameterVector('a', 2) params = a q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(params[0], q[0]) qc.rx(params[1], q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) state_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [{ a: [np.pi / 4, np.pi] }, { a: [np.pi / 4, np.pi / 4] }, { a: [np.pi / 2, np.pi / 4] }] correct_values = [[-0.5 / np.sqrt(2), 1 / np.sqrt(2)], [-0.5 / np.sqrt(2) - 0.5, -1 / 2.], [-0.5, -1 / np.sqrt(2)]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def test_qfi_maxcut(self): """Test the QFI for a simple MaxCut problem. This is interesting because it contains the same parameters in different gates. """ # create maxcut circuit for the hamiltonian # H = (I ^ I ^ Z ^ Z) + (I ^ Z ^ I ^ Z) + (Z ^ I ^ I ^ Z) + (I ^ Z ^ Z ^ I) x = ParameterVector("x", 2) ansatz = QuantumCircuit(4) # initial hadamard layer ansatz.h(ansatz.qubits) # e^{iZZ} layers def expiz(qubit0, qubit1): ansatz.cx(qubit0, qubit1) ansatz.rz(2 * x[0], qubit1) ansatz.cx(qubit0, qubit1) expiz(2, 1) expiz(3, 0) expiz(2, 0) expiz(1, 0) # mixer layer with RX gates for i in range(ansatz.num_qubits): ansatz.rx(2 * x[1], i) point = {x[0]: 0.4, x[1]: 0.69} # reference computed via finite difference reference = np.array([[16.0, -5.551], [-5.551, 18.497]]) # QFI from gradient framework qfi = QFI().convert(CircuitStateFn(ansatz), params=x[:]) actual = np.array(qfi.bind_parameters(point).eval()).real np.testing.assert_array_almost_equal(actual, reference, decimal=3)
def _eval_aux_ops( self, parameters: np.ndarray, aux_operators: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, ) -> ListOrDict[complex]: # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(self.quantum_instance) if isinstance(aux_operators, dict): list_op = ListOp(list(aux_operators.values())) else: list_op = ListOp(aux_operators) aux_op_meas = expectation.convert(StateFn(list_op, is_measurement=True)) aux_op_expect = aux_op_meas.compose( CircuitStateFn(self.ansatz.bind_parameters(parameters))) values = np.real(sampler.convert(aux_op_expect).eval()) # Discard values below threshold aux_op_results = values * (np.abs(values) > threshold) # Return None eigenvalues for None operators if aux_operators is a list. # None operators are already dropped in compute_minimum_eigenvalue if aux_operators is a dict. if isinstance(aux_operators, list): aux_operator_eigenvalues = [None] * len(aux_operators) key_value_iterator = enumerate(aux_op_results) else: aux_operator_eigenvalues = {} key_value_iterator = zip(aux_operators.keys(), aux_op_results) for key, value in key_value_iterator: if aux_operators[key] is not None: aux_operator_eigenvalues[key] = value return aux_operator_eigenvalues
def test_qfi_simple(self, method): """Test if the quantum fisher information calculation is correct for a simple test case. QFI = [[1, 0], [0, 1]] - [[0, 0], [0, cos^2(a)]] """ # create the circuit a, b = Parameter("a"), Parameter("b") qc = QuantumCircuit(1) qc.h(0) qc.rz(a, 0) qc.rx(b, 0) # convert the circuit to a QFI object op = CircuitStateFn(qc) qfi = QFI(qfi_method=method).convert(operator=op) # test for different values values_dict = [{a: np.pi / 4, b: 0.1}, {a: np.pi, b: 0.1}, {a: np.pi / 2, b: 0.1}] correct_values = [[[1, 0], [0, 0.5]], [[1, 0], [0, 0]], [[1, 0], [0, 1]]] for i, value_dict in enumerate(values_dict): actual = qfi.assign_parameters(value_dict).eval() np.testing.assert_array_almost_equal(actual, correct_values[i], decimal=1)
def test_gradient_p(self, method): """Test the state gradient for p |psi> = 1/sqrt(2)[[1, exp(ia)]] Tr(|psi><psi|Z) = 0 Tr(|psi><psi|X) = cos(a) d<H>/da = - 0.5 sin(a) """ ham = 0.5 * X - 1 * Z a = Parameter('a') params = a q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.p(a, q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.) state_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [{a: np.pi / 4}, {a: 0}, {a: np.pi / 2}] correct_values = [-0.5 / np.sqrt(2), 0, -0.5] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal(state_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1)
def test_compose_with_indices(self): """Test compose method using its permutation feature.""" pauli_op = X ^ Y ^ Z circuit_op = T ^ H matrix_op = (X ^ Y ^ H ^ T).to_matrix_op() evolved_op = EvolvedOp(matrix_op) # composition of PrimitiveOps num_qubits = 4 primitive_op = pauli_op @ circuit_op @ matrix_op composed_op = pauli_op @ circuit_op @ evolved_op self.assertEqual(primitive_op.num_qubits, num_qubits) self.assertEqual(composed_op.num_qubits, num_qubits) # with permutation num_qubits = 5 indices = [1, 4] permuted_primitive_op = evolved_op @ circuit_op.permute( indices) @ pauli_op @ matrix_op composed_primitive_op = (evolved_op @ pauli_op.compose( circuit_op, permutation=indices, front=True) @ matrix_op) self.assertTrue( np.allclose(permuted_primitive_op.to_matrix(), composed_primitive_op.to_matrix())) self.assertEqual(num_qubits, permuted_primitive_op.num_qubits) # ListOp num_qubits = 6 tensored_op = TensoredOp([pauli_op, circuit_op]) summed_op = pauli_op + circuit_op.permute([2, 1]) composed_op = circuit_op @ evolved_op @ matrix_op list_op = summed_op @ composed_op.compose( tensored_op, permutation=[1, 2, 3, 5, 4], front=True) self.assertEqual(num_qubits, list_op.num_qubits) num_qubits = 4 circuit_fn = CircuitStateFn(primitive=circuit_op.primitive, is_measurement=True) operator_fn = OperatorStateFn(primitive=circuit_op ^ circuit_op, is_measurement=True) no_perm_op = circuit_fn @ operator_fn self.assertEqual(no_perm_op.num_qubits, num_qubits) indices = [0, 4] perm_op = operator_fn.compose(circuit_fn, permutation=indices, front=True) self.assertEqual(perm_op.num_qubits, max(indices) + 1) # StateFn num_qubits = 3 dim = 2**num_qubits vec = [1.0 / (i + 1) for i in range(dim)] dic = { format(i, "b").zfill(num_qubits): 1.0 / (i + 1) for i in range(dim) } is_measurement = True op_state_fn = OperatorStateFn( matrix_op, is_measurement=is_measurement) # num_qubit = 4 vec_state_fn = VectorStateFn(vec, is_measurement=is_measurement) # 3 dic_state_fn = DictStateFn(dic, is_measurement=is_measurement) # 3 circ_state_fn = CircuitStateFn(circuit_op.to_circuit(), is_measurement=is_measurement) # 2 composed_op = op_state_fn @ vec_state_fn @ dic_state_fn @ circ_state_fn self.assertEqual(composed_op.num_qubits, op_state_fn.num_qubits) # with permutation perm = [2, 4, 6] composed = (op_state_fn @ dic_state_fn.compose( vec_state_fn, permutation=perm, front=True) @ circ_state_fn) self.assertEqual(composed.num_qubits, max(perm) + 1)
class TestOpConstruction(QiskitOpflowTestCase): """Operator Construction tests.""" def test_pauli_primitives(self): """from to file test""" newop = X ^ Y ^ Z ^ I self.assertEqual(newop.primitive, Pauli("XYZI")) kpower_op = (Y ^ 5) ^ (I ^ 3) self.assertEqual(kpower_op.primitive, Pauli("YYYYYIII")) kpower_op2 = (Y ^ I) ^ 4 self.assertEqual(kpower_op2.primitive, Pauli("YIYIYIYI")) # Check immutability self.assertEqual(X.primitive, Pauli("X")) self.assertEqual(Y.primitive, Pauli("Y")) self.assertEqual(Z.primitive, Pauli("Z")) self.assertEqual(I.primitive, Pauli("I")) def test_composed_eval(self): """Test eval of ComposedOp""" self.assertAlmostEqual(Minus.eval("1"), -(0.5**0.5)) def test_xz_compose_phase(self): """Test phase composition""" self.assertEqual((-1j * Y).eval("0").eval("0"), 0) self.assertEqual((-1j * Y).eval("0").eval("1"), 1) self.assertEqual((-1j * Y).eval("1").eval("0"), -1) self.assertEqual((-1j * Y).eval("1").eval("1"), 0) self.assertEqual((X @ Z).eval("0").eval("0"), 0) self.assertEqual((X @ Z).eval("0").eval("1"), 1) self.assertEqual((X @ Z).eval("1").eval("0"), -1) self.assertEqual((X @ Z).eval("1").eval("1"), 0) self.assertEqual((1j * Y).eval("0").eval("0"), 0) self.assertEqual((1j * Y).eval("0").eval("1"), -1) self.assertEqual((1j * Y).eval("1").eval("0"), 1) self.assertEqual((1j * Y).eval("1").eval("1"), 0) self.assertEqual((Z @ X).eval("0").eval("0"), 0) self.assertEqual((Z @ X).eval("0").eval("1"), -1) self.assertEqual((Z @ X).eval("1").eval("0"), 1) self.assertEqual((Z @ X).eval("1").eval("1"), 0) def test_evals(self): """evals test""" # TODO: Think about eval names self.assertEqual(Z.eval("0").eval("0"), 1) self.assertEqual(Z.eval("1").eval("0"), 0) self.assertEqual(Z.eval("0").eval("1"), 0) self.assertEqual(Z.eval("1").eval("1"), -1) self.assertEqual(X.eval("0").eval("0"), 0) self.assertEqual(X.eval("1").eval("0"), 1) self.assertEqual(X.eval("0").eval("1"), 1) self.assertEqual(X.eval("1").eval("1"), 0) self.assertEqual(Y.eval("0").eval("0"), 0) self.assertEqual(Y.eval("1").eval("0"), -1j) self.assertEqual(Y.eval("0").eval("1"), 1j) self.assertEqual(Y.eval("1").eval("1"), 0) with self.assertRaises(ValueError): Y.eval("11") with self.assertRaises(ValueError): (X ^ Y).eval("1111") with self.assertRaises(ValueError): Y.eval((X ^ X).to_matrix_op()) # Check that Pauli logic eval returns same as matrix logic self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("0").eval("0"), 1) self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("1").eval("0"), 0) self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("0").eval("1"), 0) self.assertEqual(PrimitiveOp(Z.to_matrix()).eval("1").eval("1"), -1) self.assertEqual(PrimitiveOp(X.to_matrix()).eval("0").eval("0"), 0) self.assertEqual(PrimitiveOp(X.to_matrix()).eval("1").eval("0"), 1) self.assertEqual(PrimitiveOp(X.to_matrix()).eval("0").eval("1"), 1) self.assertEqual(PrimitiveOp(X.to_matrix()).eval("1").eval("1"), 0) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("0").eval("0"), 0) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("1").eval("0"), -1j) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("0").eval("1"), 1j) self.assertEqual(PrimitiveOp(Y.to_matrix()).eval("1").eval("1"), 0) pauli_op = Z ^ I ^ X ^ Y mat_op = PrimitiveOp(pauli_op.to_matrix()) full_basis = list( map("".join, itertools.product("01", repeat=pauli_op.num_qubits))) for bstr1, bstr2 in itertools.product(full_basis, full_basis): # print('{} {} {} {}'.format(bstr1, bstr2, pauli_op.eval(bstr1, bstr2), # mat_op.eval(bstr1, bstr2))) np.testing.assert_array_almost_equal( pauli_op.eval(bstr1).eval(bstr2), mat_op.eval(bstr1).eval(bstr2)) gnarly_op = SummedOp( [ (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(Z), PrimitiveOp(Operator.from_label("+r0I")), 3 * (X ^ CX ^ T), ], coeff=3 + 0.2j, ) gnarly_mat_op = PrimitiveOp(gnarly_op.to_matrix()) full_basis = list( map("".join, itertools.product("01", repeat=gnarly_op.num_qubits))) for bstr1, bstr2 in itertools.product(full_basis, full_basis): np.testing.assert_array_almost_equal( gnarly_op.eval(bstr1).eval(bstr2), gnarly_mat_op.eval(bstr1).eval(bstr2)) def test_circuit_construction(self): """circuit construction test""" hadq2 = H ^ I cz = hadq2.compose(CX).compose(hadq2) qc = QuantumCircuit(2) qc.append(cz.primitive, qargs=range(2)) ref_cz_mat = PrimitiveOp(CZGate()).to_matrix() np.testing.assert_array_almost_equal(cz.to_matrix(), ref_cz_mat) def test_io_consistency(self): """consistency test""" new_op = X ^ Y ^ I label = "XYI" # label = new_op.primitive.to_label() self.assertEqual(str(new_op.primitive), label) np.testing.assert_array_almost_equal(new_op.primitive.to_matrix(), Operator.from_label(label).data) self.assertEqual(new_op.primitive, Pauli(label)) x_mat = X.primitive.to_matrix() y_mat = Y.primitive.to_matrix() i_mat = np.eye(2, 2) np.testing.assert_array_almost_equal( new_op.primitive.to_matrix(), np.kron(np.kron(x_mat, y_mat), i_mat)) hi = np.kron(H.to_matrix(), I.to_matrix()) hi2 = Operator.from_label("HI").data hi3 = (H ^ I).to_matrix() np.testing.assert_array_almost_equal(hi, hi2) np.testing.assert_array_almost_equal(hi2, hi3) xy = np.kron(X.to_matrix(), Y.to_matrix()) xy2 = Operator.from_label("XY").data xy3 = (X ^ Y).to_matrix() np.testing.assert_array_almost_equal(xy, xy2) np.testing.assert_array_almost_equal(xy2, xy3) # Check if numpy array instantiation is the same as from Operator matrix_op = Operator.from_label("+r") np.testing.assert_array_almost_equal( PrimitiveOp(matrix_op).to_matrix(), PrimitiveOp(matrix_op.data).to_matrix()) # Ditto list of lists np.testing.assert_array_almost_equal( PrimitiveOp(matrix_op.data.tolist()).to_matrix(), PrimitiveOp(matrix_op.data).to_matrix(), ) # TODO make sure this works once we resolve endianness mayhem # qc = QuantumCircuit(3) # qc.x(2) # qc.y(1) # from qiskit import BasicAer, QuantumCircuit, execute # unitary = execute(qc, BasicAer.get_backend('unitary_simulator')).result().get_unitary() # np.testing.assert_array_almost_equal(new_op.primitive.to_matrix(), unitary) def test_to_matrix(self): """to matrix text""" np.testing.assert_array_equal(X.to_matrix(), Operator.from_label("X").data) np.testing.assert_array_equal(Y.to_matrix(), Operator.from_label("Y").data) np.testing.assert_array_equal(Z.to_matrix(), Operator.from_label("Z").data) op1 = Y + H np.testing.assert_array_almost_equal(op1.to_matrix(), Y.to_matrix() + H.to_matrix()) op2 = op1 * 0.5 np.testing.assert_array_almost_equal(op2.to_matrix(), op1.to_matrix() * 0.5) op3 = (4 - 0.6j) * op2 np.testing.assert_array_almost_equal(op3.to_matrix(), op2.to_matrix() * (4 - 0.6j)) op4 = op3.tensor(X) np.testing.assert_array_almost_equal( op4.to_matrix(), np.kron(op3.to_matrix(), X.to_matrix())) op5 = op4.compose(H ^ I) np.testing.assert_array_almost_equal( op5.to_matrix(), np.dot(op4.to_matrix(), (H ^ I).to_matrix())) op6 = op5 + PrimitiveOp(Operator.from_label("+r").data) np.testing.assert_array_almost_equal( op6.to_matrix(), op5.to_matrix() + Operator.from_label("+r").data) param = Parameter("α") m = np.array([[0, -1j], [1j, 0]]) op7 = MatrixOp(m, param) np.testing.assert_array_equal(op7.to_matrix(), m * param) param = Parameter("β") op8 = PauliOp(primitive=Pauli("Y"), coeff=param) np.testing.assert_array_equal(op8.to_matrix(), m * param) param = Parameter("γ") qc = QuantumCircuit(1) qc.h(0) op9 = CircuitOp(qc, coeff=param) m = np.array([[1, 1], [1, -1]]) / np.sqrt(2) np.testing.assert_array_equal(op9.to_matrix(), m * param) def test_circuit_op_to_matrix(self): """test CircuitOp.to_matrix""" qc = QuantumCircuit(1) qc.rz(1.0, 0) qcop = CircuitOp(qc) np.testing.assert_array_almost_equal( qcop.to_matrix(), scipy.linalg.expm(-0.5j * Z.to_matrix())) def test_matrix_to_instruction(self): """Test MatrixOp.to_instruction yields an Instruction object.""" matop = (H ^ 3).to_matrix_op() with self.subTest("assert to_instruction returns Instruction"): self.assertIsInstance(matop.to_instruction(), Instruction) matop = ((H ^ 3) + (Z ^ 3)).to_matrix_op() with self.subTest("matrix operator is not unitary"): with self.assertRaises(ExtensionError): matop.to_instruction() def test_adjoint(self): """adjoint test""" gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + PrimitiveOp( Operator.from_label("+r0IX").data) np.testing.assert_array_almost_equal( np.conj(np.transpose(gnarly_op.to_matrix())), gnarly_op.adjoint().to_matrix()) def test_primitive_strings(self): """get primitives test""" self.assertEqual(X.primitive_strings(), {"Pauli"}) gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + PrimitiveOp( Operator.from_label("+r0IX").data) self.assertEqual(gnarly_op.primitive_strings(), {"QuantumCircuit", "Matrix"}) def test_to_pauli_op(self): """Test to_pauli_op method""" gnarly_op = 3 * (H ^ I ^ Y).compose(X ^ X ^ Z).tensor(T ^ Z) + PrimitiveOp( Operator.from_label("+r0IX").data) mat_op = gnarly_op.to_matrix_op() pauli_op = gnarly_op.to_pauli_op() self.assertIsInstance(pauli_op, SummedOp) for p in pauli_op: self.assertIsInstance(p, PauliOp) np.testing.assert_array_almost_equal(mat_op.to_matrix(), pauli_op.to_matrix()) def test_circuit_permute(self): r"""Test the CircuitOp's .permute method""" perm = range(7)[::-1] c_op = (((CX ^ 3) ^ X) @ (H ^ 7) @ (X ^ Y ^ Z ^ I ^ X ^ X ^ X) @ (Y ^ (CX ^ 3)) @ (X ^ Y ^ Z ^ I ^ X ^ X ^ X)) c_op_perm = c_op.permute(perm) self.assertNotEqual(c_op, c_op_perm) c_op_id = c_op_perm.permute(perm) self.assertEqual(c_op, c_op_id) def test_summed_op_reduce(self): """Test SummedOp""" sum_op = (X ^ X * 2) + (Y ^ Y) # type: PauliSumOp sum_op = sum_op.to_pauli_op() # type: SummedOp[PauliOp] with self.subTest("SummedOp test 1"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [2, 1]) sum_op = (X ^ X * 2) + (Y ^ Y) sum_op += Y ^ Y sum_op = sum_op.to_pauli_op() # type: SummedOp[PauliOp] with self.subTest("SummedOp test 2-a"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [2, 1, 1]) sum_op = sum_op.collapse_summands() with self.subTest("SummedOp test 2-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [2, 2]) sum_op = (X ^ X * 2) + (Y ^ Y) sum_op += (Y ^ Y) + (X ^ X * 2) sum_op = sum_op.to_pauli_op() # type: SummedOp[PauliOp] with self.subTest("SummedOp test 3-a"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "YY", "XX"]) self.assertListEqual([op.coeff for op in sum_op], [2, 1, 1, 2]) sum_op = sum_op.reduce().to_pauli_op() with self.subTest("SummedOp test 3-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [4, 2]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) with self.subTest("SummedOp test 4-a"): self.assertEqual(sum_op.coeff, 2) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [2, 1]) sum_op = sum_op.collapse_summands() with self.subTest("SummedOp test 4-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [4, 2]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) sum_op += Y ^ Y with self.subTest("SummedOp test 5-a"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 1]) sum_op = sum_op.collapse_summands() with self.subTest("SummedOp test 5-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [4, 3]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) sum_op += ((X ^ X) * 2 + (Y ^ Y)).to_pauli_op() with self.subTest("SummedOp test 6-a"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 2, 1]) sum_op = sum_op.collapse_summands() with self.subTest("SummedOp test 6-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [6, 3]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) sum_op += sum_op with self.subTest("SummedOp test 7-a"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 4, 2]) sum_op = sum_op.collapse_summands() with self.subTest("SummedOp test 7-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY"]) self.assertListEqual([op.coeff for op in sum_op], [8, 4]) sum_op = SummedOp([X ^ X * 2, Y ^ Y], 2) + SummedOp([X ^ X * 2, Z ^ Z], 3) with self.subTest("SummedOp test 8-a"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "XX", "ZZ"]) self.assertListEqual([op.coeff for op in sum_op], [4, 2, 6, 3]) sum_op = sum_op.collapse_summands() with self.subTest("SummedOp test 8-b"): self.assertEqual(sum_op.coeff, 1) self.assertListEqual([str(op.primitive) for op in sum_op], ["XX", "YY", "ZZ"]) self.assertListEqual([op.coeff for op in sum_op], [10, 2, 3]) sum_op = SummedOp([]) with self.subTest("SummedOp test 9"): self.assertEqual(sum_op.reduce(), sum_op) sum_op = ((Z + I) ^ Z) + (Z ^ X) with self.subTest("SummedOp test 10"): expected = SummedOp([ PauliOp(Pauli("ZZ")), PauliOp(Pauli("IZ")), PauliOp(Pauli("ZX")) ]) self.assertEqual(sum_op.to_pauli_op(), expected) def test_compose_op_of_different_dim(self): """ Test if smaller operator expands to correct dim when composed with bigger operator. Test if PrimitiveOps compose methods are consistent. """ # PauliOps of different dim xy_p = X ^ Y xyz_p = X ^ Y ^ Z pauli_op = xy_p @ xyz_p expected_result = I ^ I ^ Z self.assertEqual(pauli_op, expected_result) # MatrixOps of different dim xy_m = xy_p.to_matrix_op() xyz_m = xyz_p.to_matrix_op() matrix_op = xy_m @ xyz_m self.assertEqual(matrix_op, expected_result.to_matrix_op()) # CircuitOps of different dim xy_c = xy_p.to_circuit_op() xyz_c = xyz_p.to_circuit_op() circuit_op = xy_c @ xyz_c self.assertTrue( np.array_equal(pauli_op.to_matrix(), matrix_op.to_matrix())) self.assertTrue( np.allclose(pauli_op.to_matrix(), circuit_op.to_matrix(), rtol=1e-14)) self.assertTrue( np.allclose(matrix_op.to_matrix(), circuit_op.to_matrix(), rtol=1e-14)) def test_permute_on_primitive_op(self): """Test if permute methods of PrimitiveOps are consistent and work as expected.""" indices = [1, 2, 4] # PauliOp pauli_op = X ^ Y ^ Z permuted_pauli_op = pauli_op.permute(indices) expected_pauli_op = X ^ I ^ Y ^ Z ^ I self.assertEqual(permuted_pauli_op, expected_pauli_op) # CircuitOp circuit_op = pauli_op.to_circuit_op() permuted_circuit_op = circuit_op.permute(indices) expected_circuit_op = expected_pauli_op.to_circuit_op() self.assertEqual(Operator(permuted_circuit_op.primitive), Operator(expected_circuit_op.primitive)) # MatrixOp matrix_op = pauli_op.to_matrix_op() permuted_matrix_op = matrix_op.permute(indices) expected_matrix_op = expected_pauli_op.to_matrix_op() equal = np.allclose(permuted_matrix_op.to_matrix(), expected_matrix_op.to_matrix()) self.assertTrue(equal) def test_permute_on_list_op(self): """Test if ListOp permute method is consistent with PrimitiveOps permute methods.""" op1 = (X ^ Y ^ Z).to_circuit_op() op2 = Z ^ X ^ Y # ComposedOp indices = [1, 2, 0] primitive_op = op1 @ op2 primitive_op_perm = primitive_op.permute(indices) # CircuitOp.permute composed_op = ComposedOp([op1, op2]) composed_op_perm = composed_op.permute(indices) # reduce the ListOp to PrimitiveOp to_primitive = composed_op_perm.oplist[0] @ composed_op_perm.oplist[1] # compare resulting PrimitiveOps equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) self.assertTrue(equal) # TensoredOp indices = [3, 5, 4, 0, 2, 1] primitive_op = op1 ^ op2 primitive_op_perm = primitive_op.permute(indices) tensored_op = TensoredOp([op1, op2]) tensored_op_perm = tensored_op.permute(indices) # reduce the ListOp to PrimitiveOp composed_oplist = tensored_op_perm.oplist to_primitive = (composed_oplist[0] @ (composed_oplist[1].oplist[0] ^ composed_oplist[1].oplist[1]) @ composed_oplist[2]) # compare resulting PrimitiveOps equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) self.assertTrue(equal) # SummedOp primitive_op = X ^ Y ^ Z summed_op = SummedOp([primitive_op]) indices = [1, 2, 0] primitive_op_perm = primitive_op.permute(indices) # PauliOp.permute summed_op_perm = summed_op.permute(indices) # reduce the ListOp to PrimitiveOp to_primitive = summed_op_perm.oplist[ 0] @ primitive_op @ summed_op_perm.oplist[2] # compare resulting PrimitiveOps equal = np.allclose(primitive_op_perm.to_matrix(), to_primitive.to_matrix()) self.assertTrue(equal) def test_expand_on_list_op(self): """Test if expanded ListOp has expected num_qubits.""" add_qubits = 3 # ComposedOp composed_op = ComposedOp([(X ^ Y ^ Z), (H ^ T), (Z ^ X ^ Y ^ Z).to_matrix_op()]) expanded = composed_op._expand_dim(add_qubits) self.assertEqual(composed_op.num_qubits + add_qubits, expanded.num_qubits) # TensoredOp tensored_op = TensoredOp([(X ^ Y), (Z ^ I)]) expanded = tensored_op._expand_dim(add_qubits) self.assertEqual(tensored_op.num_qubits + add_qubits, expanded.num_qubits) # SummedOp summed_op = SummedOp([(X ^ Y), (Z ^ I ^ Z)]) expanded = summed_op._expand_dim(add_qubits) self.assertEqual(summed_op.num_qubits + add_qubits, expanded.num_qubits) def test_expand_on_state_fn(self): """Test if expanded StateFn has expected num_qubits.""" num_qubits = 3 add_qubits = 2 # case CircuitStateFn, with primitive QuantumCircuit qc2 = QuantumCircuit(num_qubits) qc2.cx(0, 1) cfn = CircuitStateFn(qc2, is_measurement=True) cfn_exp = cfn._expand_dim(add_qubits) self.assertEqual(cfn_exp.num_qubits, add_qubits + num_qubits) # case OperatorStateFn, with OperatorBase primitive, in our case CircuitStateFn osfn = OperatorStateFn(cfn) osfn_exp = osfn._expand_dim(add_qubits) self.assertEqual(osfn_exp.num_qubits, add_qubits + num_qubits) # case DictStateFn dsfn = DictStateFn("1" * num_qubits, is_measurement=True) self.assertEqual(dsfn.num_qubits, num_qubits) dsfn_exp = dsfn._expand_dim(add_qubits) self.assertEqual(dsfn_exp.num_qubits, num_qubits + add_qubits) # case VectorStateFn vsfn = VectorStateFn(np.ones(2**num_qubits, dtype=complex)) self.assertEqual(vsfn.num_qubits, num_qubits) vsfn_exp = vsfn._expand_dim(add_qubits) self.assertEqual(vsfn_exp.num_qubits, num_qubits + add_qubits) def test_permute_on_state_fn(self): """Test if StateFns permute are consistent.""" num_qubits = 4 dim = 2**num_qubits primitive_list = [1.0 / (i + 1) for i in range(dim)] primitive_dict = { format(i, "b").zfill(num_qubits): 1.0 / (i + 1) for i in range(dim) } dict_fn = DictStateFn(primitive=primitive_dict, is_measurement=True) vec_fn = VectorStateFn(primitive=primitive_list, is_measurement=True) # check if dict_fn and vec_fn are equivalent equivalent = np.allclose(dict_fn.to_matrix(), vec_fn.to_matrix()) self.assertTrue(equivalent) # permute indices = [2, 3, 0, 1] permute_dict = dict_fn.permute(indices) permute_vect = vec_fn.permute(indices) equivalent = np.allclose(permute_dict.to_matrix(), permute_vect.to_matrix()) self.assertTrue(equivalent) def test_compose_consistency(self): """Test if PrimitiveOp @ ComposedOp is consistent with ComposedOp @ PrimitiveOp.""" # PauliOp op1 = X ^ Y ^ Z op2 = X ^ Y ^ Z op3 = (X ^ Y ^ Z).to_circuit_op() comp1 = op1 @ ComposedOp([op2, op3]) comp2 = ComposedOp([op3, op2]) @ op1 self.assertListEqual(comp1.oplist, list(reversed(comp2.oplist))) # CircitOp op1 = op1.to_circuit_op() op2 = op2.to_circuit_op() op3 = op3.to_matrix_op() comp1 = op1 @ ComposedOp([op2, op3]) comp2 = ComposedOp([op3, op2]) @ op1 self.assertListEqual(comp1.oplist, list(reversed(comp2.oplist))) # MatrixOp op1 = op1.to_matrix_op() op2 = op2.to_matrix_op() op3 = op3.to_pauli_op() comp1 = op1 @ ComposedOp([op2, op3]) comp2 = ComposedOp([op3, op2]) @ op1 self.assertListEqual(comp1.oplist, list(reversed(comp2.oplist))) def test_compose_with_indices(self): """Test compose method using its permutation feature.""" pauli_op = X ^ Y ^ Z circuit_op = T ^ H matrix_op = (X ^ Y ^ H ^ T).to_matrix_op() evolved_op = EvolvedOp(matrix_op) # composition of PrimitiveOps num_qubits = 4 primitive_op = pauli_op @ circuit_op @ matrix_op composed_op = pauli_op @ circuit_op @ evolved_op self.assertEqual(primitive_op.num_qubits, num_qubits) self.assertEqual(composed_op.num_qubits, num_qubits) # with permutation num_qubits = 5 indices = [1, 4] permuted_primitive_op = evolved_op @ circuit_op.permute( indices) @ pauli_op @ matrix_op composed_primitive_op = (evolved_op @ pauli_op.compose( circuit_op, permutation=indices, front=True) @ matrix_op) self.assertTrue( np.allclose(permuted_primitive_op.to_matrix(), composed_primitive_op.to_matrix())) self.assertEqual(num_qubits, permuted_primitive_op.num_qubits) # ListOp num_qubits = 6 tensored_op = TensoredOp([pauli_op, circuit_op]) summed_op = pauli_op + circuit_op.permute([2, 1]) composed_op = circuit_op @ evolved_op @ matrix_op list_op = summed_op @ composed_op.compose( tensored_op, permutation=[1, 2, 3, 5, 4], front=True) self.assertEqual(num_qubits, list_op.num_qubits) num_qubits = 4 circuit_fn = CircuitStateFn(primitive=circuit_op.primitive, is_measurement=True) operator_fn = OperatorStateFn(primitive=circuit_op ^ circuit_op, is_measurement=True) no_perm_op = circuit_fn @ operator_fn self.assertEqual(no_perm_op.num_qubits, num_qubits) indices = [0, 4] perm_op = operator_fn.compose(circuit_fn, permutation=indices, front=True) self.assertEqual(perm_op.num_qubits, max(indices) + 1) # StateFn num_qubits = 3 dim = 2**num_qubits vec = [1.0 / (i + 1) for i in range(dim)] dic = { format(i, "b").zfill(num_qubits): 1.0 / (i + 1) for i in range(dim) } is_measurement = True op_state_fn = OperatorStateFn( matrix_op, is_measurement=is_measurement) # num_qubit = 4 vec_state_fn = VectorStateFn(vec, is_measurement=is_measurement) # 3 dic_state_fn = DictStateFn(dic, is_measurement=is_measurement) # 3 circ_state_fn = CircuitStateFn(circuit_op.to_circuit(), is_measurement=is_measurement) # 2 composed_op = op_state_fn @ vec_state_fn @ dic_state_fn @ circ_state_fn self.assertEqual(composed_op.num_qubits, op_state_fn.num_qubits) # with permutation perm = [2, 4, 6] composed = (op_state_fn @ dic_state_fn.compose( vec_state_fn, permutation=perm, front=True) @ circ_state_fn) self.assertEqual(composed.num_qubits, max(perm) + 1) def test_summed_op_equals(self): """Test corner cases of SummedOp's equals function.""" with self.subTest("multiplicative factor"): self.assertEqual(2 * X, X + X) with self.subTest("commutative"): self.assertEqual(X + Z, Z + X) with self.subTest("circuit and paulis"): z = CircuitOp(ZGate()) self.assertEqual(Z + z, z + Z) with self.subTest("matrix op and paulis"): z = MatrixOp([[1, 0], [0, -1]]) self.assertEqual(Z + z, z + Z) with self.subTest("matrix multiplicative"): z = MatrixOp([[1, 0], [0, -1]]) self.assertEqual(2 * z, z + z) with self.subTest("parameter coefficients"): expr = Parameter("theta") z = MatrixOp([[1, 0], [0, -1]]) self.assertEqual(expr * z, expr * z) with self.subTest("different coefficient types"): expr = Parameter("theta") z = MatrixOp([[1, 0], [0, -1]]) self.assertNotEqual(expr * z, 2 * z) with self.subTest("additions aggregation"): z = MatrixOp([[1, 0], [0, -1]]) a = z + z + Z b = 2 * z + Z c = z + Z + z self.assertEqual(a, b) self.assertEqual(b, c) self.assertEqual(a, c) def test_circuit_compose_register_independent(self): """Test that CircuitOp uses combines circuits independent of the register. I.e. that is uses ``QuantumCircuit.compose`` over ``combine`` or ``extend``. """ op = Z ^ 2 qr = QuantumRegister(2, "my_qr") circuit = QuantumCircuit(qr) composed = op.compose(CircuitOp(circuit)) self.assertEqual(composed.num_qubits, 2) def test_matrix_op_conversions(self): """Test to reveal QiskitError when to_instruction or to_circuit method is called on parameterized matrix op.""" m = np.array([[0, 0, 1, 0], [0, 0, 0, -1], [1, 0, 0, 0], [0, -1, 0, 0]]) matrix_op = MatrixOp(m, Parameter("beta")) for method in ["to_instruction", "to_circuit"]: with self.subTest(method): # QiskitError: multiplication of Operator with ParameterExpression isn't implemented self.assertRaises(QiskitError, getattr(matrix_op, method)) def test_list_op_to_circuit(self): """Test if unitary ListOps transpile to circuit.""" # generate unitary matrices of dimension 2,4,8, seed is fixed np.random.seed(233423) u2 = unitary_group.rvs(2) u4 = unitary_group.rvs(4) u8 = unitary_group.rvs(8) # pauli matrices as numpy.arrays x = np.array([[0.0, 1.0], [1.0, 0.0]]) y = np.array([[0.0, -1.0j], [1.0j, 0.0]]) z = np.array([[1.0, 0.0], [0.0, -1.0]]) # create MatrixOp and CircuitOp out of matrices op2 = MatrixOp(u2) op4 = MatrixOp(u4) op8 = MatrixOp(u8) c2 = op2.to_circuit_op() # algorithm using only matrix operations on numpy.arrays xu4 = np.kron(x, u4) zc2 = np.kron(z, u2) zc2y = np.kron(zc2, y) matrix = np.matmul(xu4, zc2y) matrix = np.matmul(matrix, u8) matrix = np.kron(matrix, u2) operator = Operator(matrix) # same algorithm as above, but using PrimitiveOps list_op = ((X ^ op4) @ (Z ^ c2 ^ Y) @ op8) ^ op2 circuit = list_op.to_circuit() # verify that ListOp.to_circuit() outputs correct quantum circuit self.assertTrue(operator.equiv(circuit), "ListOp.to_circuit() outputs wrong circuit!") def test_composed_op_to_circuit(self): """ Test if unitary ComposedOp transpile to circuit and represents expected operator. Test if to_circuit on non-unitary ListOp raises exception. """ x = np.array([[0.0, 1.0], [1.0, 0.0]]) # Pauli X as numpy array y = np.array([[0.0, -1.0j], [1.0j, 0.0]]) # Pauli Y as numpy array m1 = np.array([[0, 0, 1, 0], [0, 0, 0, -1], [0, 0, 0, 0], [0, 0, 0, 0]]) # non-unitary m2 = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0], [0, -1, 0, 0]]) # non-unitary m_op1 = MatrixOp(m1) m_op2 = MatrixOp(m2) pm1 = (X ^ Y) ^ m_op1 # non-unitary TensoredOp pm2 = (X ^ Y) ^ m_op2 # non-unitary TensoredOp self.assertRaises(ExtensionError, pm1.to_circuit) self.assertRaises(ExtensionError, pm2.to_circuit) summed_op = pm1 + pm2 # unitary SummedOp([TensoredOp, TensoredOp]) circuit = summed_op.to_circuit( ) # should transpile without any exception # same algorithm that leads to summed_op above, but using only arrays and matrix operations unitary = np.kron(np.kron(x, y), m1 + m2) self.assertTrue(Operator(unitary).equiv(circuit)) def test_pauli_op_to_circuit(self): """Test PauliOp.to_circuit()""" with self.subTest("single Pauli"): pauli = PauliOp(Pauli("Y")) expected = QuantumCircuit(1) expected.y(0) self.assertEqual(pauli.to_circuit(), expected) with self.subTest("single Pauli with phase"): pauli = PauliOp(Pauli("-iX")) expected = QuantumCircuit(1) expected.x(0) expected.global_phase = -pi / 2 self.assertEqual(Operator(pauli.to_circuit()), Operator(expected)) with self.subTest("two qubit"): pauli = PauliOp(Pauli("IX")) expected = QuantumCircuit(2) expected.pauli("IX", range(2)) self.assertEqual(pauli.to_circuit(), expected) expected = QuantumCircuit(2) expected.x(0) expected.id(1) self.assertEqual(pauli.to_circuit().decompose(), expected) with self.subTest("two qubit with phase"): pauli = PauliOp(Pauli("iXZ")) expected = QuantumCircuit(2) expected.pauli("XZ", range(2)) expected.global_phase = pi / 2 self.assertEqual(pauli.to_circuit(), expected) expected = QuantumCircuit(2) expected.z(0) expected.x(1) expected.global_phase = pi / 2 self.assertEqual(pauli.to_circuit().decompose(), expected) def test_op_to_circuit_with_parameters(self): """On parameterized SummedOp, to_matrix_op returns ListOp, instead of MatrixOp. To avoid the infinite recursion, OpflowError is raised.""" m1 = np.array([[0, 0, 1, 0], [0, 0, 0, -1], [0, 0, 0, 0], [0, 0, 0, 0]]) # non-unitary m2 = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0], [0, -1, 0, 0]]) # non-unitary op1_with_param = MatrixOp(m1, Parameter("alpha")) # non-unitary op2_with_param = MatrixOp(m2, Parameter("beta")) # non-unitary summed_op_with_param = op1_with_param + op2_with_param # unitary # should raise OpflowError error self.assertRaises(OpflowError, summed_op_with_param.to_circuit) def test_permute_list_op_with_inconsistent_num_qubits(self): """Test if permute raises error if ListOp contains operators with different num_qubits.""" list_op = ListOp([X, X ^ X]) self.assertRaises(OpflowError, list_op.permute, [0, 1]) @data(Z, CircuitOp(ZGate()), MatrixOp([[1, 0], [0, -1]])) def test_op_indent(self, op): """Test that indentation correctly adds INDENTATION at the beginning of each line""" initial_str = str(op) indented_str = op._indent(initial_str) starts_with_indent = indented_str.startswith(op.INDENTATION) self.assertTrue(starts_with_indent) indented_str_content = ( indented_str[len(op.INDENTATION):]).split(f"\n{op.INDENTATION}") self.assertListEqual(indented_str_content, initial_str.split("\n")) def test_composed_op_immutable_under_eval(self): """Test ``ComposedOp.eval`` does not change the operator instance.""" op = 2 * ComposedOp([X]) _ = op.eval() # previous bug: after op.eval(), op was 2 * ComposedOp([2 * X]) self.assertEqual(op, 2 * ComposedOp([X])) def test_op_parameters(self): """Test that Parameters are stored correctly""" phi = Parameter("φ") theta = ParameterVector(name="θ", length=2) qc = QuantumCircuit(2) qc.rz(phi, 0) qc.rz(phi, 1) for i in range(2): qc.rx(theta[i], i) qc.h(0) qc.x(1) l = Parameter("λ") op = PrimitiveOp(qc, coeff=l) params = {phi, l, *theta.params} self.assertEqual(params, op.parameters) self.assertEqual(params, StateFn(op).parameters) self.assertEqual(params, StateFn(qc, coeff=l).parameters) def test_list_op_parameters(self): """Test that Parameters are stored correctly in a List Operator""" lam = Parameter("λ") phi = Parameter("φ") omega = Parameter("ω") mat_op = PrimitiveOp([[0, 1], [1, 0]], coeff=omega) qc = QuantumCircuit(1) qc.rx(phi, 0) qc_op = PrimitiveOp(qc) op1 = SummedOp([mat_op, qc_op]) params = [phi, omega] self.assertEqual(op1.parameters, set(params)) # check list nesting case op2 = PrimitiveOp([[1, 0], [0, -1]], coeff=lam) list_op = ListOp([op1, op2]) params.append(lam) self.assertEqual(list_op.parameters, set(params)) @data( VectorStateFn([1, 0]), CircuitStateFn(QuantumCircuit(1)), OperatorStateFn(I), OperatorStateFn(MatrixOp([[1, 0], [0, 1]])), OperatorStateFn(CircuitOp(QuantumCircuit(1))), ) def test_statefn_eval(self, op): """Test calling eval on StateFn returns the statevector.""" expected = Statevector([1, 0]) self.assertEqual(op.eval().primitive, expected) def test_sparse_eval(self): """Test calling eval on a DictStateFn returns a sparse statevector.""" op = DictStateFn({"0": 1}) expected = scipy.sparse.csr_matrix([[1, 0]]) self.assertFalse((op.eval().primitive != expected).toarray().any()) def test_sparse_to_dict(self): """Test converting a sparse vector state function to a dict state function.""" isqrt2 = 1 / np.sqrt(2) sparse = scipy.sparse.csr_matrix([[0, isqrt2, 0, isqrt2]]) sparse_fn = SparseVectorStateFn(sparse) dict_fn = DictStateFn({"01": isqrt2, "11": isqrt2}) with self.subTest("sparse to dict"): self.assertEqual(dict_fn, sparse_fn.to_dict_fn()) with self.subTest("dict to sparse"): self.assertEqual(dict_fn.to_spmatrix_op(), sparse_fn) def test_to_circuit_op(self): """Test to_circuit_op method.""" vector = np.array([2, 2]) vsfn = VectorStateFn([1, 1], coeff=2) dsfn = DictStateFn({"0": 1, "1": 1}, coeff=2) for sfn in [vsfn, dsfn]: np.testing.assert_array_almost_equal( sfn.to_circuit_op().eval().primitive.data, vector) def test_invalid_primitive(self): """Test invalid MatrixOp construction""" msg = ("MatrixOp can only be instantiated with " "['list', 'ndarray', 'spmatrix', 'Operator'], not ") with self.assertRaises(TypeError) as cm: _ = MatrixOp("invalid") self.assertEqual(str(cm.exception), msg + "'str'") with self.assertRaises(TypeError) as cm: _ = MatrixOp(None) self.assertEqual(str(cm.exception), msg + "'NoneType'") with self.assertRaises(TypeError) as cm: _ = MatrixOp(2.0) self.assertEqual(str(cm.exception), msg + "'float'") def test_summedop_equals(self): """Test SummedOp.equals""" ops = [Z, CircuitOp(ZGate()), MatrixOp([[1, 0], [0, -1]]), Zero, Minus] sum_op = sum(ops + [ListOp(ops)]) self.assertEqual(sum_op, sum_op) self.assertEqual(sum_op + sum_op, 2 * sum_op) self.assertEqual(sum_op + sum_op + sum_op, 3 * sum_op) ops2 = [Z, CircuitOp(ZGate()), MatrixOp([[1, 0], [0, 1]]), Zero, Minus] sum_op2 = sum(ops2 + [ListOp(ops)]) self.assertNotEqual(sum_op, sum_op2) self.assertEqual(sum_op2, sum_op2) sum_op3 = sum(ops) self.assertNotEqual(sum_op, sum_op3) self.assertNotEqual(sum_op2, sum_op3) self.assertEqual(sum_op3, sum_op3) def test_empty_listops(self): """Test reduce and eval on ListOp with empty oplist.""" with self.subTest("reduce empty ComposedOp "): self.assertEqual(ComposedOp([]).reduce(), ComposedOp([])) with self.subTest("reduce empty TensoredOp "): self.assertEqual(TensoredOp([]).reduce(), TensoredOp([])) with self.subTest("eval empty ComposedOp "): self.assertEqual(ComposedOp([]).eval(), 0.0) with self.subTest("eval empty TensoredOp "): self.assertEqual(TensoredOp([]).eval(), 0.0)
def compute_eigenvalues( self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None ) -> EigensolverResult: super().compute_eigenvalues(operator, aux_operators) if self.quantum_instance is None: raise AlgorithmError( "A QuantumInstance or Backend must be supplied to run the quantum algorithm." ) self.quantum_instance.circuit_summary = True # this sets the size of the ansatz, so it must be called before the initial point # validation self._check_operator_ansatz(operator) # set an expectation for this algorithm run (will be reset to None at the end) initial_point = _validate_initial_point(self.initial_point, self.ansatz) bounds = _validate_bounds(self.ansatz) # We need to handle the array entries being zero or Optional i.e. having value None if aux_operators: zero_op = PauliSumOp.from_list([("I" * self.ansatz.num_qubits, 0)]) # Convert the None and zero values when aux_operators is a list. # Drop None and convert zero values when aux_operators is a dict. if isinstance(aux_operators, list): key_op_iterator = enumerate(aux_operators) converted = [zero_op] * len(aux_operators) else: key_op_iterator = aux_operators.items() converted = {} for key, op in key_op_iterator: if op is not None: converted[key] = zero_op if op == 0 else op aux_operators = converted else: aux_operators = None if self.betas is None: upper_bound = (abs(operator.coeff) if isinstance( operator, PauliOp) else abs(operator.coeff) * sum(abs(operation.coeff) for operation in operator)) self.betas = [upper_bound * 10] * (self.k) logger.info("beta autoevaluated to %s", self.betas[0]) result = VQDResult() result.optimal_point = [] result.optimal_parameters = [] result.optimal_value = [] result.cost_function_evals = [] result.optimizer_time = [] result.eigenvalues = [] result.eigenstates = [] if aux_operators is not None: aux_values = [] for step in range(1, self.k + 1): self._eval_count = 0 energy_evaluation, expectation = self.get_energy_evaluation( step, operator, return_expectation=True, prev_states=result.optimal_parameters) # Convert the gradient operator into a callable function that is compatible with the # optimization routine. Only used for the ground state currently as Gradient() doesnt # support SumOps yet if isinstance(self._gradient, GradientBase): gradient = self._gradient.gradient_wrapper( StateFn(operator, is_measurement=True) @ StateFn( self.ansatz), bind_params=list(self.ansatz.parameters), backend=self._quantum_instance, ) else: gradient = self._gradient start_time = time() if callable(self.optimizer): opt_result = self.optimizer( # pylint: disable=not-callable fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds) else: opt_result = self.optimizer.minimize(fun=energy_evaluation, x0=initial_point, jac=gradient, bounds=bounds) eval_time = time() - start_time result.optimal_point.append(opt_result.x) result.optimal_parameters.append( dict(zip(self.ansatz.parameters, opt_result.x))) result.optimal_value.append(opt_result.fun) result.cost_function_evals.append(opt_result.nfev) result.optimizer_time.append(eval_time) eigenvalue = (StateFn(operator, is_measurement=True).compose( CircuitStateFn( self.ansatz.bind_parameters( result.optimal_parameters[-1]))).reduce().eval()) result.eigenvalues.append(eigenvalue) result.eigenstates.append( self._get_eigenstate(result.optimal_parameters[-1])) if aux_operators is not None: bound_ansatz = self.ansatz.bind_parameters( result.optimal_point[-1]) aux_value = eval_observables(self.quantum_instance, bound_ansatz, aux_operators, expectation=expectation) aux_values.append(aux_value) if step == 1: logger.info( "Ground state optimization complete in %s seconds.\nFound opt_params %s in %s evals", eval_time, result.optimal_point, self._eval_count, ) else: logger.info( ("%s excited state optimization complete in %s s.\nFound opt_parms %s in %s evals" ), str(step - 1), eval_time, result.optimal_point, self._eval_count, ) # To match the siignature of NumpyEigenSolver Result result.eigenstates = ListOp( [StateFn(vec) for vec in result.eigenstates]) result.eigenvalues = np.array(result.eigenvalues) result.optimal_point = np.array(result.optimal_point) result.optimal_value = np.array(result.optimal_value) result.cost_function_evals = np.array(result.cost_function_evals) result.optimizer_time = np.array(result.optimizer_time) if aux_operators is not None: result.aux_operator_eigenvalues = aux_values return result
def get_energy_evaluation( self, step: int, operator: OperatorBase, return_expectation: bool = False, prev_states: Optional[List[np.ndarray]] = None, ) -> Callable[[np.ndarray], Union[float, List[float]]]: """Returns a function handle to evaluates the energy at given parameters for the ansatz. This return value is the objective function to be passed to the optimizer for evaluation. Args: step: level of enegy being calculated. 0 for ground, 1 for first excited state and so on. operator: The operator whose energy to evaluate. return_expectation: If True, return the ``ExpectationBase`` expectation converter used in the construction of the expectation value. Useful e.g. to evaluate other operators with the same expectation value converter. prev_states: List of parameters from previous rounds of optimization. Returns: A callable that computes and returns the energy of the hamiltonian of each parameter, and, optionally, the expectation Raises: RuntimeError: If the circuit is not parameterized (i.e. has 0 free parameters). AlgorithmError: If operator was not provided. """ num_parameters = self.ansatz.num_parameters if num_parameters == 0: raise RuntimeError( "The ansatz must be parameterized, but has 0 free parameters.") if operator is None: raise AlgorithmError("The operator was never provided.") if step > 1 and (len(prev_states) + 1) != step: raise RuntimeError( f"Passed previous states of the wrong size." f"Passed array has length {str(len(prev_states))}") self._check_operator_ansatz(operator) overlap_op = [] ansatz_params = self.ansatz.parameters expect_op, expectation = self.construct_expectation( ansatz_params, operator, return_expectation=True) for state in range(step - 1): prev_circ = self.ansatz.bind_parameters(prev_states[state]) overlap_op.append( ~CircuitStateFn(prev_circ) @ CircuitStateFn(self.ansatz)) def energy_evaluation(parameters): parameter_sets = np.reshape(parameters, (-1, num_parameters)) # Create dict associating each parameter with the lists of parameterization values for it param_bindings = dict( zip(ansatz_params, parameter_sets.transpose().tolist())) sampled_expect_op = self._circuit_sampler.convert( expect_op, params=param_bindings) mean = np.real(sampled_expect_op.eval()) for state in range(step - 1): sampled_final_op = self._circuit_sampler.convert( overlap_op[state], params=param_bindings) cost = sampled_final_op.eval() mean += np.real(self.betas[state] * np.conj(cost) * cost) self._eval_count += len(mean) return mean if len(mean) > 1 else mean[0] if return_expectation: return energy_evaluation, expectation return energy_evaluation
def test_parameterized_qobj(self): """grouped pauli expectation test""" two_qubit_h2 = ( (-1.052373245772859 * I ^ I) + (0.39793742484318045 * I ^ Z) + (-0.39793742484318045 * Z ^ I) + (-0.01128010425623538 * Z ^ Z) + (0.18093119978423156 * X ^ X) ) aer_sampler = CircuitSampler( self.sampler.quantum_instance, param_qobj=True, attach_results=True ) ansatz = RealAmplitudes() ansatz.num_qubits = 2 observable_meas = self.expect.convert(StateFn(two_qubit_h2, is_measurement=True)) ansatz_circuit_op = CircuitStateFn(ansatz) expect_op = observable_meas.compose(ansatz_circuit_op).reduce() def generate_parameters(num): param_bindings = {} for param in ansatz.parameters: values = [] for _ in range(num): values.append(np.random.rand()) param_bindings[param] = values return param_bindings def validate_sampler(ideal, sut, param_bindings): expect_sampled = ideal.convert(expect_op, params=param_bindings).eval() actual_sampled = sut.convert(expect_op, params=param_bindings).eval() self.assertTrue( np.allclose(actual_sampled, expect_sampled), "%s != %s" % (actual_sampled, expect_sampled), ) def get_circuit_templates(sampler): return sampler._transpiled_circ_templates def validate_aer_binding_used(templates): self.assertIsNotNone(templates) def validate_aer_templates_reused(prev_templates, cur_templates): self.assertIs(prev_templates, cur_templates) validate_sampler(self.sampler, aer_sampler, generate_parameters(1)) cur_templates = get_circuit_templates(aer_sampler) validate_aer_binding_used(cur_templates) prev_templates = cur_templates validate_sampler(self.sampler, aer_sampler, generate_parameters(2)) cur_templates = get_circuit_templates(aer_sampler) validate_aer_templates_reused(prev_templates, cur_templates) prev_templates = cur_templates validate_sampler(self.sampler, aer_sampler, generate_parameters(2)) # same num of params cur_templates = get_circuit_templates(aer_sampler) validate_aer_templates_reused(prev_templates, cur_templates)