API Reference: qbraid.runtime.oqc

Installation & Setup

To interface with OQC devices, install the oqc extra:

pip install 'qbraid[oqc]'

You must also obtain an OQC access token.

Basic Usage

Here we go through an example of submitting a job to an OQC device using the OQCProvider.

>>> from qbraid.runtime.oqc import OQCProvider
>>> provider = OQCProvider(<YOUR_API_TOKEN>)
>>> provider.get_devices()
[<qbraid.runtime.oqc.device.OQCDevice('qpu:uk:2:d865b5a184')>]
>>> device = provider.get_device('qpu:uk:2:d865b5a184')
>>> type(device)
qbraid.runtime.oqc.device.OQCDevice
>>> device.metadata()
{'device_id': 'qpu:uk:2:d865b5a184',
 'device_type': 'SIMULATOR',
 'num_qubits': 8,
 'status': 'ONLINE',
 'queue_depth': None}

Submitting jobs to an OQC device has a far range of options that other providers’ devices do not have. There are 5 kwargs that someone can pass into OQCDevice.run() to enable these customizations:

  • shots: The number of times the circuit is ran. The default value is 1000.

  • repetition_period: The length of time in between shots. The default value is 200e-6 seconds and OQC strongly recommends that you use the default unless you thoroughly understand the potential impact.

  • results_format: The two options for this are 'raw' and 'binary'.

  • metrics: The type of metrics you want to be recorded. The only metrics related to a task are its optimized circuit and the corresponding instruction count. So, there are four options:

    1. 'default': Returns both metrics.
    2. 'empty': Returns no metrics.
    3. 'optimized_circuit': Returns the optimized qasm2 circuit.
    4. 'optimized_instruction_count': Returns the instruction count of the optimized circuit.
  • optimizations: These are the optimizations you want to perform on the input program(s). OQC uses the tKet optimization compiler, as it is integrated into their compiler pipeline. Documention on these are in the pytket.passes documentation, and below are the instructions to access them through OQCDevice.run():

    OptionTKET pass
    'clifford_simplify'CliffordSimp
    'context_simplify'ContextSimp
    'decompose_controlled_gates'DecomposeArbitrarilyControlledGates
    'default_mapping_pass'DefaultMappingPass
    'empty'Empty
    'full_peephole_optimise'FullPeepholeOptimise
    'globalise_phased_x'GlobalisePhasedX
    'kak_decomposition'KAKDecomposition
    'one'One
    'peephole_optimise_2q'PeepholeOptimise2Q
    'remove_barriers'RemoveBarriers
    'remove_discarded'RemoveDiscarded
    'remove_redundancies'RemoveRedundancies
    'simplify_measured'SimplifyMeasured
    'three_qubit_squash'ThreeQubitSquash
    'two'Two

Now here’s an example of submitting a task with some custom configurations:

program = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q;
creg c[2];
measure q->c;
"""
job = device.run(
    program,
    shots=10,
    repetition_period=100e-6,
    metrics='optimized_circuit',
    optimizations='default_mapping_pass'
)

Since we have a job, we can now take a look at some of the OQCJob functions:

>>> optimized_circuit = job.metrics()["optimized_circuit"]
>>> print(optimized_circuit)
OPENQASM 2.0;
include "qelib1.inc";

qreg ringNode[2];
creg c[2];
u3(0.5*pi,0.0*pi,1.0*pi) ringNode[0];
u3(0.5*pi,0.0*pi,1.0*pi) ringNode[1];
measure ringNode[0] -> c[0];
measure ringNode[1] -> c[1];

>>> job.timings()
{'RECEIVER_DEQUEUED': '2024-05-29 19:00:02.982576+00:00',
 'RECEIVER_ENQUEUED': '2024-05-29 19:00:03.296029+00:00',
 'RECEIVER_FROM_SCC': '2024-05-29 19:00:03.295922+00:00',
 'RECEIVER_TO_SCC': '2024-05-29 19:00:02.982857+00:00',
 'SERVER_DEQUEUED': '2024-05-29 19:00:03.304669+00:00',
 'SERVER_ENQUEUED': '2024-05-29 19:00:02.964416+00:00',
 'SERVER_RECEIVED': '2024-05-29 19:00:02.912762+00:00'}
>>> job.metadata()
{'config': '{"$type": "<class \'scc.compiler.config.CompilerConfig\'>", "$data": {"repeats": 100, "repetition_period": 0.0002, "results_format": {"$type": "<class \'scc.compiler.config.QuantumResultsFormat\'>", "$data": {"format": {"$type": "<enum \'scc.compiler.config.InlineResultsProcessing\'>", "$value": 1}, "transforms": {"$type": "<enum \'scc.compiler.config.ResultsFormatting\'>", "$value": 3}}}, "metrics": {"$type": "<enum \'scc.compiler.config.MetricsType\'>", "$value": 2}, "active_calibrations": [], "optimizations": {"$type": "<class \'scc.compiler.config.Tket\'>", "$data": {"tket_optimizations": {"$type": "<enum \'scc.compiler.config.TketOptimizations\'>", "$value": 2}}}, "error_mitigation": null}}',
 'created_at': 'Wed, 29 May 2024 19:00:02 GMT',
 'id': '7f13327c-4fcf-40b7-9b57-ae83638b4f5a',
 'qpu_id': 'qpu:uk:2:d865b5a184',
 'tag': None}
>>> job.error()
'There is no error message available for this task.'

Now we take a look at the result:

>>> res = job.result()
>>> res.measurements()
[[0, 0],
 [0, 0],
 [0, 1],
 [1, 0],
 [1, 0],
 [1, 0],
 [1, 1],
 [1, 1],
 [1, 1],
 [1, 1]]

See how to visualize these results in the Visualization section.