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)
Example #2
0
    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])
Example #6
0
    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))