def test_ansatz_circuit_three_layers(self, n_qubits, topology): # Given number_of_layers = 3 ansatz = QCBMAnsatz( number_of_layers=number_of_layers, number_of_qubits=n_qubits, topology=topology, ) params = [ np.random.rand(2 * n_qubits), np.random.rand(int((n_qubits * (n_qubits - 1)) / 2)), np.random.rand(2 * n_qubits), ] expected_circuit = Circuit([ # First layer *[RX(params[0][i])(i) for i in range(n_qubits)], *[RZ(params[0][i + n_qubits])(i) for i in range(n_qubits)], # Second layer *get_entangling_layer(params[1], n_qubits, XX, topology).operations, # Third layer *[RZ(params[2][i])(i) for i in range(n_qubits)], *[RX(params[2][i + n_qubits])(i) for i in range(n_qubits)], ]) params = np.concatenate(params) # When circuit = ansatz.get_executable_circuit(params) # Then assert circuit == expected_circuit
def target_unitary(self, beta, gamma, symbols_map): target_circuit = Circuit() target_circuit += H(0) target_circuit += H(1) target_circuit += RZ(2 * gamma)(0) target_circuit += RZ(2 * gamma)(1) target_circuit += RX(2 * beta)(0) target_circuit += RX(2 * beta)(1) return target_circuit.bind(symbols_map).to_unitary()
def time_evolution_for_term( term: QubitOperator, time: Union[float, sympy.Expr]) -> circuits.Circuit: """Evolves a Pauli term for a given time and returns a circuit representing it. Based on section 4 from https://arxiv.org/abs/1001.3855 . Args: term: Pauli term to be evolved time: time of evolution Returns: Circuit: Circuit representing evolved term. """ if len(term.terms) != 1: raise ValueError("This function works only on a single term.") term_components = list(term.terms.keys())[0] base_changes = [] base_reversals = [] cnot_gates = [] central_gate = None term_types = [component[1] for component in term_components] qubit_indices = [component[0] for component in term_components] coefficient = list(term.terms.values())[0] circuit = circuits.Circuit() # If constant term, return empty circuit. if not term_components: return circuit for i, (term_type, qubit_id) in enumerate(zip(term_types, qubit_indices)): if term_type == "X": base_changes.append(H(qubit_id)) base_reversals.append(H(qubit_id)) elif term_type == "Y": base_changes.append(RX(np.pi / 2)(qubit_id)) base_reversals.append(RX(-np.pi / 2)(qubit_id)) if i == len(term_components) - 1: central_gate = RZ(2 * time * coefficient)(qubit_id) else: cnot_gates.append(CNOT(qubit_id, qubit_indices[i + 1])) for gate in base_changes: circuit += gate for gate in cnot_gates: circuit += gate circuit += central_gate for gate in reversed(cnot_gates): circuit += gate for gate in base_reversals: circuit += gate return circuit
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
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 _build_rotational_subcircuit(self, circuit: Circuit, parameters: np.ndarray) -> Circuit: """Add the subcircuit which includes several rotation gates acting on each qubit Args: circuit: The circuit to append to parameters: The variational parameters (or symbolic parameters) Returns: circuit with added rotational sub-layer """ # Add RZ(theta) RX(pi/2) RZ(theta') RX(pi/2) RZ(theta'') for qubit_index in range(self.number_of_qubits): qubit_parameters = parameters[qubit_index * 3:(qubit_index + 1) * 3] circuit += RZ(qubit_parameters[0])(qubit_index) circuit += RX(np.pi / 2)(qubit_index) circuit += RZ(qubit_parameters[1])(qubit_index) circuit += RX(np.pi / 2)(qubit_index) circuit += RZ(qubit_parameters[2])(qubit_index) return circuit
def test_commutes_with_parameter_substitution(self): theta, gamma = sympy.symbols("theta, gamma") circuit = Circuit([ RX(theta / 2)(0), X(1), RY(gamma / 4).controlled(1)(1, 2), YY(0.1)(0, 4) ]) symbols_map = {theta: 0.1, gamma: 0.5} parameterized_unitary = circuit.to_unitary() unitary = circuit.bind(symbols_map).to_unitary() np.testing.assert_array_almost_equal( np.array(parameterized_unitary.subs(symbols_map), dtype=complex), unitary)
def test_ansatz_circuit_one_layer(self, n_qubits, topology): # Given number_of_layers = 1 ansatz = QCBMAnsatz( number_of_layers=number_of_layers, number_of_qubits=n_qubits, topology=topology, ) params = [np.random.rand(n_qubits)] expected_circuit = Circuit() for i in range(n_qubits): expected_circuit += RX(params[0][i])(i) params = np.concatenate(params) # When circuit = ansatz.get_executable_circuit(params) # Then assert circuit == expected_circuit
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
class TestCreatingUnitaryFromCircuit: @pytest.fixture def cirq_unitaries(self): """ Note: We decided to go with file-based approach after extracting cirq from z-quantum-core and not being able to use `export_to_cirq` anymore. """ path_to_array = ("/".join(__file__.split("/")[:-1]) + "/hardcoded_cirq_unitaries.npy") return np.load(path_to_array, allow_pickle=True) @pytest.mark.parametrize( "circuit, unitary_index", [ # Identity gates in some test cases below are used so that comparable # Cirq circuits have the same number of qubits as Zquantum ones. (Circuit([RX(np.pi / 5)(0)]), 0), (Circuit([RY(np.pi / 2)(0), RX(np.pi / 5)(0)]), 1), ( Circuit( [I(1), I(2), I(3), I(4), RX(np.pi / 5)(0), XX(0.1)(5, 0)]), 2, ), ( Circuit([ XY(np.pi).controlled(1)(3, 1, 4), RZ(0.1 * np.pi).controlled(2)(0, 2, 1), ]), 3, ), ( Circuit( [H(1), YY(0.1).controlled(1)(0, 1, 2), X(2), Y(3), Z(4)]), 4, ), ], ) def test_without_free_params_gives_the_same_result_as_cirq( self, circuit, unitary_index, cirq_unitaries): zquantum_unitary = circuit.to_unitary() assert isinstance( zquantum_unitary, np.ndarray ), "Unitary constructed from non-parameterized circuit is not a numpy array." np.testing.assert_array_almost_equal(zquantum_unitary, cirq_unitaries[unitary_index]) def test_commutes_with_parameter_substitution(self): theta, gamma = sympy.symbols("theta, gamma") circuit = Circuit([ RX(theta / 2)(0), X(1), RY(gamma / 4).controlled(1)(1, 2), YY(0.1)(0, 4) ]) symbols_map = {theta: 0.1, gamma: 0.5} parameterized_unitary = circuit.to_unitary() unitary = circuit.bind(symbols_map).to_unitary() np.testing.assert_array_almost_equal( np.array(parameterized_unitary.subs(symbols_map), dtype=complex), unitary)
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, ), ]
def test_by_default_only_gate_operations_are_supported(): simulator = SymbolicSimulatorWithDefaultSetOfSupportedOperations() assert simulator.is_natively_supported(RX(np.pi / 2)(1)) assert not simulator.is_natively_supported( MultiPhaseOperation((0.5, 0.2, 0.3, 0.1)))
class SymbolicSimulatorWithNonSupportedOperations(SymbolicSimulator): def is_natively_supported(self, operation: Operation) -> bool: return super().is_natively_supported( operation) and operation.gate.name != "RX" class SymbolicSimulatorWithDefaultSetOfSupportedOperations(SymbolicSimulator): def is_natively_supported(self, operation: Operation) -> bool: return QuantumSimulator.is_natively_supported(self, operation) @pytest.mark.parametrize( "circuit", [ Circuit([RY(0.5)(0), RX(1)(1), CNOT(0, 2), RX(np.pi)(2)]), Circuit([RX(1)(1), CNOT(0, 2), RX(np.pi)(2), RY(0.5)(0)]), Circuit([RX(1)(1), CNOT(0, 2), RX(np.pi)(2), RY(0.5)(0), RX(0.5)(0)]), ], ) def test_quantum_simulator_switches_between_native_and_nonnative_modes_of_execution( circuit, ): simulator = SymbolicSimulatorWithNonSupportedOperations() reference_simulator = SymbolicSimulator()