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_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 test_overlap_qfi_bound_parameters(self): """Test the overlap QFI works on a circuit with multi-parameter bound gates.""" x = Parameter("x") circuit = QuantumCircuit(1) circuit.u(1, 2, 3, 0) circuit.rx(x, 0) qfi = QFI("overlap_diag").convert(StateFn(circuit), [x]) value = qfi.bind_parameters({x: 1}).eval()[0][0] ref = 0.87737713 self.assertAlmostEqual(value, ref)
def test_overlap_qfi_raises_on_unsupported_gate(self): """Test the overlap QFI raises an appropriate error on multi-param unbound gates.""" x = Parameter("x") circuit = QuantumCircuit(1) circuit.p(x, 0) with self.assertRaises(NotImplementedError): _ = QFI("overlap_diag").convert(StateFn(circuit), [x])
def test_overlap_qfi_raises_on_multiparam(self): """Test the overlap QFI raises an appropriate error on multi-param unbound gates.""" x = ParameterVector("x", 2) circuit = QuantumCircuit(1) circuit.u(x[0], x[1], 2, 0) with self.assertRaises(NotImplementedError): _ = QFI("overlap_diag").convert(StateFn(circuit), [x])
def test_qfi_overlap_works_with_bound_parameters(self): """Test all QFI methods work if the circuit contains a gate with bound parameters.""" x = Parameter('x') circuit = QuantumCircuit(1) circuit.ry(np.pi / 4, 0) circuit.rx(x, 0) state = StateFn(circuit) methods = ['lin_comb_full', 'overlap_diag', 'overlap_block_diag'] reference = 0.5 for method in methods: with self.subTest(method): qfi = QFI(method) value = np.real(qfi.convert(state, [x]).bind_parameters({x: 0.12}).eval()) self.assertAlmostEqual(value[0][0], reference)
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 test_qfi_circuit_shared_params(self): """Test the QFI circuits for parameters shared across some gates.""" # create the test circuit x = Parameter("x") circuit = QuantumCircuit(1) circuit.rx(x, 0) circuit.rx(x, 0) # construct the QFI circuits used in the evaluation circuit1 = QuantumCircuit(2) circuit1.h(1) circuit1.x(1) circuit1.cx(1, 0) circuit1.x(1) circuit1.cx(1, 0) # circuit1.rx(x, 0) # trimmed # circuit1.rx(x, 0) # trimmed circuit1.h(1) circuit2 = QuantumCircuit(2) circuit2.h(1) circuit2.x(1) circuit2.cx(1, 0) circuit2.x(1) circuit2.rx(x, 0) circuit2.cx(1, 0) # circuit2.rx(x, 0) # trimmed circuit2.h(1) circuit3 = QuantumCircuit(2) circuit3.h(1) circuit3.cx(1, 0) circuit3.x(1) circuit3.rx(x, 0) circuit3.cx(1, 0) # circuit3.rx(x, 0) # trimmed circuit3.x(1) circuit3.h(1) circuit4 = QuantumCircuit(2) circuit4.h(1) circuit4.rx(x, 0) circuit4.x(1) circuit4.cx(1, 0) circuit4.x(1) circuit4.cx(1, 0) # circuit4.rx(x, 0) # trimmed circuit4.h(1) # this naming and adding of register is required bc circuit's are only equal if the # register have the same names circuit5 = QuantumCircuit(2) circuit5.h(1) circuit5.sdg(1) circuit5.cx(1, 0) # circuit5.rx(x, 0) # trimmed circuit5.h(1) circuit6 = QuantumCircuit(2) circuit6.h(1) circuit6.sdg(1) circuit6.rx(x, 0) circuit6.cx(1, 0) circuit6.h(1) # compare qfi = QFI().convert(StateFn(circuit), params=[x]) circuit_sets = ( [circuit1, circuit2, circuit3, circuit4], [circuit5, circuit6], [circuit5, circuit6], ) list_ops = ( qfi.oplist[0].oplist[0].oplist[:-1], qfi.oplist[0].oplist[0].oplist[-1].oplist[0].oplist, qfi.oplist[0].oplist[0].oplist[-1].oplist[1].oplist, ) # compose both on the same circuit such that the comparison works base = QuantumCircuit(2) for i, (circuit_set, list_op) in enumerate(zip(circuit_sets, list_ops)): for j, (reference, composed_op) in enumerate(zip(circuit_set, list_op)): with self.subTest(f"set {i} circuit {j}"): self.assertEqual(base.compose(composed_op[1].primitive), base.compose(reference))