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 test_gives_the_same_unitary_as_original_gate_up_to_global_phase( self, gate_to_be_decomposed, target_qubits ): circuit = Circuit([gate_to_be_decomposed(*target_qubits)]) decomposed_circuit = decompose_zquantum_circuit(circuit, DECOMPOSITION_RULES) assert _is_scaled_identity( circuit.to_unitary() @ np.linalg.inv(decomposed_circuit.to_unitary()), )
def test_create_circuits_from_qubit_operator(self): # Initialize target circuit1 = Circuit([Z(0), X(1)]) circuit2 = Circuit([Y(0), Z(1)]) # Given qubit_op = QubitOperator("Z0 X1") + QubitOperator("Y0 Z1") # When pauli_circuits = create_circuits_from_qubit_operator(qubit_op) # Then self.assertEqual(pauli_circuits[0], circuit1) self.assertEqual(pauli_circuits[1], circuit2)
def estimation_tasks(self): task_1 = EstimationTask(IsingOperator("Z0"), circuit=Circuit([X(0)]), number_of_shots=10) task_2 = EstimationTask( IsingOperator("Z0"), circuit=Circuit([RY(np.pi / 2)(0)]), number_of_shots=20, ) task_3 = EstimationTask( IsingOperator((), coefficient=2.0), circuit=Circuit([RY(np.pi / 4)(0)]), number_of_shots=30, ) return [task_1, task_2, task_3]
def create_X_target_unitary(number_of_params, k_body_depth=1): thetas = create_thetas(number_of_params) symbols_map = create_symbols_map(number_of_params) target_circuit = Circuit() # Add an x operator to every qubit for i in range(3): target_circuit += create_x_operator(i, thetas[i]) if k_body_depth == 2: target_circuit += create_2_qubit_x_operator(0, 1, thetas[3]) target_circuit += create_2_qubit_x_operator(0, 2, thetas[4]) target_circuit += create_2_qubit_x_operator(1, 2, thetas[5]) return target_circuit.bind(symbols_map).to_unitary()
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 _generate_circuit(self, params: Optional[np.ndarray] = None) -> Circuit: """Returns a parametrizable circuit represention of the ansatz. By convention the initial state is taken to be the |+..+> state and is evolved first under the cost Hamiltonian and then the mixer Hamiltonian. Args: params: parameters of the circuit. """ if params is not None: Warning( "This method retuns a parametrizable circuit, params will be ignored." ) circuit = Circuit() # Prepare initial state circuit += create_layer_of_gates(self.number_of_qubits, H) # Add time evolution layers cost_circuit = time_evolution( change_operator_type(self._cost_hamiltonian, QubitOperator), sympy.Symbol(f"gamma"), ) mixer_circuit = time_evolution(self._mixer_hamiltonian, sympy.Symbol(f"beta")) for i in range(self.number_of_layers): circuit += cost_circuit.bind( {sympy.Symbol(f"gamma"): sympy.Symbol(f"gamma_{i}")}) circuit += mixer_circuit.bind( {sympy.Symbol(f"beta"): sympy.Symbol(f"beta_{i}")}) return circuit
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
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_ansatz_circuit_two_layers(self, n_qubits, topology): # Given number_of_layers = 2 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)), ] 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, ]) params = np.concatenate(params) # When circuit = ansatz.get_executable_circuit(params) # Then assert circuit == expected_circuit
def _generate_circuit(self, params: Optional[np.ndarray] = None) -> Circuit: """Returns a parametrizable circuit represention of the ansatz. Args: params: parameters of the circuit. """ if params is not None: Warning( "This method retuns a parametrizable circuit, params will be ignored." ) circuit = Circuit() # Prepare initial state circuit += create_layer_of_gates(self.number_of_qubits, RY, self._thetas) # Add time evolution layers cost_circuit = time_evolution( change_operator_type(self._cost_hamiltonian, QubitOperator), sympy.Symbol(f"gamma"), ) for i in range(self.number_of_layers): circuit += cost_circuit.bind( {sympy.Symbol(f"gamma"): sympy.Symbol(f"gamma_{i}")}) circuit += create_layer_of_gates(self.number_of_qubits, RY, -self._thetas) circuit += create_layer_of_gates( self.number_of_qubits, RZ, [-2 * sympy.Symbol(f"beta_{i}")] * self.number_of_qubits, ) circuit += create_layer_of_gates(self.number_of_qubits, RY, self._thetas) return circuit
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_exact_expectation_values_without_wavefunction_simulator( self, backend): if backend.device_name != "wavefunction-simulator": operator = QubitOperator("Z0 Z1") circuit = Circuit([X(0), X(1)]) with pytest.raises(Exception): backend.get_exact_expectation_values(circuit, operator)
def get_entangling_layer_graph_topology( params: np.ndarray, n_qubits: int, entangling_gate: GatePrototype, adjacency_matrix: np.ndarray, ) -> Circuit: """Builds a circuit representing an entangling layer according to a general graph topology. Args: params: parameters of the circuit. n_qubits: number of qubits in the circuit. entangling_gate: gate specification for the entangling layer. adjacency_matrix: adjacency matrix for the entangling layer. """ assert adjacency_matrix.shape[0] == adjacency_matrix.shape[1] == n_qubits circuit = Circuit() i = 0 for qubit1_index in range(n_qubits - 1): for qubit2_index in range(qubit1_index + 1, n_qubits): if (adjacency_matrix[qubit1_index][qubit2_index] or adjacency_matrix[qubit2_index][qubit1_index]): circuit += entangling_gate(params[i])(qubit1_index, qubit2_index) i += 1 return circuit
def create_XZ1_target_unitary(number_of_params, k_body_depth=1): thetas = create_thetas(number_of_params) symbols_map = create_symbols_map(number_of_params) target_circuit = Circuit() target_circuit += create_x_operator(0, thetas[0]) target_circuit += RZ(2 * thetas[1])(0) target_circuit += create_x_operator(1, thetas[2]) target_circuit += RZ(2 * thetas[3])(1) if k_body_depth == 2: target_circuit += create_2_qubit_x_operator(0, 1, thetas[4]) target_circuit += CNOT(0, 1) target_circuit += RZ(2 * thetas[5])(1) target_circuit += CNOT(0, 1) return target_circuit.bind(symbols_map).to_unitary()
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_ansatz_circuit_nine_layers(self, n_qubits, topology): # Given number_of_layers = 9 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), np.random.rand(int((n_qubits * (n_qubits - 1)) / 2)), np.random.rand(2 * n_qubits), np.random.rand(int((n_qubits * (n_qubits - 1)) / 2)), np.random.rand(3 * 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 *[RX(params[2][i])(i) for i in range(n_qubits)], *[RZ(params[2][i + n_qubits])(i) for i in range(n_qubits)], # Fouth layer *get_entangling_layer(params[3], n_qubits, XX, topology).operations, # Fifth layer *[RX(params[4][i])(i) for i in range(n_qubits)], *[RZ(params[4][i + n_qubits])(i) for i in range(n_qubits)], # Sixth layer *get_entangling_layer(params[5], n_qubits, XX, topology).operations, # Seventh layer *[RX(params[6][i])(i) for i in range(n_qubits)], *[RZ(params[6][i + n_qubits])(i) for i in range(n_qubits)], *[RX(params[6][i + 2 * n_qubits])(i) for i in range(n_qubits)], # Eigth layer *get_entangling_layer(params[7], n_qubits, XX, topology).operations, # Ningth layer *[RZ(params[8][i])(i) for i in range(n_qubits)], *[RX(params[8][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 test_U3_decomposition_comprises_only_rotations( self, gate_to_be_decomposed, target_qubits ): circuit = Circuit([gate_to_be_decomposed(*target_qubits)]) decomposed_circuit = decompose_zquantum_circuit(circuit, DECOMPOSITION_RULES) assert all( isinstance(op, GateOperation) and op.gate.name in ("RZ", "RY") for op in decomposed_circuit.operations )
def test_run_circuitset_and_measure_n_samples(self, backend): # We override the base test because the qiskit integration may return # more samples than requested due to the fact that each circuit in a # batch must have the same number of measurements. # Note: this test may fail with noisy devices # Given backend.number_of_circuits_run = 0 backend.number_of_jobs_run = 0 first_circuit = Circuit([ X(0), X(0), X(1), X(1), X(2), ]) second_circuit = Circuit([ X(0), X(1), X(2), ]) n_samples = [100, 105] # When backend.n_samples = n_samples measurements_set = backend.run_circuitset_and_measure( [first_circuit, second_circuit], n_samples) # Then (since SPAM error could result in unexpected bitstrings, we make sure the # most common bitstring is the one we expect) counts = measurements_set[0].get_counts() assert max(counts, key=counts.get) == "001" counts = measurements_set[1].get_counts() assert max(counts, key=counts.get) == "111" assert len(measurements_set[0].bitstrings) >= n_samples[0] assert len(measurements_set[1].bitstrings) >= n_samples[1] assert backend.number_of_circuits_run == 2
def create_target_unitary(thetas, number_of_layers): target_circuit = Circuit() target_circuit += RY(thetas[0])(0) target_circuit += RY(thetas[1])(1) betas = create_betas(number_of_layers) gammas = create_gammas(number_of_layers) symbols_map = create_symbols_map(number_of_layers) for layer_id in range(number_of_layers): beta = betas[layer_id] gamma = gammas[layer_id] target_circuit += RZ(2.0 * gamma)(0) target_circuit += RZ(2.0 * gamma)(1) target_circuit += RY(-thetas[0])(0) target_circuit += RY(-thetas[1])(1) target_circuit += RZ(-2.0 * beta)(0) target_circuit += RZ(-2.0 * beta)(1) target_circuit += RY(thetas[0])(0) target_circuit += RY(thetas[1])(1) return target_circuit.bind(symbols_map).to_unitary()
def create_2_qubit_x_operator(qubit1: int, qubit2: int, param): return Circuit( [ H(qubit1), H(qubit2), CNOT(qubit1, qubit2), RZ(2 * param)(qubit2), CNOT(qubit1, qubit2), H(qubit1), H(qubit2), ] )
def test_build_hartree_fock_circuit_jordan_wigner(self): number_of_qubits = 4 number_of_alpha_electrons = 1 number_of_beta_electrons = 1 transformation = "Jordan-Wigner" expected_circuit = Circuit([X(0), X(1)], n_qubits=number_of_qubits) actual_circuit = build_hartree_fock_circuit( number_of_qubits, number_of_alpha_electrons, number_of_beta_electrons, transformation, ) assert actual_circuit == expected_circuit
def test_build_hartree_fock_circuit_bravyi_kitaev(self): number_of_qubits = 4 number_of_alpha_electrons = 1 number_of_beta_electrons = 1 transformation = "Bravyi-Kitaev" expected_circuit = Circuit([X(0)], n_qubits=number_of_qubits) actual_circuit = build_hartree_fock_circuit( number_of_qubits, number_of_alpha_electrons, number_of_beta_electrons, transformation, ) assert actual_circuit == expected_circuit
def test_get_wavefunction_seed(self): # Given circuit = Circuit([H(0), CNOT(0, 1), CNOT(1, 2)]) backend1 = ForestSimulator("wavefunction-simulator", seed=5324) backend2 = ForestSimulator("wavefunction-simulator", seed=5324) # When wavefunction1 = backend1.get_wavefunction(circuit) wavefunction2 = backend2.get_wavefunction(circuit) # Then for (ampl1, ampl2) in zip(wavefunction1.amplitudes, wavefunction2.amplitudes): assert ampl1 == ampl2
def get_qulacs_state_from_circuit(self, circuit: Circuit): qulacs_state = qulacs.QuantumState(circuit.n_qubits) for executable, operations_group in itertools.groupby( circuit.operations, self.can_be_executed_natively): if executable: qulacs_circuit = convert_to_qulacs( Circuit(operations_group, circuit.n_qubits)) qulacs_circuit.update_quantum_state(qulacs_state) else: wavefunction = qulacs_state.get_vector() for operation in operations_group: wavefunction = operation.apply(wavefunction) qulacs_state.load(wavefunction) return qulacs_state
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
def _zquantum_exponentiate_hamiltonian(hamiltonian, time, trotter_order): ops = [] for term in hamiltonian.get_operators(): mat = _zquantum_exponentiate_qubit_hamiltonian_term( term, time, trotter_order) ops.append( circuits.CustomGateDefinition( gate_name="custom_a", matrix=sympy.Matrix(mat), params_ordering=(), )()(0, 1)) circuit = Circuit(operations=ops * trotter_order) 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 test_cvar_estimator_returns_correct_values(self, estimator, backend, operator): # Given estimation_tasks = [EstimationTask(operator, Circuit([H(0)]), 10000)] if estimator.alpha <= 0.5: target_value = -1 else: target_value = (-1 * 0.5 + 1 * (estimator.alpha - 0.5)) / estimator.alpha # When expectation_values = estimator( backend=backend, estimation_tasks=estimation_tasks, ) # Then assert expectation_values[0].values == pytest.approx(target_value, abs=2e-1)
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