def test_raises_error_if_position_is_larger_or_equal_to_length( self, length, invalid_position): repeated_circuit = circuits.Circuit([circuits.X(0)]) different_circuit = circuits.Circuit([circuits.Y(0)]) with pytest.raises(ValueError): _generate_circuit_sequence(repeated_circuit, different_circuit, length, invalid_position)
class TestGeneratingCircuitSequence: @pytest.mark.parametrize( "repeated_circuit, different_circuit, length, position, expected_result", [ ( circuits.Circuit([circuits.X(0), circuits.Y(1)]), circuits.Circuit([circuits.Z(1)]), 5, 1, circuits.Circuit([ *[circuits.X(0), circuits.Y(1)], circuits.Z(1), *([circuits.X(0), circuits.Y(1)] * 3), ]), ), ( circuits.Circuit([circuits.RX(0.5)(1)]), circuits.Circuit([circuits.CNOT(0, 2)]), 3, 0, circuits.Circuit([ circuits.CNOT(0, 2), circuits.RX(0.5)(1), circuits.RX(0.5)(1) ]), ), ( circuits.Circuit([circuits.RX(0.5)(1)]), circuits.Circuit([circuits.CNOT(0, 2)]), 3, 2, circuits.Circuit([ circuits.RX(0.5)(1), circuits.RX(0.5)(1), circuits.CNOT(0, 2), ]), ), ], ) def test_produces_correct_sequence(self, repeated_circuit, different_circuit, position, length, expected_result): actual_result = _generate_circuit_sequence(repeated_circuit, different_circuit, length, position) assert actual_result == expected_result @pytest.mark.parametrize("length, invalid_position", [(5, 5), (4, 6)]) def test_raises_error_if_position_is_larger_or_equal_to_length( self, length, invalid_position): repeated_circuit = circuits.Circuit([circuits.X(0)]) different_circuit = circuits.Circuit([circuits.Y(0)]) with pytest.raises(ValueError): _generate_circuit_sequence(repeated_circuit, different_circuit, length, invalid_position)
def test_cannot_sample_from_circuit_containing_free_symbols( self, wf_simulator, circuit_list, binding): circuit = circuits.Circuit(circuit_list) with pytest.raises(ValueError): wf_simulator.run_circuit_and_measure(circuit, n_samples=1000, symbol_map=binding)
def _generate_circuit_sequence( repeated_circuit: circuits.Circuit, different_circuit: circuits.Circuit, length: int, position: int, ): """Join multiple copies of circuit, replacing one copy with a different circuit. Args: repeated_circuit: circuit which copies should be concatenated different_circuit: circuit that will replace one copy of `repeated_circuit length: total number of circuits to join position: which copy of repeated_circuit should be replaced by `different_circuit`. Returns: Concatenation of circuits C_1, ..., C_length, where C_i = `repeated_circuit` if i != position and C_i = `different_circuit` if i == position. """ if position >= length: raise ValueError(f"Position {position} should be < {length}") return circuits.Circuit( list( chain.from_iterable([(repeated_circuit if i != position else different_circuit).operations for i in range(length)])))
def test_circuit_with_only_supported_gates_is_not_changed(self): original_circuit = circuits.Circuit([ circuits.X(0), circuits.RX(np.pi)(2), circuits.SWAP(3, 0), circuits.RY(0.5).controlled(1)(0, 2), ]) assert make_circuit_qhipster_compatible( original_circuit) == original_circuit
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 test_supported_gates_are_left_unchanged(self): supported_gate_indices = [1, 3] original_circuit = circuits.Circuit( [circuits.I(0), circuits.X(1), circuits.I(2), circuits.RX(0)(2)]) compatible_circuit = make_circuit_qhipster_compatible(original_circuit) all(compatible_circuit.operations[i] == original_circuit.operations[i] for i in supported_gate_indices)
def test_run_circuit_and_measure_works_with_multiphase_operator(self, backend): params = [-0.1, 0.3, -0.5, 0.7] circuit = circuits.Circuit( [circuits.H(0), circuits.X(1), circuits.MultiPhaseOperation(params)] ) measurements = backend.run_circuit_and_measure(circuit, n_samples=1000) counts = measurements.get_counts() assert len(measurements.bitstrings) == 1000 assert all( bitstring in [(0, 1), (1, 1)] for bitstring in measurements.bitstrings )
def test_identity_gates_are_replaced_with_zero_angle_rotation(self): identity_gate_indices = [0, 2] original_circuit = circuits.Circuit( [circuits.I(0), circuits.X(1), circuits.I(2), circuits.RX(0)(2)]) compatible_circuit = make_circuit_qhipster_compatible(original_circuit) assert all(compatible_circuit.operations[i].gate == circuits.RX(0) and compatible_circuit.operations[i].qubit_indices == original_circuit.operations[i].qubit_indices for i in identity_gate_indices)
def make_circuit_qhipster_compatible(circuit: circuits.Circuit): unsupported_operations = [ op for op in circuit.operations if op.gate.name in QHIPSTER_UNSUPPORTED_GATES ] if unsupported_operations: raise NotImplementedError( "ISWAP gates and two-qubit Pauli rotations are not supported by qHipster " f"integration. Offending operations: {unsupported_operations}.") return circuits.Circuit( operations=[ circuits.RX(0)(*op.qubit_indices) if op.gate.name == "I" else op for op in circuit.operations ], n_qubits=circuit.n_qubits, )
def test_concatenate_circuits_python_objects(self, circuit_set): # Given expected_concatenated_circuit_filename = "result-circuit.json" expected_concatenated_circuit = sum( [circuit for circuit in circuit_set], new_circuits.Circuit()) # When concatenate_circuits(circuit_set) # Then try: with open(expected_concatenated_circuit_filename) as f: concatenated_circuit = new_circuits.circuit_from_dict( json.load(f)) assert concatenated_circuit == expected_concatenated_circuit finally: remove_file_if_exists(expected_concatenated_circuit_filename)
class TestConvertingCircuitToSimplifiedQasm: @pytest.mark.parametrize( "circuit, expected_qasm", [ (circuits.Circuit(), "0\n"), ( circuits.Circuit([circuits.X(0), circuits.Y(2), circuits.Z(1)]), "\n".join(["3", "X 0", "Y 2", "Z 1"]), ), ( circuits.Circuit([circuits.X(0), circuits.Z(4)]), "\n".join(["5", "X 0", "Z 4"]), ), ( circuits.Circuit([circuits.X(4), circuits.Z(0)]), "\n".join(["5", "X 4", "Z 0"]), ), ( circuits.Circuit([circuits.X(4), circuits.CNOT(0, 3)]), "\n".join(["5", "X 4", "CNOT 0 3"]), ), ( circuits.Circuit([circuits.RX(np.pi)(1), circuits.RZ(0.5)(3)]), "\n".join([ "4", "Rx 3.14159265358979311600 1", "Rz 0.50000000000000000000 3" ]), ), ], ) def test_converting_circuit_to_qasm_emits_correct_string( self, circuit, expected_qasm): assert convert_to_simplified_qasm(circuit) == expected_qasm
def test_evolving_constant_term_qubit_operator_gives_empty_circuit(self): evolution_circuit = time_evolution_for_term(QubitOperator((), 1), np.pi) assert evolution_circuit == circuits.Circuit()
def test_circuit_with_iswap_gate_cannot_be_made_compatible( self, supported_gate): circuit = circuits.Circuit([circuits.ISWAP(0, 2), supported_gate(1)]) with pytest.raises(NotImplementedError): make_circuit_qhipster_compatible(circuit)
class TestQulacs(QuantumSimulatorTests): @pytest.mark.parametrize( "circuit, target_wavefunction", [ ( circuits.Circuit( [ circuits.H(0), circuits.H(1), circuits.MultiPhaseOperation([-0.1, 0.3, -0.5, 0.7]), circuits.X(0), circuits.X(0), ] ), np.exp(1j * np.array([-0.1, 0.3, -0.5, 0.7])) / 2, ), ( circuits.Circuit( [ circuits.H(0), circuits.H(1), circuits.MultiPhaseOperation([-0.1, 0.3, -0.5, 0.7]), circuits.MultiPhaseOperation([-0.2, 0.1, -0.2, -0.3]), circuits.X(0), circuits.X(0), ] ), np.exp(1j * np.array([-0.3, 0.4, -0.7, 0.4])) / 2, ), ( circuits.Circuit( [ circuits.MultiPhaseOperation([-0.1, 0.3, -0.5, 0.7]), ] ), np.array([np.exp(-0.1j), 0, 0, 0]), ), ( circuits.Circuit( [ circuits.H(0), circuits.MultiPhaseOperation([-0.1, 0.3, -0.5, 0.7]), ] ), np.array([np.exp(-0.1j), np.exp(0.3j), 0, 0]) / np.sqrt(2), ), ], ) def test_get_wavefunction_works_with_multiphase_operator( self, backend, circuit, target_wavefunction ): wavefunction = backend.get_wavefunction(circuit) np.testing.assert_almost_equal(wavefunction, target_wavefunction) def test_run_circuit_and_measure_works_with_multiphase_operator(self, backend): params = [-0.1, 0.3, -0.5, 0.7] circuit = circuits.Circuit( [circuits.H(0), circuits.X(1), circuits.MultiPhaseOperation(params)] ) measurements = backend.run_circuit_and_measure(circuit, n_samples=1000) counts = measurements.get_counts() assert len(measurements.bitstrings) == 1000 assert all( bitstring in [(0, 1), (1, 1)] for bitstring in measurements.bitstrings )
def test_circuit_with_two_qubit_pauli_rotation_cannot_be_made_compatible( self, supported_gate, unsupported_gate): circuit = circuits.Circuit([unsupported_gate(0, 2), supported_gate(1)]) with pytest.raises(NotImplementedError): make_circuit_qhipster_compatible(circuit)
def time_evolution_derivatives( hamiltonian: QubitOperator, time: float, method: str = "Trotter", trotter_order: int = 1, ) -> Tuple[List[circuits.Circuit], List[float]]: """Generates derivative circuits for the time evolution operator defined in function time_evolution Args: hamiltonian: The Hamiltonian to be evolved under. It should contain numeric coefficients, symbolic expressions aren't supported. time: time duration of the evolution. method: time evolution method. Currently the only option is 'Trotter'. trotter_order: order of Trotter evolution Returns: A Circuit simulating time evolution. """ if method != "Trotter": raise ValueError(f"The method {method} is currently not supported.") single_trotter_derivatives = [] factors = [1.0, -1.0] output_factors = [] terms: Iterable = list(hamiltonian.get_operators()) for i, term_1 in enumerate(terms): for factor in factors: output = circuits.Circuit() try: if isinstance(term_1, QubitOperator): r = list(term_1.terms.values())[0] / trotter_order else: r = complex(term_1.coefficient).real / trotter_order except TypeError: raise ValueError("Term coefficients need to be numerical. " f"Offending term: {term_1}") output_factors.append(r * factor) shift = factor * (np.pi / (4.0 * r)) for j, term_2 in enumerate(terms): output += time_evolution_for_term( term_2, (time + shift) / trotter_order if i == j else time / trotter_order, ) single_trotter_derivatives.append(output) if trotter_order > 1: output_circuits = [] final_factors = [] repeated_circuit = time_evolution(hamiltonian, time, method="Trotter", trotter_order=1) for position in range(trotter_order): for factor, different_circuit in zip(output_factors, single_trotter_derivatives): output_circuits.append( _generate_circuit_sequence(repeated_circuit, different_circuit, trotter_order, position)) final_factors.append(factor) return output_circuits, final_factors else: return single_trotter_derivatives, output_factors