Esempio n. 1
0
    def test_aux_operators_dict(self):
        """Test dictionary compatibility of aux_operators"""
        wavefunction = self.ry_wavefunction
        vqd = VQD(ansatz=wavefunction,
                  quantum_instance=self.statevector_simulator)

        # Start with an empty dictionary
        result = vqd.compute_eigenvalues(self.h2_op, aux_operators={})
        np.testing.assert_array_almost_equal(result.eigenvalues.real,
                                             self.h2_energy_excited,
                                             decimal=2)
        self.assertIsNone(result.aux_operator_eigenvalues)

        # Go again with two auxiliary operators
        aux_op1 = PauliSumOp.from_list([("II", 2.0)])
        aux_op2 = PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5),
                                        ("XX", -0.5)])
        aux_ops = {"aux_op1": aux_op1, "aux_op2": aux_op2}
        result = vqd.compute_eigenvalues(self.h2_op, aux_operators=aux_ops)
        self.assertEqual(len(result.eigenvalues), 2)
        self.assertEqual(len(result.eigenstates), 2)
        self.assertEqual(result.eigenvalues.dtype, np.complex128)
        self.assertAlmostEqual(result.eigenvalues[0], -1.85727503)
        self.assertEqual(len(result.aux_operator_eigenvalues), 2)
        self.assertEqual(len(result.aux_operator_eigenvalues[0]), 2)
        # expectation values
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op1"][0], 2, places=6)
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op2"][0], 0, places=1)
        # standard deviations
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op1"][1], 0.0)
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op2"][1], 0.0)

        # Go again with additional None and zero operators
        extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0}
        result = vqd.compute_eigenvalues(self.h2_op, aux_operators=extra_ops)
        self.assertEqual(len(result.eigenvalues), 2)
        self.assertEqual(len(result.eigenstates), 2)
        self.assertEqual(result.eigenvalues.dtype, np.complex128)
        self.assertAlmostEqual(result.eigenvalues[0], -1.85727503)
        self.assertEqual(len(result.aux_operator_eigenvalues), 2)
        self.assertEqual(len(result.aux_operator_eigenvalues[0]), 3)
        # expectation values
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op1"][0], 2, places=6)
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op2"][0], 0, places=6)
        self.assertEqual(
            result.aux_operator_eigenvalues[0]["zero_operator"][0], 0.0)
        self.assertTrue(
            "None_operator" not in result.aux_operator_eigenvalues[0].keys())
        # standard deviations
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op1"][1], 0.0)
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["aux_op2"][1], 0.0)
        self.assertAlmostEqual(
            result.aux_operator_eigenvalues[0]["zero_operator"][1], 0.0)
Esempio n. 2
0
    def test_qaoa(self):
        """Test the QAOA with a custom mixer.

        This test ensures that single-qubit gates end up on the correct qubits. The mixer
        uses Ry gates and the operator is :code:`[("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)]`.

        ..parsed-literal:

                 ┌───┐                                              ┌──────────────────┐»
            q_0: ┤ H ├─────────────────────X────────────────────────┤0                 ├»
                 ├───┤┌──────────────────┐ │ ┌──────────────────┐   │  exp(-i ZZ)(3.0) │»
            q_1: ┤ H ├┤0                 ├─X─┤0                 ├─X─┤1                 ├»
                 ├───┤│  exp(-i ZZ)(1.0) │   │  exp(-i ZZ)(2.0) │ │ └──────────────────┘»
            q_2: ┤ H ├┤1                 ├─X─┤1                 ├─X─────────────────────»
                 ├───┤└──────────────────┘ │ └──────────────────┘                       »
            q_3: ┤ H ├─────────────────────X────────────────────────────────────────────»
                 └───┘                                                                  »
            «     ┌────────┐                    ┌──────────────────┐         »
            «q_0: ┤ Ry(-1) ├────────────────────┤0                 ├────X────»
            «     ├────────┤┌──────────────────┐│  exp(-i ZZ)(6.0) │    │    »
            «q_1: ┤ Ry(-3) ├┤0                 ├┤1                 ├────X────»
            «     ├────────┤│  exp(-i ZZ)(4.0) │└──────────────────┘         »
            «q_2: ┤ Ry(0)  ├┤1                 ├─────────X───────────────────»
            «     ├────────┤└──────────────────┘         │                   »
            «q_3: ┤ Ry(-2) ├─────────────────────────────X───────────────────»
            «     └────────┘                                                 »
            «                         ┌────────┐
            «q_0: ────────────────────┤ Ry(-3) ├
            «     ┌──────────────────┐├────────┤
            «q_1: ┤0                 ├┤ Ry(-1) ├
            «     │  exp(-i ZZ)(2.0) │├────────┤
            «q_2: ┤1                 ├┤ Ry(-2) ├
            «     └──────────────────┘├────────┤
            «q_3: ────────────────────┤ Ry(0)  ├
            «                         └────────┘
        """

        mixer = QuantumCircuit(4)
        for idx in range(4):
            mixer.ry(-idx, idx)

        op = PauliSumOp.from_list([("IZZI", 1), ("ZIIZ", 2), ("ZIZI", 3)])
        circ = QAOAAnsatz(op, reps=2, mixer_operator=mixer)

        swapped = self.pm_.run(circ.decompose())
        param_dict = {p: idx + 1 for idx, p in enumerate(swapped.parameters)}
        swapped.assign_parameters(param_dict, inplace=True)

        # There is some optionality in the order of two instructions. Both are valid.
        valid_expected = []
        for order in [0, 1]:
            expected = QuantumCircuit(4)
            expected.h(range(4))
            expected.append(PauliEvolutionGate(Pauli("ZZ"), 1), (1, 2))
            expected.swap(0, 1)
            expected.swap(2, 3)
            expected.append(PauliEvolutionGate(Pauli("ZZ"), 2), (1, 2))
            expected.swap(1, 2)
            expected.append(PauliEvolutionGate(Pauli("ZZ"), 3), (0, 1))
            expected.ry(-1, 0)
            expected.ry(-3, 1)
            expected.ry(0, 2)
            expected.ry(-2, 3)
            if order == 0:
                expected.append(PauliEvolutionGate(Pauli("ZZ"), 6), (0, 1))
                expected.append(PauliEvolutionGate(Pauli("ZZ"), 4), (2, 1))
            else:
                expected.append(PauliEvolutionGate(Pauli("ZZ"), 4), (2, 1))
                expected.append(PauliEvolutionGate(Pauli("ZZ"), 6), (0, 1))

            expected.swap(0, 1)
            expected.swap(2, 3)
            expected.append(PauliEvolutionGate(Pauli("ZZ"), 2), (1, 2))
            expected.ry(-3, 0)
            expected.ry(-1, 1)
            expected.ry(-2, 2)
            expected.ry(0, 3)

            valid_expected.append(expected == swapped)

        self.assertEqual(set(valid_expected), {True, False})
Esempio n. 3
0
    def test_t_device(self):
        """Test the swap strategy to route a complete problem on a T device.

        The coupling map in this test corresponds to

        .. parsed-literal::

            0 -- 1 -- 2
                 |
                 3
                 |
                 4

        The problem being routed is a fully connect ZZ graph. It has 10 terms since there are
        five qubits in the coupling map. This test checks that the circuit produced by the
        commuting gate router has the instructions we expect. There are several circuits that are
        valid since some of the Rzz gates commute.
        """
        swaps = (
            ((1, 3), ),
            ((0, 1), (3, 4)),
            ((1, 3), ),
        )

        cmap = CouplingMap([[0, 1], [1, 2], [1, 3], [3, 4]])
        cmap.make_symmetric()

        swap_strat = SwapStrategy(cmap, swaps)

        # A dense Pauli op.
        op = PauliSumOp.from_list([
            ("IIIZZ", 1),
            ("IIZIZ", 2),
            ("IZIIZ", 3),
            ("ZIIIZ", 4),
            ("IIZZI", 5),
            ("IZIZI", 6),
            ("ZIIZI", 7),
            ("IZZII", 8),
            ("ZIZII", 9),
            ("ZZIII", 10),
        ])

        circ = QuantumCircuit(5)
        circ.append(PauliEvolutionGate(op, 1), range(5))

        pm_ = PassManager([
            FindCommutingPauliEvolutions(),
            Commuting2qGateRouter(swap_strat),
        ])

        swapped = circuit_to_dag(pm_.run(circ))

        # The swapped circuit can take on several forms as some of the gates commute.
        # We test that sets of gates are where we expected them in the circuit data
        def inst_info(op, qargs, qreg):
            """Get a tuple we can easily test."""
            param = None
            if len(op.params) > 0:
                param = op.params[0]

            return op.name, param, qreg.index(qargs[0]), qreg.index(qargs[1])

        qreg = swapped.qregs["q"]
        inst_list = list(
            inst_info(node.op, node.qargs, qreg)
            for node in swapped.op_nodes())

        # First block has the Rzz gates ("IIIZZ", 1), ("IIZZI", 5), ("IZIZI", 6), ("ZZIII", 10)
        expected = {
            ("PauliEvolution", 1.0, 0, 1),
            ("PauliEvolution", 5.0, 1, 2),
            ("PauliEvolution", 6.0, 1, 3),
            ("PauliEvolution", 10.0, 3, 4),
        }
        self.assertSetEqual(set(inst_list[0:4]), expected)

        # Block 2 is a swap
        self.assertSetEqual({inst_list[4]}, {("swap", None, 1, 3)})

        # Block 3 This combines a swap layer and two layers of Rzz gates.
        expected = {
            ("PauliEvolution", 8.0, 2, 1),
            ("PauliEvolution", 7.0, 3, 4),
            ("PauliEvolution", 3.0, 0, 1),
            ("swap", None, 0, 1),
            ("PauliEvolution", 2.0, 1, 2),
            ("PauliEvolution", 4.0, 1, 3),
            ("swap", None, 3, 4),
        }
        self.assertSetEqual(set(inst_list[5:12]), expected)

        # Test the remaining instructions.
        self.assertSetEqual({inst_list[12]}, {("swap", None, 1, 3)})
        self.assertSetEqual({inst_list[13]}, {("PauliEvolution", 9.0, 2, 1)})
Esempio n. 4
0
    def mode_based_mapping(
            second_q_op: SecondQuantizedOp,
            pauli_table: List[Tuple[Pauli, Pauli]]) -> PauliSumOp:
        """Utility method to map a `SecondQuantizedOp` to a `PauliSumOp` using a pauli table.

        Args:
            second_q_op: the `SecondQuantizedOp` to be mapped.
            pauli_table: a table of paulis built according to the modes of the operator

        Returns:
            The `PauliSumOp` corresponding to the problem-Hamiltonian in the qubit space.

        Raises:
            QiskitNatureError: If number length of pauli table does not match the number
                of operator modes, or if the operator has unexpected label content
        """
        nmodes = len(pauli_table)
        if nmodes != second_q_op.register_length:
            raise QiskitNatureError(
                f"Pauli table len {nmodes} does not match"
                f"operator register length {second_q_op.register_length}")

            # 0. Some utilities
        def times_creation_op(position, pauli_table):
            # The creation operator is given by 0.5*(X + 1j*Y)
            real_part = SparsePauliOp(pauli_table[position][0], coeffs=[0.5])
            imag_part = SparsePauliOp(pauli_table[position][1], coeffs=[0.5j])

            return real_part + imag_part

        def times_annihilation_op(position, pauli_table):
            # The annihilation operator is given by 0.5*(X - 1j*Y)
            real_part = SparsePauliOp(pauli_table[position][0], coeffs=[0.5])
            imag_part = SparsePauliOp(pauli_table[position][1], coeffs=[-0.5j])

            return real_part + imag_part

        # 1. Initialize an operator list with the identity scaled by the `self.coeff`
        all_false = np.asarray([False] * nmodes, dtype=bool)

        ret_op_list = []

        # TODO to_list() is not an attribute of SecondQuantizedOp. Change the former to have this or
        #   change the signature above to take FermionicOp?
        for label, coeff in second_q_op.to_list():

            ret_op = SparsePauliOp(Pauli((all_false, all_false)),
                                   coeffs=[coeff])

            # Go through the label and replace the fermion operators by their qubit-equivalent, then
            # save the respective Pauli string in the pauli_str list.
            for position, char in enumerate(label):
                if char == "+":
                    ret_op &= times_creation_op(position, pauli_table)
                elif char == "-":
                    ret_op &= times_annihilation_op(position, pauli_table)
                elif char == "N":
                    # The occupation number operator N is given by `+-`.
                    ret_op &= times_creation_op(position, pauli_table)
                    ret_op &= times_annihilation_op(position, pauli_table)
                elif char == "E":
                    # The `emptiness number` operator E is given by `-+` = (I - N).
                    ret_op &= times_annihilation_op(position, pauli_table)
                    ret_op &= times_creation_op(position, pauli_table)
                elif char == "I":
                    continue

                # catch any disallowed labels
                else:
                    raise QiskitNatureError(
                        f"FermionicOp label included '{char}'. "
                        "Allowed characters: I, N, E, +, -")
            ret_op_list.append(ret_op)

        zero_op = SparsePauliOp(Pauli((all_false, all_false)), coeffs=[0])
        return PauliSumOp(sum(ret_op_list, zero_op)).reduce()
Esempio n. 5
0
def get_operator(ins, penalty: float = 1e5) -> Tuple[PauliSumOp, float]:
    """Generate Hamiltonian for TSP of a graph.
    Args:
        ins (TspData) : TSP data including coordinates and distances.
        penalty: Penalty coefficient for the constraints
    Returns:
        operator for the Hamiltonian and a
        constant shift for the obj function.
    """
    num_nodes = ins.dim
    num_qubits = num_nodes**2
    zero = np.zeros(num_qubits, dtype=bool)
    pauli_list = []
    shift = 0.
    for i in range(num_nodes):
        for j in range(num_nodes):
            if i == j:
                continue
            for p__ in range(num_nodes):
                q = (p__ + 1) % num_nodes
                shift += ins.w[i, j] / 4

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + p__] = True
                pauli_list.append([-ins.w[i, j] / 4, Pauli((z_p, zero))])

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[j * num_nodes + q] = True
                pauli_list.append([-ins.w[i, j] / 4, Pauli((z_p, zero))])

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + p__] = True
                z_p[j * num_nodes + q] = True
                pauli_list.append([ins.w[i, j] / 4, Pauli((z_p, zero))])

    for i in range(num_nodes):
        for p__ in range(num_nodes):
            z_p = np.zeros(num_qubits, dtype=bool)
            z_p[i * num_nodes + p__] = True
            pauli_list.append([penalty, Pauli((z_p, zero))])
            shift += -penalty

    for p__ in range(num_nodes):
        for i in range(num_nodes):
            for j in range(i):
                shift += penalty / 2

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + p__] = True
                pauli_list.append([-penalty / 2, Pauli((z_p, zero))])

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[j * num_nodes + p__] = True
                pauli_list.append([-penalty / 2, Pauli((z_p, zero))])

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + p__] = True
                z_p[j * num_nodes + p__] = True
                pauli_list.append([penalty / 2, Pauli((z_p, zero))])

    for i in range(num_nodes):
        for p__ in range(num_nodes):
            for q in range(p__):
                shift += penalty / 2

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + p__] = True
                pauli_list.append([-penalty / 2, Pauli((z_p, zero))])

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + q] = True
                pauli_list.append([-penalty / 2, Pauli((z_p, zero))])

                z_p = np.zeros(num_qubits, dtype=bool)
                z_p[i * num_nodes + p__] = True
                z_p[i * num_nodes + q] = True
                pauli_list.append([penalty / 2, Pauli((z_p, zero))])
    shift += 2 * penalty * num_nodes
    opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list]
    return PauliSumOp.from_list(opflow_list), shift
Esempio n. 6
0
def get_operator(weight_matrix: np.ndarray,
                 K: np.ndarray) -> Tuple[PauliSumOp, float]:
    r"""
    Generate Hamiltonian for the clique.

    The goals is can we find a complete graph of size K?

    To build the Hamiltonian the following logic is applied.

    | Suppose Xv denotes whether v should appear in the clique (Xv=1 or 0)\n
    | H = Ha + Hb\n
    | Ha = (K-sum_{v}{Xv})\^2
    | Hb = K(K−1)/2 - sum_{(u,v)\in E}{XuXv}

    | Besides, Xv = (Zv+1)/2
    | By replacing Xv with Zv and simplifying it, we get what we want below.

    Note: in practice, we use H = A\*Ha + Bb, where A is a large constant such as 1000.

    A is like a huge penality over the violation of Ha,
    which forces Ha to be 0, i.e., you have exact K vertices selected.
    Under this assumption, Hb = 0 starts to make sense,
    it means the subgraph constitutes a clique or complete graph.
    Note the lowest possible value of Hb is 0.

    Without the above assumption, Hb may be negative (say you select all).
    In this case, one needs to use Hb\^2 in the hamiltonian to minimize the difference.

    Args:
        weight_matrix : adjacency matrix.
        K: K

    Returns:
        The operator for the Hamiltonian and a constant shift for the obj function.
    """
    # pylint: disable=invalid-name
    num_nodes = len(weight_matrix)
    pauli_list = []
    shift = 0.

    Y = K - 0.5 * num_nodes  # Y = K - sum_{v}{1 / 2}

    A = 1000.
    # Ha part:
    shift += A * Y * Y

    for i in range(num_nodes):
        for j in range(num_nodes):
            if i != j:
                xp = np.zeros(num_nodes, dtype=np.bool)
                zp = np.zeros(num_nodes, dtype=np.bool)
                zp[i] = True
                zp[j] = True
                pauli_list.append([A * 0.25, Pauli(zp, xp)])
            else:
                shift += A * 0.25
    for i in range(num_nodes):
        xp = np.zeros(num_nodes, dtype=np.bool)
        zp = np.zeros(num_nodes, dtype=np.bool)
        zp[i] = True
        pauli_list.append([-A * Y, Pauli(zp, xp)])

    shift += 0.5 * K * (K - 1)

    for i in range(num_nodes):
        for j in range(i):
            if weight_matrix[i, j] != 0:
                xp = np.zeros(num_nodes, dtype=np.bool)
                zp = np.zeros(num_nodes, dtype=np.bool)
                zp[i] = True
                zp[j] = True
                pauli_list.append([-0.25, Pauli(zp, xp)])

                zp2 = np.zeros(num_nodes, dtype=np.bool)
                zp2[i] = True
                pauli_list.append([-0.25, Pauli(zp2, xp)])

                zp3 = np.zeros(num_nodes, dtype=np.bool)
                zp3[j] = True
                pauli_list.append([-0.25, Pauli(zp3, xp)])

                shift += -0.25

    opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list]
    return PauliSumOp.from_list(opflow_list), shift
Esempio n. 7
0
    def setUp(self):
        super().setUp()

        algorithm_globals.random_seed = 1234
        qi_sv = QuantumInstance(
            Aer.get_backend("aer_simulator_statevector"),
            seed_simulator=algorithm_globals.random_seed,
            seed_transpiler=algorithm_globals.random_seed,
        )

        # set up quantum neural networks
        num_qubits = 3
        feature_map = ZFeatureMap(feature_dimension=num_qubits, reps=1)
        ansatz = RealAmplitudes(num_qubits, reps=1)

        # CircuitQNNs
        qc = QuantumCircuit(num_qubits)
        qc.append(feature_map, range(num_qubits))
        qc.append(ansatz, range(num_qubits))

        def parity(x):
            return f"{x:b}".count("1") % 2

        circuit_qnn_1 = CircuitQNN(
            qc,
            input_params=feature_map.parameters,
            weight_params=ansatz.parameters,
            interpret=parity,
            output_shape=2,
            sparse=False,
            quantum_instance=qi_sv,
        )

        # qnn2 for checking result without parity
        circuit_qnn_2 = CircuitQNN(
            qc,
            input_params=feature_map.parameters,
            weight_params=ansatz.parameters,
            sparse=False,
            quantum_instance=qi_sv,
        )

        # OpflowQNN
        observable = PauliSumOp.from_list([("Z" * num_qubits, 1)])
        opflow_qnn = TwoLayerQNN(
            num_qubits,
            feature_map=feature_map,
            ansatz=ansatz,
            observable=observable,
            quantum_instance=qi_sv,
        )

        self.qnns = {
            "circuit1": circuit_qnn_1,
            "circuit2": circuit_qnn_2,
            "opflow": opflow_qnn
        }

        # define sample numbers
        self.n_list = [
            5000, 8000, 10000, 40000, 60000, 100000, 150000, 200000, 500000,
            1000000
        ]
        self.n = 5000
Esempio n. 8
0
    def test_build_hopping_operators(self):
        """Tests that the correct hopping operator is built from QMolecule."""
        # TODO extract it somewhere
        expected_hopping_operators = (
            {
                "E_0":
                PauliSumOp(
                    SparsePauliOp(
                        [
                            [
                                True, True, False, False, False, False, False,
                                False
                            ],
                            [
                                True, True, False, False, False, True, False,
                                False
                            ],
                            [
                                True, True, False, False, True, False, False,
                                False
                            ],
                            [
                                True, True, False, False, True, True, False,
                                False
                            ],
                        ],
                        coeffs=[
                            0.25 + 0.0j, 0.0 - 0.25j, 0.0 + 0.25j, 0.25 + 0.0j
                        ],
                    ),
                    coeff=1.0,
                ),
                "Edag_0":
                PauliSumOp(
                    SparsePauliOp(
                        [
                            [
                                True, True, False, False, False, False, False,
                                False
                            ],
                            [
                                True, True, False, False, False, True, False,
                                False
                            ],
                            [
                                True, True, False, False, True, False, False,
                                False
                            ],
                            [
                                True, True, False, False, True, True, False,
                                False
                            ],
                        ],
                        coeffs=[
                            0.25 + 0.0j, 0.0 + 0.25j, 0.0 - 0.25j, 0.25 + 0.0j
                        ],
                    ),
                    coeff=1.0,
                ),
                "E_1":
                PauliSumOp(
                    SparsePauliOp(
                        [
                            [
                                False, False, True, True, False, False, False,
                                False
                            ],
                            [
                                False, False, True, True, False, False, False,
                                True
                            ],
                            [
                                False, False, True, True, False, False, True,
                                False
                            ],
                            [
                                False, False, True, True, False, False, True,
                                True
                            ],
                        ],
                        coeffs=[
                            0.25 + 0.0j, 0.0 - 0.25j, 0.0 + 0.25j, 0.25 + 0.0j
                        ],
                    ),
                    coeff=1.0,
                ),
                "Edag_1":
                PauliSumOp(
                    SparsePauliOp(
                        [
                            [
                                False, False, True, True, False, False, False,
                                False
                            ],
                            [
                                False, False, True, True, False, False, False,
                                True
                            ],
                            [
                                False, False, True, True, False, False, True,
                                False
                            ],
                            [
                                False, False, True, True, False, False, True,
                                True
                            ],
                        ],
                        coeffs=[
                            0.25 + 0.0j, 0.0 + 0.25j, 0.0 - 0.25j, 0.25 + 0.0j
                        ],
                    ),
                    coeff=1.0,
                ),
                "E_2":
                PauliSumOp(
                    SparsePauliOp(
                        [
                            [
                                True, True, True, True, False, False, False,
                                False
                            ],
                            [
                                True, True, True, True, False, False, False,
                                True
                            ],
                            [
                                True, True, True, True, False, False, True,
                                False
                            ],
                            [True, True, True, True, False, False, True, True],
                            [
                                True, True, True, True, False, True, False,
                                False
                            ],
                            [True, True, True, True, False, True, False, True],
                            [True, True, True, True, False, True, True, False],
                            [True, True, True, True, False, True, True, True],
                            [
                                True, True, True, True, True, False, False,
                                False
                            ],
                            [True, True, True, True, True, False, False, True],
                            [True, True, True, True, True, False, True, False],
                            [True, True, True, True, True, False, True, True],
                            [True, True, True, True, True, True, False, False],
                            [True, True, True, True, True, True, False, True],
                            [True, True, True, True, True, True, True, False],
                            [True, True, True, True, True, True, True, True],
                        ],
                        coeffs=[
                            0.0625 + 0.0j,
                            0.0 - 0.0625j,
                            0.0 + 0.0625j,
                            0.0625 + 0.0j,
                            0.0 - 0.0625j,
                            -0.0625 + 0.0j,
                            0.0625 + 0.0j,
                            0.0 - 0.0625j,
                            0.0 + 0.0625j,
                            0.0625 + 0.0j,
                            -0.0625 + 0.0j,
                            0.0 + 0.0625j,
                            0.0625 + 0.0j,
                            0.0 - 0.0625j,
                            0.0 + 0.0625j,
                            0.0625 + 0.0j,
                        ],
                    ),
                    coeff=1.0,
                ),
                "Edag_2":
                PauliSumOp(
                    SparsePauliOp(
                        [
                            [
                                True, True, True, True, False, False, False,
                                False
                            ],
                            [
                                True, True, True, True, False, False, False,
                                True
                            ],
                            [
                                True, True, True, True, False, False, True,
                                False
                            ],
                            [True, True, True, True, False, False, True, True],
                            [
                                True, True, True, True, False, True, False,
                                False
                            ],
                            [True, True, True, True, False, True, False, True],
                            [True, True, True, True, False, True, True, False],
                            [True, True, True, True, False, True, True, True],
                            [
                                True, True, True, True, True, False, False,
                                False
                            ],
                            [True, True, True, True, True, False, False, True],
                            [True, True, True, True, True, False, True, False],
                            [True, True, True, True, True, False, True, True],
                            [True, True, True, True, True, True, False, False],
                            [True, True, True, True, True, True, False, True],
                            [True, True, True, True, True, True, True, False],
                            [True, True, True, True, True, True, True, True],
                        ],
                        coeffs=[
                            0.0625 + 0.0j,
                            0.0 + 0.0625j,
                            0.0 - 0.0625j,
                            0.0625 + 0.0j,
                            0.0 + 0.0625j,
                            -0.0625 + 0.0j,
                            0.0625 + 0.0j,
                            0.0 + 0.0625j,
                            0.0 - 0.0625j,
                            0.0625 + 0.0j,
                            -0.0625 + 0.0j,
                            0.0 - 0.0625j,
                            0.0625 + 0.0j,
                            0.0 + 0.0625j,
                            0.0 - 0.0625j,
                            0.0625 + 0.0j,
                        ],
                    ),
                    coeff=1.0,
                ),
            },
            {},
            {
                "E_0": ((0, ), (1, )),
                "Edag_0": ((1, ), (0, )),
                "E_1": ((2, ), (3, )),
                "Edag_1": ((3, ), (2, )),
                "E_2": ((0, 2), (1, 3)),
                "Edag_2": ((1, 3), (0, 2)),
            },
        )

        hopping_operators = _build_qeom_hopping_ops(self.num_modals,
                                                    self.qubit_converter)
        self.assertEqual(hopping_operators, expected_hopping_operators)
def get_operator(values: List[int], weights: List[int],
                 max_weight: int) -> Tuple[PauliSumOp, float]:
    """
    Generate Hamiltonian for the knapsack problem.

    Notes:
        To build the cost function for the Hamiltonian we add a term S
        that will vary with our solution. In order to make it change wit the solution
        we enhance X with a number of additional bits X' = [x_0,..x_{n-1},y_{n}..y_{n+m-1}].
        The bytes y[i] will be the binary representation of S.
        In this way the optimizer will be able to optimize S as well as X.

        The cost function is
        $$C(X') = M(W_{max} - \\sum_{i=0}^{n-1} x_{i}w_{i} - S)^2 - \\sum_{i}^{n-1} x_{i}v_{i}$$
        where S = sum(2**j * y[j]), j goes from n to n+log(W_max).
        M is a number large enough to dominate the sum of values.

        Because S can only be positive, when W_max >= sum(x[i]*w[i])
        the optimizer can find an S (or better the y[j] that compose S)
        so that it will take the first term to 0.
        This way the function is dominated by the sum of values.
        If W_max < sum(x[i]*w[i]) then the first term can never be 0
        and, multiplied by a large M, will always dominate the function.

        The minimum value of the function will be that where the constraint is respected
        and the sum of values is maximized.

    Args:
        values (list of non-negative integers) : a list of values
        weights (list of non-negative integers) : a list of weights
        max_weight (non negative integer) : the maximum weight the knapsack can carry

    Returns:
        operator for the Hamiltonian
        a constant shift for the obj function.

    Raises:
        ValueError: values and weights have different lengths
        ValueError: A value or a weight is negative
        ValueError: All values are zero
        ValueError: max_weight is negative

    """
    if len(values) != len(weights):
        raise ValueError("The values and weights must have the same length")

    if any(v < 0 for v in values) or any(w < 0 for w in weights):
        raise ValueError("The values and weights cannot be negative")

    if all(v == 0 for v in values):
        raise ValueError("The values cannot all be 0")

    if max_weight < 0:
        raise ValueError("max_weight cannot be negative")

    y_size = int(math.log(max_weight, 2)) + 1 if max_weight > 0 else 1
    n = len(values)
    num_values = n + y_size
    pauli_list = []
    shift = 0

    # pylint: disable=invalid-name
    M = 10 * np.sum(values)

    # term for sum(x_i*w_i)**2
    for i in range(n):
        for j in range(n):
            coefficient = -1 * 0.25 * weights[i] * weights[j] * M
            pauli_op = _get_pauli_op(num_values, [j])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

            pauli_op = _get_pauli_op(num_values, [i])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

            coefficient = -1 * coefficient
            pauli_op = _get_pauli_op(num_values, [i, j])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

    # term for sum(2**j*y_j)**2
    for i in range(y_size):
        for j in range(y_size):
            coefficient = -1 * 0.25 * (2**i) * (2**j) * M

            pauli_op = _get_pauli_op(num_values, [n + j])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

            pauli_op = _get_pauli_op(num_values, [n + i])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

            coefficient = -1 * coefficient
            pauli_op = _get_pauli_op(num_values, [n + i, n + j])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

    # term for -2*W_max*sum(x_i*w_i)
    for i in range(n):
        coefficient = max_weight * weights[i] * M

        pauli_op = _get_pauli_op(num_values, [i])
        pauli_list.append([coefficient, pauli_op])
        shift -= coefficient

    # term for -2*W_max*sum(2**j*y_j)
    for j in range(y_size):
        coefficient = max_weight * (2**j) * M

        pauli_op = _get_pauli_op(num_values, [n + j])
        pauli_list.append([coefficient, pauli_op])
        shift -= coefficient

    for i in range(n):
        for j in range(y_size):
            coefficient = -1 * 0.5 * weights[i] * (2**j) * M

            pauli_op = _get_pauli_op(num_values, [n + j])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

            pauli_op = _get_pauli_op(num_values, [i])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

            coefficient = -1 * coefficient
            pauli_op = _get_pauli_op(num_values, [i, n + j])
            pauli_list.append([coefficient, pauli_op])
            shift -= coefficient

    # term for sum(x_i*v_i)
    for i in range(n):
        coefficient = 0.5 * values[i]

        pauli_op = _get_pauli_op(num_values, [i])
        pauli_list.append([coefficient, pauli_op])
        shift -= coefficient

    opflow_list = [(pauli[1].to_label(), pauli[0]) for pauli in pauli_list]
    return PauliSumOp.from_list(opflow_list), shift
Esempio n. 10
0
 def test_getitem(self):
     """ test get item method """
     target = X + Z
     self.assertEqual(target[0], PauliSumOp(SparsePauliOp(X.primitive)))
     self.assertEqual(target[1], PauliSumOp(SparsePauliOp(Z.primitive)))
Esempio n. 11
0
    def test_adjoint(self):
        """ adjoint test """
        pauli_sum = PauliSumOp(SparsePauliOp(Pauli("XYZX"), coeffs=[2]),
                               coeff=3)
        expected = PauliSumOp(SparsePauliOp(Pauli("XYZX")), coeff=6)

        self.assertEqual(pauli_sum.adjoint(), expected)

        pauli_sum = PauliSumOp(SparsePauliOp(Pauli("XYZY"), coeffs=[2]),
                               coeff=3j)
        expected = PauliSumOp(SparsePauliOp(Pauli("XYZY")), coeff=-6j)
        self.assertEqual(pauli_sum.adjoint(), expected)

        pauli_sum = PauliSumOp(SparsePauliOp(Pauli("X"), coeffs=[1]))
        self.assertEqual(pauli_sum.adjoint(), pauli_sum)

        pauli_sum = PauliSumOp(SparsePauliOp(Pauli("Y"), coeffs=[1]))
        self.assertEqual(pauli_sum.adjoint(), pauli_sum)

        pauli_sum = PauliSumOp(SparsePauliOp(Pauli("Z"), coeffs=[1]))
        self.assertEqual(pauli_sum.adjoint(), pauli_sum)

        pauli_sum = (Z ^ Z) + (Y ^ I)
        self.assertEqual(pauli_sum.adjoint(), pauli_sum)
Esempio n. 12
0
    def test_permute(self):
        """ permute test """
        pauli_sum = PauliSumOp(SparsePauliOp((X ^ Y ^ Z).primitive))
        expected = PauliSumOp(SparsePauliOp((X ^ I ^ Y ^ Z ^ I).primitive))

        self.assertEqual(pauli_sum.permute([1, 2, 4]), expected)
Esempio n. 13
0
    def compute_minimum_eigenvalue(
        self,
        operator: OperatorBase,
        aux_operators: Optional[ListOrDict[OperatorBase]] = None
    ) -> MinimumEigensolverResult:
        super().compute_minimum_eigenvalue(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

        # Convert the gradient operator into a callable function that is compatible with the
        # optimization routine.
        if isinstance(self._gradient, GradientBase):
            gradient = self._gradient.gradient_wrapper(
                ~StateFn(operator) @ StateFn(self._ansatz),
                bind_params=self._ansatz_params,
                backend=self._quantum_instance,
            )
        else:
            gradient = self._gradient

        self._eval_count = 0
        energy_evaluation, expectation = self.get_energy_evaluation(
            operator, return_expectation=True)

        start_time = time()

        # keep this until Optimizer.optimize is removed
        try:
            opt_result = self.optimizer.minimize(fun=energy_evaluation,
                                                 x0=initial_point,
                                                 jac=gradient,
                                                 bounds=bounds)
        except AttributeError:
            # self.optimizer is an optimizer with the deprecated interface that uses
            # ``optimize`` instead of ``minimize```
            warnings.warn(
                "Using an optimizer that is run with the ``optimize`` method is "
                "deprecated as of Qiskit Terra 0.19.0 and will be unsupported no "
                "sooner than 3 months after the release date. Instead use an optimizer "
                "providing ``minimize`` (see qiskit.algorithms.optimizers.Optimizer).",
                DeprecationWarning,
                stacklevel=2,
            )

            opt_result = self.optimizer.optimize(len(initial_point),
                                                 energy_evaluation, gradient,
                                                 bounds, initial_point)

        eval_time = time() - start_time

        result = VQEResult()
        result.optimal_point = opt_result.x
        result.optimal_parameters = dict(zip(self._ansatz_params,
                                             opt_result.x))
        result.optimal_value = opt_result.fun
        result.cost_function_evals = opt_result.nfev
        result.optimizer_time = eval_time
        result.eigenvalue = opt_result.fun + 0j
        result.eigenstate = self._get_eigenstate(result.optimal_parameters)

        logger.info(
            "Optimization complete in %s seconds.\nFound opt_params %s in %s evals",
            eval_time,
            result.optimal_point,
            self._eval_count,
        )

        # TODO delete as soon as get_optimal_vector etc are removed
        self._ret = result

        if aux_operators is not None:
            aux_values = self._eval_aux_ops(opt_result.x,
                                            aux_operators,
                                            expectation=expectation)
            result.aux_operator_eigenvalues = aux_values

        return result
class TestAuxOpsEvaluator(QiskitAlgorithmsTestCase):
    """Tests evaluator of auxiliary operators for algorithms."""
    def setUp(self):
        super().setUp()
        self.seed = 50
        algorithm_globals.random_seed = self.seed
        self.h2_op = (-1.052373245772859 * (I ^ I) + 0.39793742484318045 *
                      (I ^ Z) - 0.39793742484318045 * (Z ^ I) -
                      0.01128010425623538 * (Z ^ Z) + 0.18093119978423156 *
                      (X ^ X))

        self.threshold = 1e-8
        self.backend_names = ["statevector_simulator", "qasm_simulator"]

    def get_exact_expectation(self, ansatz: QuantumCircuit,
                              observables: ListOrDict[OperatorBase]):
        """
        Calculates the exact expectation to be used as an expected result for unit tests.
        """
        if isinstance(observables, dict):
            observables_list = list(observables.values())
        else:
            observables_list = observables

        # the exact value is a list of (mean, variance) where we expect 0 variance
        exact = [(Statevector(ansatz).expectation_value(observable), 0)
                 for observable in observables_list]

        if isinstance(observables, dict):
            return dict(zip(observables.keys(), exact))

        return exact

    def _run_test(
        self,
        expected_result: ListOrDict[Tuple[complex, complex]],
        quantum_state: Union[QuantumCircuit, Statevector],
        decimal: int,
        expectation: ExpectationBase,
        observables: ListOrDict[OperatorBase],
        quantum_instance: Union[QuantumInstance, Backend],
    ):
        result = eval_observables(quantum_instance, quantum_state, observables,
                                  expectation, self.threshold)

        if isinstance(observables, dict):
            np.testing.assert_equal(list(result.keys()),
                                    list(expected_result.keys()))
            np.testing.assert_array_almost_equal(list(result.values()),
                                                 list(
                                                     expected_result.values()),
                                                 decimal=decimal)
        else:
            np.testing.assert_array_almost_equal(result,
                                                 expected_result,
                                                 decimal=decimal)

    @data(
        [
            PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5),
                                  ("XX", -0.5)]),
            PauliSumOp.from_list([("II", 2.0)]),
        ],
        [
            PauliSumOp.from_list([("ZZ", 2.0)]),
        ],
        {
            "op1":
            PauliSumOp.from_list([("II", 2.0)]),
            "op2":
            PauliSumOp.from_list([("II", 0.5), ("ZZ", 0.5), ("YY", 0.5),
                                  ("XX", -0.5)]),
        },
        {
            "op1": PauliSumOp.from_list([("ZZ", 2.0)]),
        },
    )
    def test_eval_observables(self, observables: ListOrDict[OperatorBase]):
        """Tests evaluator of auxiliary operators for algorithms."""

        ansatz = EfficientSU2(2)
        parameters = np.array(
            [
                1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0, 1.2, 4.2, 1.4, 2.0,
                1.2, 4.2, 1.4, 2.0
            ],
            dtype=float,
        )

        bound_ansatz = ansatz.bind_parameters(parameters)
        expected_result = self.get_exact_expectation(bound_ansatz, observables)

        for backend_name in self.backend_names:
            shots = 4096 if backend_name == "qasm_simulator" else 1
            decimal = (1 if backend_name == "qasm_simulator" else 6
                       )  # to accommodate for qasm being imperfect
            with self.subTest(msg=f"Test {backend_name} backend."):
                backend = BasicAer.get_backend(backend_name)
                quantum_instance = QuantumInstance(
                    backend=backend,
                    shots=shots,
                    seed_simulator=self.seed,
                    seed_transpiler=self.seed,
                )
                expectation = ExpectationFactory.build(
                    operator=self.h2_op,
                    backend=quantum_instance,
                )

                with self.subTest(msg="Test QuantumCircuit."):
                    self._run_test(
                        expected_result,
                        bound_ansatz,
                        decimal,
                        expectation,
                        observables,
                        quantum_instance,
                    )

                with self.subTest(msg="Test Statevector."):
                    statevector = Statevector(bound_ansatz)
                    self._run_test(
                        expected_result,
                        statevector,
                        decimal,
                        expectation,
                        observables,
                        quantum_instance,
                    )

                with self.subTest(msg="Test StateFn."):
                    statefn = StateFn(bound_ansatz)
                    self._run_test(
                        expected_result,
                        statefn,
                        decimal,
                        expectation,
                        observables,
                        quantum_instance,
                    )
Esempio n. 15
0
def _two_body(edge_list, p, q, r, s, h2_pqrs):  # pylint: disable=invalid-name
    """
    Map the term a^\\dagger_p a^\\dagger_q a_r a_s + h.c. to qubit operator.

    Args:
        edge_list (numpy.ndarray): 2xE matrix, each indicates (from, to) pair
        p (int): index of the two body term
        q (int): index of the two body term
        r (int): index of the two body term
        s (int): index of the two body term
        h2_pqrs (complex): coefficient of the two body term at (p, q, r, s)

    Returns:
        PauliSumOp: mapped qubit operator
    """
    # Handle case of four unique indices.
    v = np.zeros(edge_list.shape[1])
    id_op = PauliSumOp.from_list([(Pauli(v, v).to_label(), 1)])
    final_coeff = 1.0

    if len(set([p, q, r, s])) == 4:
        b_p = edge_operator_bi(edge_list, p)
        b_q = edge_operator_bi(edge_list, q)
        b_r = edge_operator_bi(edge_list, r)
        b_s = edge_operator_bi(edge_list, s)
        a_pq = edge_operator_aij(edge_list, p, q)
        a_rs = edge_operator_aij(edge_list, r, s)
        a_pq = -a_pq if q < p else a_pq
        a_rs = -a_rs if s < r else a_rs

        qubit_op = (a_pq * a_rs) * (-id_op - b_p * b_q + b_p * b_r +
                                    b_p * b_s + b_q * b_r + b_q * b_s -
                                    b_r * b_s - b_p * b_q * b_r * b_s)
        final_coeff = 0.125

    # Handle case of three unique indices.
    elif len(set([p, q, r, s])) == 3:
        b_p = edge_operator_bi(edge_list, p)
        b_q = edge_operator_bi(edge_list, q)
        if p == r:
            b_s = edge_operator_bi(edge_list, s)
            a_qs = edge_operator_aij(edge_list, q, s)
            a_qs = -a_qs if s < q else a_qs
            qubit_op = (a_qs * b_s + b_q * a_qs) * (id_op - b_p)
            final_coeff = 1j * 0.25
        elif p == s:
            b_r = edge_operator_bi(edge_list, r)
            a_qr = edge_operator_aij(edge_list, q, r)
            a_qr = -a_qr if r < q else a_qr
            qubit_op = (a_qr * b_r + b_q * a_qr) * (id_op - b_p)
            final_coeff = 1j * -0.25
        elif q == r:
            b_s = edge_operator_bi(edge_list, s)
            a_ps = edge_operator_aij(edge_list, p, s)
            a_ps = -a_ps if s < p else a_ps
            qubit_op = (a_ps * b_s + b_p * a_ps) * (id_op - b_q)
            final_coeff = 1j * -0.25
        elif q == s:
            b_r = edge_operator_bi(edge_list, r)
            a_pr = edge_operator_aij(edge_list, p, r)
            a_pr = -a_pr if r < p else a_pr
            qubit_op = (a_pr * b_r + b_p * a_pr) * (id_op - b_q)
            final_coeff = 1j * 0.25
        else:
            pass

    # Handle case of two unique indices.
    elif len(set([p, q, r, s])) == 2:
        b_p = edge_operator_bi(edge_list, p)
        b_q = edge_operator_bi(edge_list, q)
        qubit_op = (id_op - b_p) * (id_op - b_q)
        if p == s:
            final_coeff = 0.25
        else:
            final_coeff = -0.25
    else:
        pass

    qubit_op = (final_coeff * h2_pqrs) * qubit_op
    qubit_op.simplify()
    return qubit_op
Esempio n. 16
0
    def test_build_hopping_operators(self):
        """Tests that the correct hopping operator is built from QMolecule."""
        # TODO extract it somewhere
        expected_hopping_operators = (
            {
                "E_0":
                PauliSumOp.from_list([("IIXX", 0.25), ("IIYX", 0.25j),
                                      ("IIXY", -0.25j), ("IIYY", 0.25)]),
                "Edag_0":
                PauliSumOp.from_list([("IIXX", 0.25), ("IIYX", -0.25j),
                                      ("IIXY", 0.25j), ("IIYY", 0.25)]),
                "E_1":
                PauliSumOp.from_list([("XXII", 0.25), ("YXII", 0.25j),
                                      ("XYII", -0.25j), ("YYII", 0.25)]),
                "Edag_1":
                PauliSumOp.from_list([("XXII", 0.25), ("YXII", -0.25j),
                                      ("XYII", 0.25j), ("YYII", 0.25)]),
                "E_2":
                PauliSumOp.from_list([
                    ("XXXX", 0.0625),
                    ("YXXX", 0.0625j),
                    ("XYXX", -0.0625j),
                    ("YYXX", 0.0625),
                    ("XXYX", 0.0625j),
                    ("YXYX", -0.0625),
                    ("XYYX", 0.0625),
                    ("YYYX", 0.0625j),
                    ("XXXY", -0.0625j),
                    ("YXXY", 0.0625),
                    ("XYXY", -0.0625),
                    ("YYXY", -0.0625j),
                    ("XXYY", 0.0625),
                    ("YXYY", 0.0625j),
                    ("XYYY", -0.0625j),
                    ("YYYY", 0.0625),
                ]),
                "Edag_2":
                PauliSumOp.from_list([
                    ("XXXX", 0.0625),
                    ("YXXX", -0.0625j),
                    ("XYXX", 0.0625j),
                    ("YYXX", 0.0625),
                    ("XXYX", -0.0625j),
                    ("YXYX", -0.0625),
                    ("XYYX", 0.0625),
                    ("YYYX", -0.0625j),
                    ("XXXY", 0.0625j),
                    ("YXXY", 0.0625),
                    ("XYXY", -0.0625),
                    ("YYXY", 0.0625j),
                    ("XXYY", 0.0625),
                    ("YXYY", -0.0625j),
                    ("XYYY", 0.0625j),
                    ("YYYY", 0.0625),
                ]),
            },
            {},
            {
                "E_0": ((0, ), (1, )),
                "Edag_0": ((1, ), (0, )),
                "E_1": ((2, ), (3, )),
                "Edag_1": ((3, ), (2, )),
                "E_2": ((0, 2), (1, 3)),
                "Edag_2": ((1, 3), (0, 2)),
            },
        )

        hopping_operators = _build_qeom_hopping_ops(self.num_modals,
                                                    self.qubit_converter)
        self.assertEqual(hopping_operators, expected_hopping_operators)
Esempio n. 17
0
class TestEvolutionGate(QiskitTestCase):
    """Test the evolution gate."""
    def setUp(self):
        super().setUp()
        # fix random seed for reproducibility (used in QDrift)
        algorithm_globals.random_seed = 2

    def test_matrix_decomposition(self):
        """Test the default decomposition."""
        op = (X ^ 3) + (Y ^ 3) + (Z ^ 3)
        time = 0.123

        matrix = op.to_matrix()
        evolved = scipy.linalg.expm(-1j * time * matrix)

        evo_gate = PauliEvolutionGate(op, time, synthesis=MatrixExponential())

        self.assertTrue(Operator(evo_gate).equiv(evolved))

    def test_lie_trotter(self):
        """Test constructing the circuit with Lie Trotter decomposition."""
        op = (X ^ 3) + (Y ^ 3) + (Z ^ 3)
        time = 0.123
        reps = 4
        evo_gate = PauliEvolutionGate(op,
                                      time,
                                      synthesis=LieTrotter(reps=reps))
        decomposed = evo_gate.definition.decompose()
        self.assertEqual(decomposed.count_ops()["cx"], reps * 3 * 4)

    def test_rzx_order(self):
        """Test ZX is mapped onto the correct qubits."""
        evo_gate = PauliEvolutionGate(X ^ Z)
        decomposed = evo_gate.definition.decompose()

        ref = QuantumCircuit(2)
        ref.h(1)
        ref.cx(1, 0)
        ref.rz(2.0, 0)
        ref.cx(1, 0)
        ref.h(1)

        # don't use circuit equality since RZX here decomposes with RZ on the bottom
        self.assertTrue(Operator(decomposed).equiv(ref))

    def test_suzuki_trotter(self):
        """Test constructing the circuit with Lie Trotter decomposition."""
        op = (X ^ 3) + (Y ^ 3) + (Z ^ 3)
        time = 0.123
        reps = 4
        for order in [2, 4, 6]:
            if order == 2:
                expected_cx = reps * 5 * 4
            elif order % 2 == 0:
                # recurse (order - 2) / 2 times, base case has 5 blocks with 4 CX each
                expected_cx = reps * 5**((order - 2) / 2) * 5 * 4
            else:
                # recurse (order - 1) / 2 times, base case has 3 blocks with 4 CX each
                expected_cx = reps * 5**((order - 1) / 2) * 3 * 4

            with self.subTest(order=order):
                evo_gate = PauliEvolutionGate(op,
                                              time,
                                              synthesis=SuzukiTrotter(
                                                  order=order, reps=reps))
                decomposed = evo_gate.definition.decompose()
                self.assertEqual(decomposed.count_ops()["cx"], expected_cx)

    def test_suzuki_trotter_manual(self):
        """Test the evolution circuit of Suzuki Trotter against a manually constructed circuit."""
        op = X + Y
        time = 0.1
        reps = 1
        evo_gate = PauliEvolutionGate(op,
                                      time,
                                      synthesis=SuzukiTrotter(order=4,
                                                              reps=reps))

        # manually construct expected evolution
        expected = QuantumCircuit(1)
        p_4 = 1 / (4 - 4**(1 / 3)
                   )  # coefficient for reduced time from Suzuki paper
        for _ in range(2):
            # factor of 1/2 already included with factor 1/2 in rotation gate
            expected.rx(p_4 * time, 0)
            expected.ry(2 * p_4 * time, 0)
            expected.rx(p_4 * time, 0)

        expected.rx((1 - 4 * p_4) * time, 0)
        expected.ry(2 * (1 - 4 * p_4) * time, 0)
        expected.rx((1 - 4 * p_4) * time, 0)

        for _ in range(2):
            expected.rx(p_4 * time, 0)
            expected.ry(2 * p_4 * time, 0)
            expected.rx(p_4 * time, 0)

        self.assertEqual(evo_gate.definition.decompose(), expected)

    @data(
        (X + Y, 0.5, 1, [(Pauli("X"), 0.5), (Pauli("X"), 0.5)]),
        (X, 0.238, 2, [(Pauli("X"), 0.238)]),
    )
    @unpack
    def test_qdrift_manual(self, op, time, reps, sampled_ops):
        """Test the evolution circuit of Suzuki Trotter against a manually constructed circuit."""
        qdrift = QDrift(reps=reps)
        evo_gate = PauliEvolutionGate(op, time, synthesis=qdrift)
        evo_gate.definition.decompose()

        # manually construct expected evolution
        expected = QuantumCircuit(1)
        for pauli in sampled_ops:
            if pauli[0].to_label() == "X":
                expected.rx(2 * pauli[1], 0)
            elif pauli[0].to_label() == "Y":
                expected.ry(2 * pauli[1], 0)

        self.assertTrue(Operator(evo_gate.definition).equiv(expected))

    def test_qdrift_evolution(self):
        """Test QDrift on an example."""
        op = 0.1 * (Z ^ Z) + (X ^ I) + (I ^ X) + 0.2 * (X ^ X)
        reps = 20
        qdrift = PauliEvolutionGate(op,
                                    time=0.5 / reps,
                                    synthesis=QDrift(reps=reps)).definition
        exact = scipy.linalg.expm(-0.5j * op.to_matrix()).dot(np.eye(4)[0, :])

        def energy(evo):
            return Statevector(evo).expectation_value(op.to_matrix())

        self.assertAlmostEqual(energy(exact), energy(qdrift), places=2)

    def test_passing_grouped_paulis(self):
        """Test passing a list of already grouped Paulis."""
        grouped_ops = [(X ^ Y) + (Y ^ X), (Z ^ I) + (Z ^ Z) + (I ^ Z), (X ^ X)]
        evo_gate = PauliEvolutionGate(grouped_ops,
                                      time=0.12,
                                      synthesis=LieTrotter())
        decomposed = evo_gate.definition.decompose()
        self.assertEqual(decomposed.count_ops()["rz"], 4)
        self.assertEqual(decomposed.count_ops()["rzz"], 1)
        self.assertEqual(decomposed.count_ops()["rxx"], 1)

    def test_list_from_grouped_paulis(self):
        """Test getting a string representation from grouped Paulis."""
        grouped_ops = [(X ^ Y) + (Y ^ X), (Z ^ I) + (Z ^ Z) + (I ^ Z), (X ^ X)]
        evo_gate = PauliEvolutionGate(grouped_ops,
                                      time=0.12,
                                      synthesis=LieTrotter())

        pauli_strings = []
        for op in evo_gate.operator:
            if isinstance(op, SparsePauliOp):
                pauli_strings.append(op.to_list())
            else:
                pauli_strings.append([(str(op), 1 + 0j)])

        expected = [
            [("XY", 1 + 0j), ("YX", 1 + 0j)],
            [("ZI", 1 + 0j), ("ZZ", 1 + 0j), ("IZ", 1 + 0j)],
            [("XX", 1 + 0j)],
        ]
        self.assertListEqual(pauli_strings, expected)

    def test_dag_conversion(self):
        """Test constructing a circuit with evolutions yields a DAG with evolution blocks."""
        time = Parameter("t")
        evo = PauliEvolutionGate((Z ^ 2) + (X ^ 2), time=time)

        circuit = QuantumCircuit(2)
        circuit.h(circuit.qubits)
        circuit.append(evo, circuit.qubits)
        circuit.cx(0, 1)

        dag = circuit_to_dag(circuit)

        expected_ops = {"HGate", "CXGate", "PauliEvolutionGate"}
        ops = {node.op.__class__.__name__ for node in dag.op_nodes()}

        self.assertEqual(ops, expected_ops)

    @data("chain", "fountain")
    def test_cnot_chain_options(self, option):
        """Test selecting different kinds of CNOT chains."""

        op = Z ^ Z ^ Z
        synthesis = LieTrotter(reps=1, cx_structure=option)
        evo = PauliEvolutionGate(op, synthesis=synthesis)

        expected = QuantumCircuit(3)
        if option == "chain":
            expected.cx(2, 1)
            expected.cx(1, 0)
        else:
            expected.cx(1, 0)
            expected.cx(2, 0)

        expected.rz(2, 0)

        if option == "chain":
            expected.cx(1, 0)
            expected.cx(2, 1)
        else:
            expected.cx(2, 0)
            expected.cx(1, 0)

        self.assertEqual(expected, evo.definition)

    @data(
        Pauli("XI"),
        X ^ I,  # PauliOp
        SparsePauliOp(Pauli("XI")),
        PauliSumOp(SparsePauliOp("XI")),
    )
    def test_different_input_types(self, op):
        """Test all different supported input types and that they yield the same."""
        expected = QuantumCircuit(2)
        expected.rx(4, 1)

        with self.subTest(msg="plain"):
            evo = PauliEvolutionGate(op, time=2, synthesis=LieTrotter())
            self.assertEqual(evo.definition, expected)

        with self.subTest(msg="wrapped in list"):
            evo = PauliEvolutionGate([op], time=2, synthesis=LieTrotter())
            self.assertEqual(evo.definition, expected)

    def test_pauliop_coefficients_respected(self):
        """Test that global ``PauliOp`` coefficients are being taken care of."""
        evo = PauliEvolutionGate(5 * (Z ^ I), time=1, synthesis=LieTrotter())
        circuit = evo.definition.decompose()
        rz_angle = circuit.data[0][0].params[0]
        self.assertEqual(rz_angle, 10)

    def test_paulisumop_coefficients_respected(self):
        """Test that global ``PauliSumOp`` coefficients are being taken care of."""
        evo = PauliEvolutionGate(5 * (2 * X + 3 * Y - Z),
                                 time=1,
                                 synthesis=LieTrotter())
        circuit = evo.definition.decompose()
        rz_angles = [
            circuit.data[0][0].params[0],  # X
            circuit.data[1][0].params[0],  # Y
            circuit.data[2][0].params[0],  # Z
        ]
        self.assertListEqual(rz_angles, [20, 30, -10])
Esempio n. 18
0
    def _linear_encoding(self, spin: Union[Fraction,
                                           float]) -> List[PauliSumOp]:
        """
        Generates a 'linear_encoding' of the spin S operators 'X', 'Y', 'Z' and 'identity'
        to qubit operators (linear combinations of pauli strings).
        In this 'linear_encoding' each individual spin S system is represented via
        2S+1 qubits and the state |s> is mapped to the state |00...010..00>, where the s-th qubit is
        in state 1.

        Returns:
            The 4-element list of transformed spin S 'X', 'Y', 'Z' and 'identity' operators.
            I.e. spin_op_encoding[0]` corresponds to the linear combination of pauli strings needed
            to represent the embedded 'X' operator
        """

        spin_op_encoding: List[PauliSumOp] = []
        dspin = int(2 * spin + 1)
        nqubits = dspin

        # quick functions to generate a pauli with X / Y / Z at location `i`
        pauli_id = Pauli("I" * nqubits)

        def pauli_x(i):
            return Pauli("I" * i + "X" + "I" * (nqubits - i - 1))

        def pauli_y(i):
            return Pauli("I" * i + "Y" + "I" * (nqubits - i - 1))

        def pauli_z(i):
            return Pauli("I" * i + "Z" + "I" * (nqubits - i - 1))

        # 1. build the non-diagonal X operator
        x_summands = []
        for i, coeff in enumerate(
                np.diag(SpinOp("X", spin=spin).to_matrix(), 1)):
            x_summands.append(
                PauliSumOp(coeff / 2.0 *
                           SparsePauliOp(pauli_x(i).dot(pauli_x(i + 1))) +
                           coeff / 2.0 *
                           SparsePauliOp(pauli_y(i).dot(pauli_y(i + 1)))))
        spin_op_encoding.append(reduce(operator.add, x_summands))

        # 2. build the non-diagonal Y operator
        y_summands = []
        for i, coeff in enumerate(
                np.diag(SpinOp("Y", spin=spin).to_matrix(), 1)):
            y_summands.append(
                PauliSumOp(-1j * coeff / 2.0 *
                           SparsePauliOp(pauli_x(i).dot(pauli_y(i + 1))) +
                           1j * coeff / 2.0 *
                           SparsePauliOp(pauli_y(i).dot(pauli_x(i + 1)))))
        spin_op_encoding.append(reduce(operator.add, y_summands))

        # 3. build the diagonal Z
        z_summands = []
        for i, coeff in enumerate(np.diag(SpinOp("Z", spin=spin).to_matrix())):
            # get the first upper diagonal of coeff.
            z_summands.append(
                PauliSumOp(coeff / 2.0 * SparsePauliOp(pauli_z(i)) +
                           coeff / 2.0 * SparsePauliOp(pauli_id)))

        z_operator = reduce(operator.add, z_summands)
        spin_op_encoding.append(z_operator)

        # 4. add the identity operator
        spin_op_encoding.append(PauliSumOp(1.0 * SparsePauliOp(pauli_id)))

        # return the lookup table for the transformed XYZI operators
        return spin_op_encoding