Spaces:
Running
Running
| """ | |
| IBM Quantum Cloud Integration Plugin | |
| This module implements a robust Cirq → Qiskit conversion utility that handles | |
| a broad set of common gates (including parameterized rotations) and measurements. | |
| It then authenticates with IBM Quantum using qiskit-ibm-provider, transpiles the | |
| converted circuit, submits the job to an IBM backend, and returns the results. | |
| Provider Setup Guidance: | |
| 1. Create an IBM Quantum account and retrieve your API token. | |
| 2. Save your token by calling: | |
| from qiskit_ibm_provider import IBMProvider | |
| IBMProvider.save_account(token='MY_API_TOKEN') | |
| (This stores your credentials in $HOME/.qiskit/qiskit-ibm.json.) | |
| 3. Alternatively, set the environment variable QISKIT_IBM_TOKEN. | |
| 4. Or, for a single session, pass the token when instantiating the provider. | |
| Requirements: | |
| - cirq | |
| - qiskit | |
| - qiskit-ibm-provider | |
| - sympy | |
| """ | |
| import cirq | |
| import sympy | |
| from qiskit import transpile | |
| from qiskit_ibm_provider import IBMProvider | |
| ############################################# | |
| # Part 1: Robust Cirq to Qiskit Conversion # | |
| ############################################# | |
| def _handle_rx(rads): | |
| from qiskit.circuit.library import RXGate | |
| if isinstance(rads, sympy.Basic): | |
| from qiskit.circuit import Parameter | |
| return RXGate(Parameter(str(rads))) | |
| elif isinstance(rads, (float, int)): | |
| return RXGate(rads) | |
| else: | |
| return RXGate(rads) | |
| def _handle_ry(rads): | |
| from qiskit.circuit.library import RYGate | |
| if isinstance(rads, sympy.Basic): | |
| from qiskit.circuit import Parameter | |
| return RYGate(Parameter(str(rads))) | |
| else: | |
| return RYGate(rads) | |
| def _handle_rz(rads): | |
| from qiskit.circuit.library import RZGate | |
| if isinstance(rads, sympy.Basic): | |
| from qiskit.circuit import Parameter | |
| return RZGate(Parameter(str(rads))) | |
| else: | |
| return RZGate(rads) | |
| def cirq_to_qiskit(cirq_circuit: cirq.Circuit): | |
| """ | |
| Converts a Cirq circuit into a Qiskit QuantumCircuit. | |
| Supports: | |
| - Single-qubit gates: X, Y, Z, S, T, H, and parameterized rotations (Rx, Ry, Rz). | |
| - Two-qubit gates: CNOT, CZ, SWAP. | |
| - Measurements. | |
| Symbolic parameters in Cirq become Qiskit Parameter objects. | |
| Returns: | |
| A Qiskit QuantumCircuit. | |
| """ | |
| # Collect and sort qubits | |
| all_qubits = sorted(cirq_circuit.all_qubits(), key=lambda q: str(q)) | |
| num_qubits = len(all_qubits) | |
| from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit | |
| qreg = QuantumRegister(num_qubits, 'q') | |
| creg = ClassicalRegister(num_qubits, 'c') | |
| qc = QuantumCircuit(qreg, creg) | |
| qubit_map = {q: i for i, q in enumerate(all_qubits)} | |
| for moment in cirq_circuit: | |
| for op in moment.operations: | |
| gate = op.gate | |
| qubits = op.qubits | |
| # Handle measurement | |
| if isinstance(gate, cirq.MeasurementGate): | |
| for q in qubits: | |
| idx = qubit_map[q] | |
| qc.measure(qreg[idx], creg[idx]) | |
| # Single-qubit operations | |
| elif len(qubits) == 1: | |
| idx = qubit_map[qubits[0]] | |
| if isinstance(gate, cirq.XPowGate) and gate.exponent == 1 and gate.global_shift == 0: | |
| qc.x(qreg[idx]) | |
| elif isinstance(gate, cirq.YPowGate) and gate.exponent == 1 and gate.global_shift == 0: | |
| qc.y(qreg[idx]) | |
| elif isinstance(gate, cirq.ZPowGate) and gate.exponent == 1 and gate.global_shift == 0: | |
| qc.z(qreg[idx]) | |
| # For constant gates, compare directly | |
| elif gate == cirq.S: | |
| qc.s(qreg[idx]) | |
| elif gate == cirq.T: | |
| qc.t(qreg[idx]) | |
| elif gate == cirq.H: | |
| qc.h(qreg[idx]) | |
| # Parametric rotations | |
| elif isinstance(gate, cirq.XPowGate) and gate.global_shift == 0: | |
| angle = sympy.pi * gate.exponent | |
| qc.append(_handle_rx(angle), [qreg[idx]]) | |
| elif isinstance(gate, cirq.YPowGate) and gate.global_shift == 0: | |
| angle = sympy.pi * gate.exponent | |
| qc.append(_handle_ry(angle), [qreg[idx]]) | |
| elif isinstance(gate, cirq.ZPowGate) and gate.global_shift == 0: | |
| angle = sympy.pi * gate.exponent | |
| qc.append(_handle_rz(angle), [qreg[idx]]) | |
| else: | |
| continue | |
| # Two-qubit operations | |
| elif len(qubits) == 2: | |
| idx0 = qubit_map[qubits[0]] | |
| idx1 = qubit_map[qubits[1]] | |
| if isinstance(gate, cirq.CNOT): | |
| qc.cx(qreg[idx0], qreg[idx1]) | |
| elif isinstance(gate, cirq.CZ): | |
| qc.cz(qreg[idx0], qreg[idx1]) | |
| elif isinstance(gate, cirq.SWAP): | |
| qc.swap(qreg[idx0], qreg[idx1]) | |
| else: | |
| continue | |
| else: | |
| continue | |
| return qc | |
| ########################################### | |
| # Part 2: IBM Quantum Integration # | |
| ########################################### | |
| def authenticate_ibm(token: str = None): | |
| """ | |
| Authenticates with IBM Quantum using the provided token. | |
| If token is None, attempts to load credentials from the environment or saved file. | |
| Returns the IBMProvider instance. | |
| """ | |
| if token: | |
| return IBMProvider(token=token) | |
| else: | |
| return IBMProvider() | |
| def submit_cirq_to_ibm(cirq_circuit: cirq.Circuit, token: str = None, shots: int = 1024, backend_name: str = None): | |
| """ | |
| Converts a Cirq circuit to a Qiskit circuit, transpiles it for an IBM backend, | |
| submits the job, and returns the job ID and results. | |
| """ | |
| provider = authenticate_ibm(token) | |
| qiskit_circ = cirq_to_qiskit(cirq_circuit) | |
| if qiskit_circ is None: | |
| raise ValueError("Conversion from Cirq to Qiskit failed.") | |
| if backend_name: | |
| backend = provider.get_backend(backend_name) | |
| else: | |
| simulator_backends = provider.backends(filters=lambda b: b.configuration().simulator) | |
| if not simulator_backends: | |
| raise ValueError("No simulator backends available.") | |
| from qiskit_ibm_provider import least_busy | |
| backend = least_busy(simulator_backends) | |
| transpiled_circ = transpile(qiskit_circ, backend=backend) | |
| job = backend.run(transpiled_circ, shots=shots) | |
| job_id = job.job_id() | |
| result = job.result() | |
| counts = result.get_counts() | |
| return job_id, counts | |
| def demo_hardware_integration_real(token: str, cirq_circuit: cirq.Circuit): | |
| """ | |
| Demonstrates real integration by converting a Cirq circuit to Qiskit, | |
| submitting it to IBM Quantum, and returning job results. | |
| """ | |
| try: | |
| job_id, counts = submit_cirq_to_ibm(cirq_circuit, token, shots=1024) | |
| return { | |
| "success": True, | |
| "job_id": job_id, | |
| "status": "Completed", | |
| "results": counts | |
| } | |
| except Exception as e: | |
| return { | |
| "success": False, | |
| "message": str(e) | |
| } | |
| if __name__ == "__main__": | |
| # Example demonstration: | |
| q0, q1 = cirq.LineQubit.range(2) | |
| demo_circuit = cirq.Circuit( | |
| cirq.H(q0), | |
| cirq.CNOT(q0, q1), | |
| cirq.measure(q0, key='m0'), | |
| cirq.measure(q1, key='m1') | |
| ) | |
| # Replace "YOUR_TOKEN_HERE" with your actual IBM Quantum API token. | |
| token = "5930ef6e6c64d2c69651052ee317561b8f64a6ff614e051f8053eb8a3b813e864b7ac94e2d52f97ac487953419e7d62416379a99e0902a0a24b5842c8a2b9e16" | |
| try: | |
| job_id, counts = submit_cirq_to_ibm(demo_circuit, token, shots=1024) | |
| print("IBM Quantum Job submitted successfully!") | |
| print("Job ID:", job_id) | |
| print("Counts:", counts) | |
| except Exception as error: | |
| print("Error during IBM Quantum integration:") | |
| print(error) |