Exemplo n.º 1
0
def get_local_zero_state_operator(number_of_qubits: int):
    operator = IsingOperator("")
    for qubit_index in range(number_of_qubits):
        operator -= IsingOperator("Z{}".format(qubit_index),
                                  1 / (number_of_qubits))

    save_qubit_operator(operator, "qubit-operator.json")
Exemplo n.º 2
0
def get_context_selection_circuit(
        term: Tuple[Tuple[int, str], ...]) -> Tuple[Circuit, IsingOperator]:
    """Get the context selection circuit for measuring the expectation value
    of a Pauli term.

    Args:
        term: The Pauli term, expressed using the OpenFermion convention.
    
    Returns:
        Tuple containing:
        - The context selection circuit.
        - The frame operator
    """

    context_selection_circuit = Circuit()
    operator = IsingOperator(())
    for factor in term:
        if factor[1] == "X":
            context_selection_circuit += Circuit(
                pyquil.gates.RY(-np.pi / 2, factor[0]))
        elif factor[1] == "Y":
            context_selection_circuit += Circuit(
                pyquil.gates.RX(np.pi / 2, factor[0]))
        operator *= IsingOperator((factor[0], "Z"))

    return context_selection_circuit, operator
Exemplo n.º 3
0
def get_context_selection_circuit_for_group(
    qubit_operator: QubitOperator,
) -> Tuple[Circuit, IsingOperator]:
    """Get the context selection circuit for measuring the expectation value
    of a group of co-measurable Pauli terms.

    Args:
        qubit_operator: operator representing group of co-measurable Pauli term
    """
    context_selection_circuit = Circuit()
    transformed_operator = IsingOperator()
    context: List[Tuple[int, str]] = []

    for term in qubit_operator.terms:
        term_operator = IsingOperator(())
        for qubit, operator in term:
            for existing_qubit, existing_operator in context:
                if existing_qubit == qubit and existing_operator != operator:
                    raise ValueError("Terms are not co-measurable")
            if (qubit, operator) not in context:
                context.append((qubit, operator))
            term_operator *= IsingOperator((qubit, "Z"))
        transformed_operator += term_operator * qubit_operator.terms[term]

    for factor in context:
        if factor[1] == "X":
            context_selection_circuit += RY(-np.pi / 2)(factor[0])
        elif factor[1] == "Y":
            context_selection_circuit += RX(np.pi / 2)(factor[0])

    return context_selection_circuit, transformed_operator
Exemplo n.º 4
0
    def frame_operators(self):
        operators = [
            2.0 * IsingOperator((1, "Z")) * IsingOperator((2, "Z")),
            1.0 * IsingOperator((3, "Z")) * IsingOperator((0, "Z")),
            -1.0 * IsingOperator((2, "Z")),
        ]

        return operators
Exemplo n.º 5
0
 def estimation_tasks(self):
     task_1 = EstimationTask(IsingOperator("Z0"),
                             circuit=Circuit(Program(X(0))),
                             number_of_shots=10)
     task_2 = EstimationTask(
         IsingOperator("Z0"),
         circuit=Circuit(Program(RZ(np.pi / 2, 0))),
         number_of_shots=20,
     )
     task_3 = EstimationTask(
         IsingOperator((), coefficient=2.0),
         circuit=Circuit(Program(RY(np.pi / 4, 0))),
         number_of_shots=30,
     )
     return [task_1, task_2, task_3]
Exemplo n.º 6
0
    def test_get_expectation_values_for_circuitset(self, backend):
        # Given
        backend.number_of_circuits_run = 0
        backend.number_of_jobs_run = 0
        num_circuits = 10
        circuitset = [
            Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2)))
            for _ in range(num_circuits)
        ]
        operator = IsingOperator("[]")
        target_expectation_values = np.array([1])

        # When
        backend.n_samples = 1
        expectation_values_set = backend.get_expectation_values_for_circuitset(
            circuitset, operator)

        # Then
        assert len(expectation_values_set) == num_circuits

        for expectation_values in expectation_values_set:
            assert isinstance(expectation_values, ExpectationValues)
            assert isinstance(expectation_values.values, np.ndarray)
            assert expectation_values.values == pytest.approx(
                target_expectation_values, abs=1e-15)
        assert backend.number_of_circuits_run == num_circuits
        if backend.supports_batching:
            assert backend.number_of_jobs_run == int(
                np.ceil(num_circuits / backend.batch_size))
        else:
            assert backend.number_of_jobs_run == num_circuits
Exemplo n.º 7
0
    def test_get_expectation_values_for_circuitset(self):
        # Given
        num_circuits = 10
        circuitset = [
            Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2))) for _ in range(num_circuits)
        ]
        operator = IsingOperator("[]")
        target_expectation_values = np.array([1])

        # When
        for backend in self.backends:
            backend.n_samples = 1
            expectation_values_set = backend.get_expectation_values_for_circuitset(
                circuitset, operator
            )

            # Then
            self.assertEqual(len(expectation_values_set), num_circuits)

            for expectation_values in expectation_values_set:
                self.assertIsInstance(expectation_values, ExpectationValues)
                self.assertIsInstance(expectation_values.values, np.ndarray)
                np.testing.assert_array_almost_equal(
                    expectation_values.values, target_expectation_values, decimal=15
                )
Exemplo n.º 8
0
def convert_qubo_to_openfermion_ising(
        qubo: BinaryQuadraticModel) -> IsingOperator:
    """Converts dimod BinaryQuadraticModel to OpenFermion IsingOperator object.

    The resulting Openfermion IsingOperator has the following property:
    For every bitstring, its expected value is the same as the energy of the original QUBO.
    In order to ensure this, we had to add a minus sign for the coefficients
    of the linear terms coming from dimod conversion.
    For more context about conventions used please refer to note in `convert_measurements_to_sampleset` docstring.

    Args:
        qubo: Object we want to convert

    Returns:
        IsingOperator: IsingOperator representation of the input qubo.

    """
    linear_coeffs, quadratic_coeffs, offset = qubo.to_ising()

    list_of_ising_strings = [f"{offset}[]"]

    for i, value in linear_coeffs.items():
        list_of_ising_strings.append(f"{-value}[Z{i}]")

    for (i, j), value in quadratic_coeffs.items():
        list_of_ising_strings.append(f"{value}[Z{i} Z{j}]")

    ising_string = " + ".join(list_of_ising_strings)
    return IsingOperator(ising_string)
def _validate_expectation_value_includes_coefficients(
    estimator: EstimateExpectationValues, ):
    estimation_tasks = [
        EstimationTask(IsingOperator("Z0"), Circuit([RX(np.pi / 3)(0)]),
                       10000),
        EstimationTask(IsingOperator("Z0", 19.971997),
                       Circuit([RX(np.pi / 3)(0)]), 10000),
    ]

    expectation_values = estimator(
        backend=_backend,
        estimation_tasks=estimation_tasks,
    )

    return not np.array_equal(expectation_values[0].values,
                              expectation_values[1].values)
Exemplo n.º 10
0
 def test_get_expectation_values_empty_op(self, backend):
     # Given
     circuit = Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2)))
     operator = IsingOperator()
     # When
     backend.n_samples = 1
     expectation_values = backend.get_expectation_values(circuit, operator)
     # Then
     assert expectation_values.values == pytest.approx(0.0, abs=1e-7)
def _validate_constant_terms_are_included_in_output(
    estimator: EstimateExpectationValues, ):
    estimation_tasks = [
        EstimationTask(IsingOperator("Z0"), Circuit([H(0)]), 10000),
        EstimationTask(
            IsingOperator("Z0") + IsingOperator("[]", 19.971997),
            Circuit([H(0)]),
            10000,
        ),
    ]

    expectation_values = estimator(
        backend=_backend,
        estimation_tasks=estimation_tasks,
    )

    return not np.array_equal(expectation_values[0].values,
                              expectation_values[1].values)
Exemplo n.º 12
0
    def test_change_operator_type(self):
        # Given
        operator1 = QubitOperator('Z0 Z1', 4.5)
        operator2 = IsingOperator('Z0 Z1', 4.5)
        operator3 = IsingOperator()
        operator4 = IsingOperator('Z0', 0.5) + IsingOperator('Z1', 2.5)
        # When
        new_operator1 = change_operator_type(operator1, IsingOperator)
        new_operator2 = change_operator_type(operator2, QubitOperator)
        new_operator3 = change_operator_type(operator3, QubitOperator)
        new_operator4 = change_operator_type(operator4, QubitOperator)

        # Then
        self.assertEqual(IsingOperator('Z0 Z1', 4.5), new_operator1)
        self.assertEqual(QubitOperator('Z0 Z1', 4.5), new_operator2)
        self.assertEqual(QubitOperator(), new_operator3)
        self.assertEqual(
            QubitOperator('Z0', 0.5) + QubitOperator('Z1', 2.5), new_operator4)
def _create_reduced_hamiltonian(
    hamiltonian: IsingOperator,
    term_with_largest_expval: IsingOperator,
    largest_expval: float,
) -> IsingOperator:
    """Reduce the cost hamiltonian by substituting one qubit of the term with largest expectation
    value with the other qubit of the term. See equation (15) of the original paper.

    Args:
        hamiltonian: hamiltonian to be reduced
        term_with_largest_expval: term with largest expectation value
        largest_expval: the expectation value of `term_with_largest_expval`

    Returns:
        Reduced hamiltonian.
    """
    assert len(term_with_largest_expval.terms.keys()) == 1

    reduced_hamiltonian = IsingOperator()

    qubit_to_get_rid_of: int = [*term_with_largest_expval.terms][0][1][0]

    for (term, coefficient) in hamiltonian.terms.items():
        # term is tuple representing one term of IsingOperator, example ((2, 'Z'), (3, 'Z'))
        if term not in term_with_largest_expval.terms:
            # If term is not the term_with_largest_expval
            new_term: Tuple = ()
            for qubit in term:
                # qubit is a component of qubit operator on 1 qubit ex. (2, 'Z')
                qubit_indice: int = qubit[0]

                # Map the new cost hamiltonian onto reduced qubits
                new_qubit_indice = _get_new_qubit_indice(
                    qubit_indice, term_with_largest_expval
                )
                new_qubit = (new_qubit_indice, "Z")
                new_term += (new_qubit,)

                if qubit_indice == qubit_to_get_rid_of:
                    coefficient *= np.sign(largest_expval)

            reduced_hamiltonian += IsingOperator(new_term, coefficient)

    return reduced_hamiltonian
Exemplo n.º 14
0
 def test_get_expectation_values_empty_op(self):
     # Given
     circuit = Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2)))
     operator = IsingOperator()
     # When
     for backend in self.backends:
         backend.n_samples = 1
         expectation_values = backend.get_expectation_values(circuit, operator)
         # Then
         self.assertAlmostEqual(sum(expectation_values.values), 0.0)
Exemplo n.º 15
0
    def test_isingop_to_dict_io(self):
        # Given
        ising_op = IsingOperator("[] + 3[Z0 Z1] + [Z1 Z2]")

        # When
        isingop_dict = convert_isingop_to_dict(ising_op)
        recreated_isingop = convert_dict_to_isingop(isingop_dict)

        # Then
        self.assertEqual(recreated_isingop, ising_op)
Exemplo n.º 16
0
    def test_isingop_io(self):
        # Given
        ising_op = IsingOperator("[] + 3[Z0 Z1] + [Z1 Z2]")

        # When
        save_ising_operator(ising_op, "ising_op.json")
        loaded_op = load_ising_operator("ising_op.json")

        # Then
        self.assertEqual(ising_op, loaded_op)
        os.remove("ising_op.json")
Exemplo n.º 17
0
def get_context_selection_circuit(
    operator: QubitOperator,
) -> Tuple[Circuit, IsingOperator]:
    """Get the context selection circuit for measuring the expectation value
    of a Pauli term.

    Args:
        operator: operator consisting of a single Pauli Term

    """
    context_selection_circuit = Circuit()
    output_operator = IsingOperator(())
    terms = list(operator.terms.keys())[0]
    for term in terms:
        term_type = term[1]
        qubit_id = term[0]
        if term_type == "X":
            context_selection_circuit += Circuit(pyquil.gates.RY(-np.pi / 2, qubit_id))
        elif term_type == "Y":
            context_selection_circuit += Circuit(pyquil.gates.RX(np.pi / 2, qubit_id))
        output_operator *= IsingOperator((qubit_id, "Z"))
    return context_selection_circuit, output_operator
Exemplo n.º 18
0
def get_context_selection_circuit_for_group(
    qubit_operator: QubitOperator, ) -> Tuple[Circuit, IsingOperator]:
    """Get the context selection circuit for measuring the expectation value
    of a group of co-measurable Pauli terms.

    Args:
        term: The Pauli term, expressed using the OpenFermion convention.
    
    Returns:
        Tuple containing:
        - The context selection circuit.
        - The frame operator
    """

    context_selection_circuit = Circuit()
    transformed_operator = IsingOperator()
    context = []

    for term in qubit_operator.terms:
        term_operator = IsingOperator(())
        for qubit, operator in term:
            for existing_qubit, existing_operator in context:
                if existing_qubit == qubit and existing_operator != operator:
                    raise ValueError("Terms are not co-measurable")
            if not (qubit, operator) in context:
                context.append((qubit, operator))
            term_operator *= IsingOperator((qubit, "Z"))
        transformed_operator += term_operator * qubit_operator.terms[term]

    for factor in context:
        if factor[1] == "X":
            context_selection_circuit += Circuit(
                pyquil.gates.RY(-np.pi / 2, factor[0]))
        elif factor[1] == "Y":
            context_selection_circuit += Circuit(
                pyquil.gates.RX(np.pi / 2, factor[0]))

    return context_selection_circuit, transformed_operator
Exemplo n.º 19
0
 def test_get_expectation_values_identity(self, backend):
     # Given
     circuit = Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2)))
     operator = IsingOperator("[]")
     target_expectation_values = np.array([1])
     n_samples = 1
     # When
     backend.n_samples = 1
     expectation_values = backend.get_expectation_values(circuit, operator)
     # Then
     assert isinstance(expectation_values, ExpectationValues)
     assert isinstance(expectation_values.values, np.ndarray)
     assert expectation_values.values == pytest.approx(
         target_expectation_values, abs=1e-15
     )
Exemplo n.º 20
0
 def test_get_expectation_values_identity(self):
     # Given
     circuit = Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2)))
     operator = IsingOperator('[]')
     target_expectation_values = np.array([1])
     n_samples = 1
     # When
     for backend in self.backends:
         backend.n_samples = 1
         expectation_values = backend.get_expectation_values(
             circuit, operator)
         # Then
         self.assertIsInstance(expectation_values, ExpectationValues)
         np.testing.assert_array_almost_equal(expectation_values.values,
                                              target_expectation_values,
                                              decimal=15)
Exemplo n.º 21
0
def convert_qubo_to_openfermion_ising(qubo: BinaryQuadraticModel):
    """Converts dimod BinaryQuadraticModel to OpenFermion IsingOperator object.

    Args:
        qubo: Object we want to convert

    Returns:
        IsingOperator: IsingOperator representation of the input qubo.

    """
    linear_coeffs, quadratic_coeffs, offset = qubo.to_ising()

    list_of_ising_strings = [f"{offset}[]"]

    for i, value in linear_coeffs.items():
        list_of_ising_strings.append(f"{value}[Z{i}]")

    for (i, j), value in quadratic_coeffs.items():
        list_of_ising_strings.append(f"{value}[Z{i} Z{j}]")

    ising_string = " + ".join(list_of_ising_strings)
    return IsingOperator(ising_string)
Exemplo n.º 22
0
    def test_get_expectation_values_for_circuitset(self, backend):
        # Given
        num_circuits = 10
        circuitset = [
            Circuit(Program(H(0), CNOT(0, 1), CNOT(1, 2))) for _ in range(num_circuits)
        ]
        operator = IsingOperator("[]")
        target_expectation_values = np.array([1])

        # When
        backend.n_samples = 1
        expectation_values_set = backend.get_expectation_values_for_circuitset(
            circuitset, operator
        )

        # Then
        assert len(expectation_values_set) == num_circuits

        for expectation_values in expectation_values_set:
            assert isinstance(expectation_values, ExpectationValues)
            assert isinstance(expectation_values.values, np.ndarray)
            assert expectation_values.values == pytest.approx(
                target_expectation_values, abs=1e-15
            )
Exemplo n.º 23
0
def run_circuit_and_get_expval(
    backend_specs: dict,
    circuit: str,
    operators: str,
):
    """Takes a circuit to obtain the expectation value of an operator on a
    given backend.

    All backend calls used in this function are defined as ``QuantumBackend``
    and ``QuantumSimulator`` interface standard methods implemented are defined
    in the ``z-quantum-core`` repository.

    There are two computation modes: sampling and exact. Expectation values are
    computed by post-processing samples when an Orquestra ``QuantumBackend`` is
    used or the number of samples was specified for a ``QuantumSimulator``
    backend. When the number of samples was not specified, ``QuantumSimulator``
    backends run in exact mode.

    Args:
        backend_specs (dict): the parsed Orquestra backend specifications
        circuit (str): the circuit represented as an OpenQASM 2.0 program
        operators (str): the operator in an ``openfermion.QubitOperator``
            or ``openfermion.IsingOperator`` representation
    """
    backend_specs = json.loads(backend_specs)
    operators = json.loads(operators)

    backend = create_object(backend_specs)

    # 1. Parse circuit
    qc = QuantumCircuit.from_qasm_str(circuit)

    # 2. Create operators
    ops = []
    for op in operators:
        if backend.n_samples is not None:
            # Operator for Backend/Simulator in sampling mode
            ops.append(IsingOperator(op))
        else:
            # Operator for Simulator exact mode
            ops.append(QubitOperator(op))

    # 2.+1
    # Activate the qubits that are measured but were not acted on
    # By applying the identity
    # Note: this is a temporary logic subject to be removed once supported by
    # Orquestra

    # Get the active qubits of the circuit
    active_qubits = []
    for instr in qc.data:
        instruction_qubits = [qubit.index for qubit in instr[1]]
        active_qubits.extend(instruction_qubits)

    active_qubits = set(active_qubits)

    # Get the qubits we'd like to measure
    # Data for identities is not stored, need to account for empty terms
    op_qubits = [term[0][0] for op in ops for term in op.terms if term]

    need_to_activate = set(op_qubits) - active_qubits
    if not need_to_activate == set():
        for qubit in need_to_activate:
            # Apply the identity
            qc.id(qubit)

    # If there are still no instructions, apply identity to the first qubit
    # Can happen for an empty circuit when measuring the identity operator
    if not qc.data:
        qc.id(qc.qubits[0])

    # Convert to zquantum.core.circuit.Circuit
    circuit = Circuit(qc)

    # 3. Expval
    results = _get_expval(backend, circuit, ops)

    save_list(results, "expval.json")
Exemplo n.º 24
0
def run_circuit_and_get_expval(
    backend_specs: str,
    circuit: str,
    operators: str,
):
    """Executes a circuit to obtain the expectation value of an operator on a
    given backend.

    All Orquestra backend interface calls used in this function are standard
    methods of the ``QuantumBackend`` and ``QuantumSimulator`` interfaces as
    defined in the ``z-quantum-core`` repository.

    There are two computation modes: sampling and exact. Expectation values are
    computed by post-processing samples when an Orquestra ``QuantumBackend`` is
    used or the number of samples was specified for a ``QuantumSimulator``
    backend. When the number of samples isn't specified, ``QuantumSimulator``
    backends run in exact mode.

    Args:
        backend_specs (str): the Orquestra backend specification in a json
            representation
        circuit (str): the circuit represented as an OpenQASM 2.0 program
        operators (str): the operator in an ``openfermion.QubitOperator``
            or ``openfermion.IsingOperator`` representation
    """
    backend_specs = json.loads(backend_specs)
    n_samples = backend_specs.pop("n_samples", None)

    operators = json.loads(operators)

    backend = create_object(backend_specs)

    # 1. Parse circuit
    qc = QuantumCircuit.from_qasm_str(circuit)

    # 2. Create operators
    ops = []
    if n_samples is not None:
        # Operators for Backend/Simulator in sampling mode
        for op in operators:
            ops.append(IsingOperator(op))
    else:
        # Operators for Simulator exact mode
        for op in operators:
            ops.append(QubitOperator(op))

    # 2.+1
    # Activate the qubits that are measured but were not acted on
    # by applying the identity
    # Note: this is a temporary logic subject to be removed once supported by
    # Orquestra

    # Get the active qubits of the circuit
    active_qubits = []
    for instr in qc.data:
        instruction_qubits = [qubit.index for qubit in instr[1]]
        active_qubits.extend(instruction_qubits)

    need_to_activate = set(range(len(qc.qubits))) - set(active_qubits)
    if need_to_activate:
        # We activate all the qubits for convenience
        for qubit in need_to_activate:
            # Apply the identity
            qc.id(qubit)

    # Convert to zquantum.core.circuits.Circuit
    circuit = import_from_qiskit(qc)

    # 3. Expval
    results = _get_expval(backend, circuit, ops, n_samples)

    save_list(results, "expval.json")
 def operator(self):
     return IsingOperator("Z0")
Exemplo n.º 26
0
class TestEstimatorUtils:
    def test_get_context_selection_circuit_for_group(self):
        group = QubitOperator("X0 Y1") - 0.5 * QubitOperator((1, "Y"))
        circuit, ising_operator = get_context_selection_circuit_for_group(
            group)

        # Need to convert to QubitOperator in order to get matrix representation
        qubit_operator = change_operator_type(ising_operator, QubitOperator)

        target_unitary = qubit_operator_sparse(group)
        transformed_unitary = (
            circuit.to_unitary().conj().T
            @ qubit_operator_sparse(qubit_operator) @ circuit.to_unitary())

        assert np.allclose(target_unitary.todense(), transformed_unitary)

    def test_perform_context_selection(self):
        target_operators = []
        target_operators.append(10.0 * QubitOperator("Z0"))
        target_operators.append(-3 * QubitOperator("Y0"))
        target_operators.append(1 * QubitOperator("X0"))
        target_operators.append(20 * QubitOperator(""))

        expected_operators = []
        expected_operators.append(10.0 * QubitOperator("Z0"))
        expected_operators.append(-3 * QubitOperator("Z0"))
        expected_operators.append(1 * QubitOperator("Z0"))
        expected_operators.append(20 * QubitOperator(""))

        base_circuit = Circuit([X(0)])
        x_term_circuit = Circuit([RY(-np.pi / 2)(0)])
        y_term_circuit = Circuit([RX(np.pi / 2)(0)])

        expected_circuits = [
            base_circuit,
            base_circuit + y_term_circuit,
            base_circuit + x_term_circuit,
            base_circuit,
        ]

        estimation_tasks = [
            EstimationTask(operator, base_circuit, None)
            for operator in target_operators
        ]

        tasks_with_context_selection = perform_context_selection(
            estimation_tasks)

        for task, expected_circuit, expected_operator in zip(
                tasks_with_context_selection, expected_circuits,
                expected_operators):
            assert task.operator.terms == expected_operator.terms
            assert task.circuit == expected_circuit

    @pytest.fixture()
    def frame_operators(self):
        operators = [
            2.0 * IsingOperator((1, "Z")) * IsingOperator((2, "Z")),
            1.0 * IsingOperator((3, "Z")) * IsingOperator((0, "Z")),
            -1.0 * IsingOperator((2, "Z")),
        ]

        return operators

    @pytest.fixture()
    def circuits(self):
        circuits = [Circuit() for _ in range(5)]

        circuits[1] += RX(1.2)(0)
        circuits[1] += RY(1.5)(1)
        circuits[1] += RX(-0.0002)(0)
        circuits[1] += RY(0)(1)

        for circuit in circuits[2:]:
            circuit += RX(sympy.Symbol("theta_0"))(0)
            circuit += RY(sympy.Symbol("theta_1"))(1)
            circuit += RX(sympy.Symbol("theta_2"))(0)
            circuit += RY(sympy.Symbol("theta_3"))(1)

        return circuits

    @pytest.mark.parametrize(
        "n_samples, target_n_samples_list",
        [
            (100, [100, 100, 100]),
            (17, [17, 17, 17]),
        ],
    )
    def test_allocate_shots_uniformly(
        self,
        frame_operators,
        n_samples,
        target_n_samples_list,
    ):
        allocate_shots = partial(allocate_shots_uniformly,
                                 number_of_shots=n_samples)
        circuit = Circuit()
        estimation_tasks = [
            EstimationTask(operator, circuit, 1)
            for operator in frame_operators
        ]

        new_estimation_tasks = allocate_shots(estimation_tasks)

        for task, target_n_samples in zip(new_estimation_tasks,
                                          target_n_samples_list):
            assert task.number_of_shots == target_n_samples

    @pytest.mark.parametrize(
        "total_n_shots, prior_expectation_values, target_n_samples_list",
        [
            (400, None, [200, 100, 100]),
            (400, ExpectationValues(np.array([0, 0, 0])), [200, 100, 100]),
            (400, ExpectationValues(np.array([1, 0.3, 0.3])), [0, 200, 200]),
        ],
    )
    def test_allocate_shots_proportionally(
        self,
        frame_operators,
        total_n_shots,
        prior_expectation_values,
        target_n_samples_list,
    ):
        allocate_shots = partial(
            allocate_shots_proportionally,
            total_n_shots=total_n_shots,
            prior_expectation_values=prior_expectation_values,
        )
        circuit = Circuit()
        estimation_tasks = [
            EstimationTask(operator, circuit, 1)
            for operator in frame_operators
        ]

        new_estimation_tasks = allocate_shots(estimation_tasks)

        for task, target_n_samples in zip(new_estimation_tasks,
                                          target_n_samples_list):
            assert task.number_of_shots == target_n_samples

    @pytest.mark.parametrize(
        "n_samples",
        [-1],
    )
    def test_allocate_shots_uniformly_invalid_inputs(
        self,
        n_samples,
    ):
        estimation_tasks = []
        with pytest.raises(ValueError):
            allocate_shots_uniformly(estimation_tasks,
                                     number_of_shots=n_samples)

    @pytest.mark.parametrize(
        "total_n_shots, prior_expectation_values",
        [
            (-1, ExpectationValues(np.array([0, 0, 0]))),
        ],
    )
    def test_allocate_shots_proportionally_invalid_inputs(
        self,
        total_n_shots,
        prior_expectation_values,
    ):
        estimation_tasks = []
        with pytest.raises(ValueError):
            _ = allocate_shots_proportionally(estimation_tasks, total_n_shots,
                                              prior_expectation_values)

    def test_evaluate_estimation_circuits_no_symbols(
        self,
        circuits,
    ):
        evaluate_circuits = partial(evaluate_estimation_circuits,
                                    symbols_maps=[[] for _ in circuits])
        operator = QubitOperator()
        estimation_tasks = [
            EstimationTask(operator, circuit, 1) for circuit in circuits
        ]

        new_estimation_tasks = evaluate_circuits(estimation_tasks)

        for old_task, new_task in zip(estimation_tasks, new_estimation_tasks):
            assert old_task.circuit == new_task.circuit

    def test_evaluate_estimation_circuits_all_symbols(
        self,
        circuits,
    ):
        symbols_maps = [[
            (sympy.Symbol("theta_0"), 0),
            (sympy.Symbol("theta_1"), 0),
            (sympy.Symbol("theta_2"), 0),
            (sympy.Symbol("theta_3"), 0),
        ] for _ in circuits]
        evaluate_circuits = partial(
            evaluate_estimation_circuits,
            symbols_maps=symbols_maps,
        )
        operator = QubitOperator()
        estimation_tasks = [
            EstimationTask(operator, circuit, 1) for circuit in circuits
        ]

        new_estimation_tasks = evaluate_circuits(estimation_tasks)

        for new_task in new_estimation_tasks:
            assert len(new_task.circuit.free_symbols) == 0

    def test_group_greedily_all_different_groups(self):
        target_operator = 10.0 * QubitOperator("Z0")
        target_operator -= 3.0 * QubitOperator("Y0")
        target_operator += 1.0 * QubitOperator("X0")
        target_operator += 20.0 * QubitOperator("")

        expected_operators = [
            10.0 * QubitOperator("Z0"),
            -3.0 * QubitOperator("Y0"),
            1.0 * QubitOperator("X0"),
            20.0 * QubitOperator(""),
        ]

        circuit = Circuit([X(0)])

        estimation_tasks = [EstimationTask(target_operator, circuit, None)]

        grouped_tasks = group_greedily(estimation_tasks)

        for task, operator in zip(grouped_tasks, expected_operators):
            assert task.operator == operator

        for initial_task, modified_task in zip(estimation_tasks,
                                               grouped_tasks):
            assert modified_task.circuit == initial_task.circuit
            assert modified_task.number_of_shots == initial_task.number_of_shots

    def test_group_greedily_all_comeasureable(self):
        target_operator = 10.0 * QubitOperator("Y0")
        target_operator -= 3.0 * QubitOperator("Y0 Y1")
        target_operator += 1.0 * QubitOperator("Y1")
        target_operator += 20.0 * QubitOperator("Y0 Y1 Y2")

        circuit = Circuit([X(0), X(1), X(2)])

        estimation_tasks = [EstimationTask(target_operator, circuit, None)]

        grouped_tasks = group_greedily(estimation_tasks)

        assert len(grouped_tasks) == 1
        assert grouped_tasks[0].operator == target_operator

        for initial_task, modified_task in zip(estimation_tasks,
                                               grouped_tasks):
            assert modified_task.circuit == initial_task.circuit
            assert modified_task.number_of_shots == initial_task.number_of_shots

    def test_group_individually(self):
        target_operator = 10.0 * QubitOperator("Z0")
        target_operator += 5.0 * QubitOperator("Z1")
        target_operator -= 3.0 * QubitOperator("Y0")
        target_operator += 1.0 * QubitOperator("X0")
        target_operator += 20.0 * QubitOperator("")

        expected_operator_terms_per_frame = [
            (10.0 * QubitOperator("Z0")).terms,
            (5.0 * QubitOperator("Z1")).terms,
            (-3.0 * QubitOperator("Y0")).terms,
            (1.0 * QubitOperator("X0")).terms,
            (20.0 * QubitOperator("")).terms,
        ]

        circuit = Circuit([X(0)])

        estimation_tasks = [EstimationTask(target_operator, circuit, None)]

        grouped_tasks = group_individually(estimation_tasks)

        assert len(grouped_tasks) == 5

        for task in grouped_tasks:
            assert task.operator.terms in expected_operator_terms_per_frame

    @pytest.mark.parametrize(
        ",".join([
            "estimation_tasks",
            "ref_estimation_tasks_to_measure",
            "ref_non_measured_estimation_tasks",
            "ref_indices_to_measure",
            "ref_non_measured_indices",
        ]),
        [
            (
                [
                    EstimationTask(IsingOperator("2[Z0] + 3 [Z1 Z2]"),
                                   Circuit([X(0)]), 10),
                    EstimationTask(
                        IsingOperator("2[Z0] + 3 [Z1 Z2] + 4[]"),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        17,
                    ),
                ],
                [
                    EstimationTask(IsingOperator("2[Z0] + 3 [Z1 Z2]"),
                                   Circuit([X(0)]), 10),
                    EstimationTask(
                        IsingOperator("2[Z0] + 3 [Z1 Z2] + 4 []"),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        17,
                    ),
                ],
                [],
                [0, 1, 2],
                [],
            ),
            (
                [
                    EstimationTask(IsingOperator("2[Z0] + 3 [Z1 Z2]"),
                                   Circuit([X(0)]), 10),
                    EstimationTask(
                        IsingOperator("4[] "),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        17,
                    ),
                ],
                [
                    EstimationTask(IsingOperator("2[Z0] + 3 [Z1 Z2]"),
                                   Circuit([X(0)]), 10),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        17,
                    ),
                ],
                [
                    EstimationTask(IsingOperator("4[]"),
                                   Circuit([RZ(np.pi / 2)(0)]), 1000)
                ],
                [0, 2],
                [1],
            ),
            (
                [
                    EstimationTask(IsingOperator("- 3 []"), Circuit([X(0)]),
                                   0),
                    EstimationTask(
                        IsingOperator("2[Z0] + 3 [Z1 Z2] + 4[]"),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        17,
                    ),
                ],
                [
                    EstimationTask(
                        IsingOperator("2[Z0] + 3 [Z1 Z2] + 4 []"),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        17,
                    ),
                ],
                [
                    EstimationTask(IsingOperator("- 3 []"), Circuit([X(0)]),
                                   0),
                ],
                [1, 2],
                [0],
            ),
            (
                [
                    EstimationTask(IsingOperator("- 3 []"), Circuit([X(0)]),
                                   0),
                    EstimationTask(
                        IsingOperator("2[Z0] + 3 [Z1 Z2] + 4[]"),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        0,
                    ),
                ],
                [
                    EstimationTask(
                        IsingOperator("2[Z0] + 3 [Z1 Z2] + 4 []"),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                ],
                [
                    EstimationTask(IsingOperator("- 3 []"), Circuit([X(0)]),
                                   0),
                    EstimationTask(
                        IsingOperator("4[Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        0,
                    ),
                ],
                [1],
                [0, 2],
            ),
        ],
    )
    def test_split_estimation_tasks_to_measure(
        self,
        estimation_tasks,
        ref_estimation_tasks_to_measure,
        ref_non_measured_estimation_tasks,
        ref_indices_to_measure,
        ref_non_measured_indices,
    ):

        (
            estimation_task_to_measure,
            non_measured_estimation_tasks,
            indices_to_measure,
            indices_for_non_measureds,
        ) = split_estimation_tasks_to_measure(estimation_tasks)

        assert estimation_task_to_measure == ref_estimation_tasks_to_measure
        assert non_measured_estimation_tasks == ref_non_measured_estimation_tasks
        assert indices_to_measure == ref_indices_to_measure
        assert ref_non_measured_indices == indices_for_non_measureds

    @pytest.mark.parametrize(
        "estimation_tasks,ref_expectation_values",
        [
            (
                [
                    EstimationTask(
                        IsingOperator("4[] "),
                        Circuit([RZ(np.pi / 2)(0)]),
                        1000,
                    ),
                ],
                [
                    ExpectationValues(
                        np.asarray([4.0]),
                        correlations=[np.asarray([[0.0]])],
                        estimator_covariances=[np.asarray([[0.0]])],
                    ),
                ],
            ),
            (
                [
                    EstimationTask(IsingOperator("- 2.5 [] - 0.5 []"),
                                   Circuit([X(0)]), 0),
                    EstimationTask(IsingOperator("0.001[] "),
                                   Circuit([RZ(np.pi / 2)(0)]), 2),
                    EstimationTask(
                        IsingOperator("2.5 [Z1] + 1.0 [Z2 Z3]"),
                        Circuit([RY(np.pi / 2)(0)]),
                        0,
                    ),
                ],
                [
                    ExpectationValues(
                        np.asarray([-3.0]),
                        correlations=[np.asarray([[0.0]])],
                        estimator_covariances=[np.asarray([[0.0]])],
                    ),
                    ExpectationValues(
                        np.asarray([0.001]),
                        correlations=[np.asarray([[0.0]])],
                        estimator_covariances=[np.asarray([[0.0]])],
                    ),
                    ExpectationValues(
                        np.asarray([0.0]),
                        correlations=[np.asarray([[0.0]])],
                        estimator_covariances=[np.asarray([[0.0]])],
                    ),
                ],
            ),
        ],
    )
    def test_evaluate_non_measured_estimation_tasks(self, estimation_tasks,
                                                    ref_expectation_values):

        expectation_values = evaluate_non_measured_estimation_tasks(
            estimation_tasks)

        for ex_val, ref_ex_val in zip(expectation_values,
                                      ref_expectation_values):
            assert np.allclose(ex_val.values, ref_ex_val.values)
            assert np.allclose(ex_val.correlations, ref_ex_val.correlations)
            assert np.allclose(ex_val.estimator_covariances,
                               ref_ex_val.estimator_covariances)

    @pytest.mark.parametrize(
        "estimation_tasks",
        [
            ([
                EstimationTask(IsingOperator("- 2.5 [] - 0.5 [Z1]"),
                               Circuit([X(0)]), 1),
            ]),
            ([
                EstimationTask(
                    IsingOperator("0.001 [Z0]"),
                    Circuit([RZ(np.pi / 2)(0)]),
                    0,
                ),
                EstimationTask(IsingOperator("2.0[] "),
                               Circuit([RZ(np.pi / 2)(0)]), 2),
                EstimationTask(
                    IsingOperator("1.5 [Z0 Z1]"),
                    Circuit([RY(np.pi / 2)(0)]),
                    10,
                ),
            ]),
        ],
    )
    def test_evaluate_non_measured_estimation_tasks_fails_with_non_zero_shots(
            self, estimation_tasks):
        with pytest.raises(RuntimeError):
            _ = evaluate_non_measured_estimation_tasks(estimation_tasks)
        assert contract(estimator)
"""

import numpy as np
from openfermion import IsingOperator
from zquantum.core.circuits import RX, RY, RZ, Circuit, H
from zquantum.core.interfaces.estimation import (
    EstimateExpectationValues,
    EstimationTask,
)
from zquantum.core.symbolic_simulator import SymbolicSimulator

_backend = SymbolicSimulator(seed=1997)

_estimation_tasks = [
    EstimationTask(IsingOperator("Z0"), Circuit([H(0)]), 10000),
    EstimationTask(
        IsingOperator("Z0") + IsingOperator("Z1") + IsingOperator("Z2"),
        Circuit([H(0), RX(np.pi / 3)(0), H(2)]),
        10000,
    ),
    EstimationTask(
        IsingOperator("Z0") + IsingOperator("Z1", 4),
        Circuit([
            RX(np.pi)(0),
            RY(0.12)(1),
            RZ(np.pi / 3)(1),
            RY(1.9213)(0),
        ]),
        10000,
    ),
Exemplo n.º 28
0
                    Circuit([RY(np.pi / 2)(0)]),
                    10,
                ),
            ]),
        ],
    )
    def test_evaluate_non_measured_estimation_tasks_fails_with_non_zero_shots(
            self, estimation_tasks):
        with pytest.raises(RuntimeError):
            _ = evaluate_non_measured_estimation_tasks(estimation_tasks)


TEST_CASES_EIGENSTATES = [
    (
        [
            EstimationTask(IsingOperator("Z0"),
                           circuit=Circuit([X(0)]),
                           number_of_shots=10),
            EstimationTask(
                IsingOperator((), coefficient=2.0),
                circuit=Circuit([RY(np.pi / 4)(0)]),
                number_of_shots=30,
            ),
        ],
        [ExpectationValues(np.array([-1])),
         ExpectationValues(np.array([2]))],
    ),
]
TEST_CASES_NONEIGENSTATES = [
    (
        [
Exemplo n.º 29
0
 def test_get_context_selection_circuit_diagonal(self):
     term = ((4, "Z"), (2, "Z"))
     circuit, ising_operator = get_context_selection_circuit(term)
     assert len(circuit.gates) == 0
     assert ising_operator == IsingOperator(term)
Exemplo n.º 30
0
 def test_get_context_selection_circuit_diagonal(self):
     term = ((4, "Z"), (2, "Z"))
     circuit, ising_operator = get_context_selection_circuit(term)
     self.assertEqual(len(circuit.gates), 0)
     self.assertEqual(ising_operator, IsingOperator(term))