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")
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
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
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
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]
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
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 )
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)
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)
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
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)
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)
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")
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
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
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 )
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)
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)
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 )
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")
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")
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, ),
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 = [ ( [
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)
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))