def get_exact_expectation_values(self, circuit, qubit_operator, **kwargs): """Run a circuit to prepare a wavefunction and measure the exact expectation values with respect to a given operator. Args: circuit (zquantum.core.circuit.Circuit): the circuit to prepare the state qubit_operator (openfermion.ops.QubitOperator): the operator to measure Returns: zquantum.core.measurement.ExpectationValues: the expectation values of each term in the operator """ self.num_circuits_run += 1 self.num_jobs_run += 1 wavefunction = self.get_wavefunction(circuit) # Pyquil does not support PauliSums with no terms. if len(qubit_operator.terms) == 0: return ExpectationValues(np.zeros((0, ))) values = [] for op in qubit_operator: values.append(expectation(op, wavefunction)) return expectation_values_to_real(ExpectationValues( np.asarray(values)))
def get_exact_expectation_values(self, circuit, qubit_operator): self.number_of_jobs_run += 1 self.number_of_circuits_run += 1 if self.device_name != "wavefunction-simulator": raise RuntimeError( "To compute exact expectation values, the device name must be " '"wavefunction-simulator". The device name is currently ' f"{self.device_name}." ) cxn = get_forest_connection(self.device_name, self.seed) # Pyquil does not support PauliSums with no terms. if len(qubit_operator.terms) == 0: return ExpectationValues(np.zeros((0,))) pauli_sum = qubitop_to_pyquilpauli(qubit_operator) expectation_values = np.real( cxn.expectation(export_to_pyquil(circuit), pauli_sum.terms) ) if expectation_values.shape[0] != len(pauli_sum): raise ( RuntimeError( f"Expected {len(pauli_sum)} expectation values but received " f"{expectation_values.shape[0]}." ) ) return ExpectationValues(expectation_values)
def test_concatenate_expectation_values(): expectation_values_set = [ ExpectationValues(np.array([1.0, 2.0])), ExpectationValues(np.array([3.0, 4.0])), ] combined_expectation_values = concatenate_expectation_values( expectation_values_set) assert combined_expectation_values.correlations is None assert combined_expectation_values.estimator_covariances is None assert np.allclose(combined_expectation_values.values, [1.0, 2.0, 3.0, 4.0])
def test_expectation_values_to_real(): # Given expectation_values = ExpectationValues( np.array([0.0 + 0.1j, 0.0 + 1e-10j, -1.0])) target_expectation_values = ExpectationValues(np.array([0.0, 0.0, -1.0])) # When real_expectation_values = expectation_values_to_real(expectation_values) # Then for value in expectation_values.values: assert not isinstance(value, complex) np.testing.assert_array_equal(real_expectation_values.values, target_expectation_values.values)
def get_exact_expectation_values(self, circuit, qubit_operator, **kwargs): if self.device_name != 'wavefunction-simulator' and self.n_samples!=None: raise Exception("Exact expectation values work only for the wavefunction simulator and n_samples equal to None.") cxn = get_forest_connection(self.device_name) # Pyquil does not support PauliSums with no terms. if len(qubit_operator.terms) == 0: return ExpectationValues(np.zeros((0,))) pauli_sum = qubitop_to_pyquilpauli(qubit_operator) expectation_values = cxn.expectation(circuit.to_pyquil(), pauli_sum.terms) if expectation_values.shape[0] != len(pauli_sum): raise(RuntimeError("Expected {} expectation values but received {}.".format(len(pauli_sum), expectation_values.shape[0]))) return ExpectationValues(expectation_values)
def __call__( self, backend: QuantumBackend, estimation_tasks: List[EstimationTask]) -> List[ExpectationValues]: """Given a circuit, backend, and target operators, this method produces expectation values using Gibbs objective function. Args: backend: the backend that will be used to run the circuits estimation_tasks: the estimation tasks defining the problem. Each task consist of target operator, circuit and number of shots. alpha: defines to exponent coefficient, `exp(-alpha * expectation_value)`. See equation 2 in the original paper. """ if self.alpha <= 0: raise ValueError("alpha needs to be a value greater than 0.") circuits, operators, shots_per_circuit = zip( *[(e.circuit, e.operator, e.number_of_shots) for e in estimation_tasks]) distributions_list = [ backend.get_bitstring_distribution(circuit, n_samples=n_shots) for circuit, n_shots in zip(circuits, shots_per_circuit) ] return [ ExpectationValues( np.array([ _calculate_expectation_value_for_distribution( distribution, operator, self.alpha) ])) for distribution, operator in zip(distributions_list, operators) ]
def test_sum_expectation_values_with_covariances(): values = np.array([5, -2, 1]) correlations = [np.array([[1, 0.5], [0.5, 2]]), np.array([[7]])] covariances = [correlations[0] / 10, correlations[1] / 10] expectation_values = ExpectationValues(values, correlations, covariances) total = sum_expectation_values(expectation_values) assert np.isclose(total.value, 4) assert np.isclose(total.precision, np.sqrt((1 + 0.5 + 0.5 + 2 + 7) / 10))
def test_evaluate_qubit_operator(self): # Given qubit_op = QubitOperator("0.5 [] + 0.5 [Z1]") expectation_values = ExpectationValues([0.5, 0.5]) # When value_estimate = evaluate_qubit_operator(qubit_op, expectation_values) # Then self.assertAlmostEqual(value_estimate.value, 0.5)
def __call__( self, backend: QuantumBackend, estimation_tasks: List[EstimationTask]) -> List[ExpectationValues]: """Given a circuit, backend, and target operators, this method produces expectation values using CVaR method. Args: backend: the backend that will be used to run the circuits estimation_tasks: the estimation tasks defining the problem. Each task consist of target operator, circuit and number of shots. """ if self.alpha > 1 or self.alpha <= 0: raise ValueError("alpha needs to be a value between 0 and 1.") circuits, operators, shots_per_circuit = zip( *[(e.circuit, e.operator, e.number_of_shots) for e in estimation_tasks]) if not self.use_exact_expectation_values: distributions_list = [ backend.get_bitstring_distribution(circuit, n_samples=n_shots) for circuit, n_shots in zip(circuits, shots_per_circuit) ] return [ ExpectationValues( np.array([ _calculate_expectation_value_for_distribution( distribution, operator, self.alpha) ])) for distribution, operator in zip( distributions_list, operators) ] else: wavefunctions_list = [ backend.get_wavefunction(circuit) for circuit in circuits ] return [ ExpectationValues( np.array([ _calculate_expectation_value_for_wavefunction( distribution, operator, self.alpha) ])) for distribution, operator in zip( wavefunctions_list, operators) ]
def get_estimated_expectation_values( self, backend: QuantumBackend, circuit: Circuit, target_operator: IsingOperator, alpha: float, n_samples: Optional[int] = None, ) -> ExpectationValues: """Given a circuit, backend, and target operators, this method produces expectation values using CVaR algorithm. Args: backend (QuantumBackend): the backend that will be used to run the circuit circuit (Circuit): the circuit that prepares the state. target_operator (SymbolicOperator): Operator to be estimated. alpha (float): defines what part of the best measurements should be taken into account in the estimation process. n_samples (int): Number of measurements done on the unknown quantum state. Raises: AttributeError: If backend is not a QuantumSimulator. Returns: ExpectationValues: expectation values for each term in the target operator. """ if alpha > 1 or alpha <= 0: raise ValueError("alpha needs to be a value between 0 and 1.") if not isinstance(target_operator, IsingOperator): raise TypeError("Operator should be of type IsingOperator.") distribution = backend.get_bitstring_distribution(circuit, n_samples=n_samples) expected_values_per_bitstring = {} for bitstring in distribution.distribution_dict: expected_value = Measurements(bitstring).get_expectation_values( target_operator ) expected_values_per_bitstring[bitstring] = expected_value.values[0] sorted_expected_values_per_bitstring_list = sorted( expected_values_per_bitstring.items(), key=lambda item: item[1] ) cumulative_prob = 0.0 cumulative_value = 0.0 for bitstring, energy in sorted_expected_values_per_bitstring_list: prob = distribution.distribution_dict[bitstring] if cumulative_prob + prob < alpha: cumulative_prob += prob cumulative_value += prob * energy else: cumulative_value += (alpha - cumulative_prob) * energy break final_value = cumulative_value / alpha return ExpectationValues(final_value)
def test_evaluate_qubit_operator_list(self): # Given qubit_op_list = [ QubitOperator("0.5 [] + 0.5 [Z1]"), QubitOperator("0.3 [X1] + 0.2[Y2]"), ] expectation_values = ExpectationValues([0.5, 0.5, 0.4, 0.6]) # When value_estimate = evaluate_qubit_operator_list(qubit_op_list, expectation_values) # Then self.assertAlmostEqual(value_estimate.value, 0.74)
def test_concatenate_expectation_values_with_cov_and_corr(): expectation_values_set = [ ExpectationValues( np.array([1.0, 2.0]), estimator_covariances=[np.array([[0.1, 0.2], [0.3, 0.4]])], correlations=[np.array([[-0.1, -0.2], [-0.3, -0.4]])], ), ExpectationValues( np.array([3.0, 4.0]), estimator_covariances=[np.array([[0.1]]), np.array([[0.2]])], correlations=[np.array([[-0.1]]), np.array([[-0.2]])], ), ] combined_expectation_values = concatenate_expectation_values( expectation_values_set) assert len(combined_expectation_values.estimator_covariances) == 3 assert np.allclose( combined_expectation_values.estimator_covariances[0], [[0.1, 0.2], [0.3, 0.4]], ) assert np.allclose(combined_expectation_values.estimator_covariances[1], [[0.1]]) assert np.allclose(combined_expectation_values.estimator_covariances[2], [[0.2]]) assert len(combined_expectation_values.correlations) == 3 assert np.allclose( combined_expectation_values.correlations[0], [[-0.1, -0.2], [-0.3, -0.4]], ) assert np.allclose(combined_expectation_values.correlations[1], [[-0.1]]) assert np.allclose(combined_expectation_values.correlations[2], [[-0.2]]) assert np.allclose(combined_expectation_values.values, [1.0, 2.0, 3.0, 4.0])
def get_exact_expectation_values(self, circuit, qubit_operator, **kwargs): if self.n_samples != None: raise Exception( "Exact expectation values work only for n_samples equal to None." ) expectation_values = [] qulacs_state = self.get_qulacs_state_from_circuit(circuit) for op in qubit_operator: qulacs_observable = create_observable_from_openfermion_text(str(op)) for term_id in range(qulacs_observable.get_term_count()): term = qulacs_observable.get_term(term_id) expectation_values.append( np.real(term.get_expectation_value(qulacs_state)) ) return ExpectationValues(np.array(expectation_values))
def test_expectation_values_io(): expectation_values = np.array([0.0, 0.0, -1.0]) correlations = [] correlations.append(np.array([[1.0, -1.0], [-1.0, 1.0]])) correlations.append(np.array([[1.0]])) estimator_covariances = [] estimator_covariances.append(np.array([[0.1, -0.1], [-0.1, 0.1]])) estimator_covariances.append(np.array([[0.1]])) expectation_values_object = ExpectationValues(expectation_values, correlations, estimator_covariances) save_expectation_values(expectation_values_object, "expectation_values.json") expectation_values_object_loaded = load_expectation_values( "expectation_values.json") assert np.allclose( expectation_values_object.values, expectation_values_object_loaded.values, ) assert len(expectation_values_object.correlations) == len( expectation_values_object_loaded.correlations) assert len(expectation_values_object.estimator_covariances) == len( expectation_values_object_loaded.estimator_covariances) for i in range(len(expectation_values_object.correlations)): assert np.allclose( expectation_values_object.correlations[i], expectation_values_object_loaded.correlations[i], ) for i in range(len(expectation_values_object.estimator_covariances)): assert np.allclose( expectation_values_object.estimator_covariances[i], expectation_values_object_loaded.estimator_covariances[i], ) remove_file_if_exists("expectation_values.json")
def get_exact_expectation_values(self, circuit, qubit_operator, **kwargs): """ Calculates the expectation values for given operator, based on the exact quantum state produced by circuit. Args: circuit (core.circuit.Circuit): quantum circuit to be executed. qubit_operator (openfermion): Operator for which we calculate the expectation value. Returns: ExpectationValues: object representing expectation values for given operator. """ # convert the OpenFermion operator to PennyLane observables coeffs, obs = qml.qchem.convert_hamiltonian(qubit_operator).terms num_qubits = len(circuit.get_qubits()) dev = qml.device(self.device, wires=num_qubits) ansatz = qml.from_qiskit(circuit.to_qiskit()) qnodes = qml.map(lambda weights, **kwargs: ansatz(**kwargs), obs, dev, measure="expval") return ExpectationValues( values=[coeff * qnode([]) for coeff, qnode in zip(coeffs, qnodes)])
class TestEstimatorUtils: def test_get_context_selection_circuit_offdiagonal(self): term = QubitOperator("X0 Y1") circuit, ising_operator = get_context_selection_circuit(term) # Need to convert to QubitOperator in order to get matrix representation qubit_operator = change_operator_type(ising_operator, QubitOperator) target_unitary = qubit_operator_sparse(term) 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_get_context_selection_circuit_diagonal(self): term = QubitOperator("Z2 Z4") circuit, ising_operator = get_context_selection_circuit(term) assert len(circuit.gates) == 0 assert ising_operator == change_operator_type(term, IsingOperator) 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(Program(X(0))) x_term_circuit = Circuit(Program(RX(np.pi / 2, 0))) y_term_circuit = Circuit(Program(RY(-np.pi / 2, 0))) expected_circuits = [ base_circuit, base_circuit + x_term_circuit, base_circuit + y_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)] for circuit in circuits: circuit.qubits = [Qubit(i) for i in range(2)] circuits[1].gates = [ Gate("Rx", [circuits[1].qubits[0]], [1.2]), Gate("Ry", [circuits[1].qubits[1]], [1.5]), Gate("Rx", [circuits[1].qubits[0]], [-0.0002]), Gate("Ry", [circuits[1].qubits[1]], [0]), ] for circuit in circuits[2:]: circuit.gates = [ Gate("Rx", [circuit.qubits[0]], [sympy.Symbol("theta_0")]), Gate("Ry", [circuit.qubits[1]], [sympy.Symbol("theta_1")]), Gate("Rx", [circuit.qubits[0]], [sympy.Symbol("theta_2")]), Gate("Ry", [circuit.qubits[1]], [sympy.Symbol("theta_3")]), ] 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 = 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 new_task.circuit.symbolic_params == [] 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(Program(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(Program([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(Program(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
( [QubitOperator("[Z0 Z1] + [Z0]"), QubitOperator("[X0 X1] + [X0]")], None, np.array([2.0, 2.0]), ), ( [ QubitOperator("[Z0 Z1] + [Z0] + []"), QubitOperator("[X0 X1] + [X0] + 5 []"), ], None, np.array([2.0, 2.0]), ), ( [QubitOperator("[Z0 Z1] + [Z0]"), QubitOperator("[X0 X1] + [X0]")], ExpectationValues(np.zeros(4)), np.array([2.0, 2.0]), ), ( [QubitOperator("2 [Z0 Z1] + 3 [Z0]"), QubitOperator("[X0 X1] + [X0]")], ExpectationValues(np.zeros(4)), np.array([13.0, 2.0]), ), ( [QubitOperator("2[]")], ExpectationValues(np.array([1])), np.array([0.0]), ), ( [ QubitOperator("2 [Z0 Z1] + 3 [Z0] + 8[]"),
def test_sum_expectation_values(self): expectation_values = ExpectationValues(np.array([5, -2, 1])) total = sum_expectation_values(expectation_values) assert np.isclose(total.value, 4) assert total.precision is None
_ = 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 = [ ( [ EstimationTask( IsingOperator("Z0"), circuit=Circuit([H(0)]), number_of_shots=1000, ), EstimationTask( IsingOperator("Z0", coefficient=-2), circuit=Circuit([RY(np.pi / 4)(0)]), number_of_shots=1000,
def target_expectation_values(self): return [ ExpectationValues(-1), ExpectationValues(0), ExpectationValues(2) ]
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)