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_jax_chain_rule(self, method: str, autograd: bool): """Test the chain rule functionality using Jax d<H>/d<X> = 2<X> d<H>/d<Z> = - sin(<Z>) <Z> = Tr(|psi><psi|Z) = sin(a)sin(b) <X> = Tr(|psi><psi|X) = cos(a) d<H>/da = d<H>/d<X> d<X>/da + d<H>/d<Z> d<Z>/da = - 2 cos(a)sin(a) - sin(sin(a)sin(b)) * cos(a)sin(b) d<H>/db = d<H>/d<X> d<X>/db + d<H>/d<Z> d<Z>/db = - sin(sin(a)sin(b)) * sin(a)cos(b) """ 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]) def combo_fn(x): return jnp.power(x[0], 2) + jnp.cos(x[1]) def grad_combo_fn(x): return np.array([2 * x[0], -np.sin(x[1])]) op = ListOp( [ ~StateFn(X) @ CircuitStateFn(primitive=qc, coeff=1.0), ~StateFn(Z) @ CircuitStateFn(primitive=qc, coeff=1.0), ], combo_fn=combo_fn, grad_combo_fn=None if autograd else grad_combo_fn, ) 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 = [[-1.0, 0.0], [-1.2397, -0.2397], [0, -0.45936]] 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_u(self, method): """Test the state gradient for U Tr(|psi><psi|Z) = - 0.5 sin(a)cos(c) Tr(|psi><psi|X) = cos^2(a/2) cos(b+c) - sin^2(a/2) cos(b-c) """ ham = 0.5 * X - 1 * Z a = Parameter("a") b = Parameter("b") c = Parameter("c") q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.u(a, b, c, q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) params = [a, b, c] state_grad = Gradient(grad_method=method).convert(operator=op, params=params) values_dict = [{ a: np.pi / 4, b: 0, c: 0 }, { a: np.pi / 4, b: np.pi / 4, c: np.pi / 4 }] correct_values = [[0.3536, 0, 0], [0.3232, -0.42678, -0.92678]] 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) # Tr(|psi><psi|Z) = - 0.5 sin(a)cos(c) # Tr(|psi><psi|X) = cos^2(a/2) cos(b+c) - sin^2(a/2) cos(b-c) # dTr(|psi><psi|H)/da = 0.5(cos(2a)) + 0.5() q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.u(a, a, a, q[0]) 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 = [[-1.03033], [-1]] 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_pauli_expectation_param_qobj(self): """Test PauliExpectation with param_qobj""" q_instance = QuantumInstance( self.backend, seed_simulator=self.seed, seed_transpiler=self.seed, shots=10000 ) qubit_op = (0.1 * I ^ I) + (0.2 * I ^ Z) + (0.3 * Z ^ I) + (0.4 * Z ^ Z) + (0.5 * X ^ X) ansatz = RealAmplitudes(qubit_op.num_qubits) ansatz_circuit_op = CircuitStateFn(ansatz) observable = PauliExpectation().convert(~StateFn(qubit_op)) expect_op = observable.compose(ansatz_circuit_op).reduce() params1 = {} params2 = {} for param in ansatz.parameters: params1[param] = [0] params2[param] = [0, 0] sampler1 = CircuitSampler(backend=q_instance, param_qobj=False) samples1 = sampler1.convert(expect_op, params=params1) val1 = np.real(samples1.eval())[0] samples2 = sampler1.convert(expect_op, params=params2) val2 = np.real(samples2.eval()) sampler2 = CircuitSampler(backend=q_instance, param_qobj=True) samples3 = sampler2.convert(expect_op, params=params1) val3 = np.real(samples3.eval()) samples4 = sampler2.convert(expect_op, params=params2) val4 = np.real(samples4.eval()) np.testing.assert_array_almost_equal([val1] * 2, val2, decimal=2) np.testing.assert_array_almost_equal(val1, val3, decimal=2) np.testing.assert_array_almost_equal([val1] * 2, val4, decimal=2)
def _eval_aux_ops( self, parameters: np.ndarray, aux_operators: List[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, ) -> np.ndarray: # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(self.quantum_instance) aux_op_meas = expectation.convert( StateFn(ListOp(aux_operators), 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) # Deal with the aux_op behavior where there can be Nones or Zero qubit Paulis in the list _aux_op_nones = [op is None for op in aux_operators] aux_operator_eigenvalues = [ None if is_none else [result] for (is_none, result) in zip(_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 aux_operator_eigenvalues = np.array([aux_operator_eigenvalues], dtype=object) return aux_operator_eigenvalues
def test_natural_gradient(self, method, regularization): """Test the natural gradient""" try: for params in (ParameterVector('a', 2), [Parameter('a'), Parameter('b')]): ham = 0.5 * X - 1 * Z 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.) nat_grad = NaturalGradient(grad_method=method, regularization=regularization)\ .convert(operator=op, params=params) values_dict = [{params[0]: np.pi / 4, params[1]: np.pi / 2}] correct_values = [[-2.36003979, 2.06503481]] \ if regularization == 'ridge' else [[-4.2, 0]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( nat_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=0) except MissingOptionalLibraryError as ex: self.skipTest(str(ex))
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) @ 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 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()[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_gradient4(self, method): """Test the state gradient 4 Tr(|psi><psi|ZX) = -cos(a) daTr(|psi><psi|ZX) = sin(a) """ ham = X ^ Z a = Parameter("a") params = a q = QuantumRegister(2) qc = QuantumCircuit(q) qc.x(q[0]) qc.h(q[1]) qc.crz(a, q[0], q[1]) 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}, {a: 0}, {a: np.pi / 2}] correct_values = [1 / np.sqrt(2), 0, 1] 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_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_state_gradient3(self, method): """Test the state gradient 3 Tr(|psi><psi|Z) = sin(a)sin(c(a)) = sin(a)sin(cos(a)+1) Tr(|psi><psi|X) = cos(a) d<H>/da = - 0.5 sin(a) - 1 cos(a)sin(cos(a)+1) + 1 sin^2(a)cos(cos(a)+1) """ ham = 0.5 * X - 1 * Z a = Parameter("a") # b = Parameter('b') params = a c = np.cos(a) + 1 q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(c, 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}, {a: 0}, {a: np.pi / 2}] correct_values = [-1.1220, -0.9093, 0.0403] 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(self, method): """Test if the quantum fisher information calculation is correct QFI = [[1, 0], [0, 1]] - [[0, 0], [0, cos^2(a)]] """ 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.) qfi = QFI(qfi_method=method).convert(operator=op, params=params) values_dict = [{ params[0]: np.pi / 4, params[1]: 0.1 }, { params[0]: np.pi, params[1]: 0.1 }, { params[0]: np.pi / 2, params[1]: 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): np.testing.assert_array_almost_equal( qfi.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_natural_gradient4(self, grad_method, qfi_method, regularization): """Test the natural gradient 4""" # Avoid regularization = lasso intentionally because it does not converge try: ham = 0.5 * X - 1 * Z a = Parameter("a") params = a q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) op = ~StateFn(ham) @ CircuitStateFn(primitive=qc, coeff=1.0) nat_grad = NaturalGradient(grad_method=grad_method, qfi_method=qfi_method, regularization=regularization).convert( operator=op, params=params) values_dict = [{a: np.pi / 4}] correct_values = [[0.0]] if regularization == "ridge" else [[ -1.41421342 ]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( nat_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=3) except MissingOptionalLibraryError as ex: self.skipTest(str(ex))
def test_natural_gradient(self, method, regularization): """Test the natural gradient""" try: for params in (ParameterVector("a", 2), [Parameter("a"), Parameter("b")]): ham = 0.5 * X - 1 * Z 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) nat_grad = NaturalGradient( grad_method=method, regularization=regularization).convert(operator=op) values_dict = [{params[0]: np.pi / 4, params[1]: np.pi / 2}] # reference values obtained by classically computing the natural gradients correct_values = [[ -3.26, 1.63 ]] if regularization == "ridge" else [[-4.24, 0]] for i, value_dict in enumerate(values_dict): np.testing.assert_array_almost_equal( nat_grad.assign_parameters(value_dict).eval(), correct_values[i], decimal=1) except MissingOptionalLibraryError as ex: self.skipTest(str(ex))
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.) 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 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.) 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_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.) state_hess = Hessian(hess_method=method).convert(operator=op, params=params) 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.], [-1 / 2., 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 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_state_gradient2(self, method): """Test the state gradient 2 Tr(|psi><psi|Z) = sin(a)sin(a) Tr(|psi><psi|X) = cos(a) d<H>/da = - 0.5 sin(a) - 2 cos(a)sin(a) """ ham = 0.5 * X - 1 * Z a = Parameter('a') # b = Parameter('b') params = [a] q = QuantumRegister(1) qc = QuantumCircuit(q) qc.h(q) qc.rz(a, q[0]) qc.rx(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 = [-1.353553, -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_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.0) 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_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_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_circuit_state_fn_from_dict_initialize(self): """state fn circuit from dict initialize test""" statedict = {"101": 0.5, "100": 0.1, "000": 0.2, "111": 0.5} sfc = CircuitStateFn.from_dict(statedict) self.assertIsInstance(sfc, CircuitStateFn) samples = sfc.sample() np.testing.assert_array_almost_equal( StateFn(statedict).to_matrix(), np.round(sfc.to_matrix(), decimals=1)) for k, v in samples.items(): self.assertIn(k, statedict) # It's ok if these are far apart because the dict is sampled. self.assertAlmostEqual(v, np.abs(statedict[k])**0.5, delta=0.5) # Follows same code path as above, but testing to be thorough sfc_vector = CircuitStateFn.from_vector(StateFn(statedict).to_matrix()) np.testing.assert_array_almost_equal( StateFn(statedict).to_matrix(), sfc_vector.to_matrix())
def test_compose(self): """ compose test """ target = (X + Z) @ (Y + Z) expected = 1j * Z - 1j * Y - 1j * X + I self.assertEqual(target, expected) observable = (X ^ X) + (Y ^ Y) + (Z ^ Z) state = CircuitStateFn((CX @ (X ^ H @ X)).to_circuit()) self.assertAlmostEqual((~OperatorStateFn(observable) @ state).eval(), -3)
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.0)], 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 _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 construct_expectation( self, parameter: Union[List[float], List[Parameter], np.ndarray], operator: OperatorBase, return_expectation: bool = False, ) -> Union[OperatorBase, Tuple[OperatorBase, ExpectationBase]]: 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 return_expectation: If True, return the ``ExpectationBase`` expectation converter used in the construction of the expectation value. Useful e.g. to compute the standard deviation of the expectation value. Returns: The Operator equalling the measurement of the ansatz :class:`StateFn` by the Observable's expectation :class:`StateFn`, and, optionally, the expectation converter. Raises: AlgorithmError: If no operator has been provided. AlgorithmError: If no expectation is passed and None could be inferred via the ExpectationFactory. """ if operator is None: raise AlgorithmError("The operator was never provided.") self._check_operator_ansatz(operator) # if expectation was never created, try to create one if self.expectation is None: expectation = ExpectationFactory.build( operator=operator, backend=self.quantum_instance, include_custom=self._include_custom, ) else: expectation = self.expectation param_dict = dict(zip(self._ansatz_params, parameter)) # type: Dict wave_function = self.ansatz.assign_parameters(param_dict) observable_meas = expectation.convert( StateFn(operator, is_measurement=True)) ansatz_circuit_op = CircuitStateFn(wave_function) expect_op = observable_meas.compose(ansatz_circuit_op).reduce() if return_expectation: return expect_op, expectation return expect_op
def _eval_aux_ops( self, parameters: np.ndarray, aux_operators: ListOrDict[OperatorBase], expectation: ExpectationBase, threshold: float = 1e-12, ) -> ListOrDict[Tuple[complex, 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))) aux_op_expect_sampled = sampler.convert(aux_op_expect) # compute means values = np.real(aux_op_expect_sampled.eval()) # compute standard deviations variances = np.real( expectation.compute_variance(aux_op_expect_sampled)) if not isinstance(variances, np.ndarray) and variances == 0.0: # when `variances` is a single value equal to 0., our expectation value is exact and we # manually ensure the variances to be a list of the correct length variances = np.zeros(len(aux_operators), dtype=float) std_devs = np.sqrt(variances / self.quantum_instance.run_config.shots) # Discard values below threshold aux_op_means = values * (np.abs(values) > threshold) # zip means and standard deviations into tuples aux_op_results = zip(aux_op_means, std_devs) # 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_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.0) shots = 8000 backend = BasicAer.get_backend(backend_type) q_instance = QuantumInstance(backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2) if method == "fin_diff": np.random.seed(8) prob_grad = Gradient(grad_method=method, epsilon=shots**(-1 / 6.0)).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))