QubiC Example

Connect a QubiC backend to Coda with coda-qubic.

coda-qubic is an example coda-node integration for QubiC systems. Use it as a reference for connecting your own QPU with a device YAML, executor factory, native IR translator, hardware runner, and node startup flow.

At runtime, the integration follows this flow:

  • Coda sends a compiled NativeGateIR job to the node.
  • coda-qubic translates the IR into QubiC gate programs.
  • The runner executes the program on QubiC hardware, a QubiC RPC server, or a simulator.

coda-qubic requires Python 3.12 or newer. coda-node itself supports Python 3.11 or newer.

What coda-qubic provides

  • coda_qubic.executor_factory:create_executor for coda-node
  • QubiCConfig for validating device YAML
  • QubiCJobRunner, which implements executor.run(ir, shots)
  • Translation from Coda NativeGateIR to QubiC gate-level programs
  • RPC, local hardware, pulse simulator, and Qiskit noisy simulator modes
  • Optional cancellation support through cancel_current_job()

Install

Clone the integration and install dependencies:

$git clone https://github.com/conductorquantum/coda-qubic.git
$cd coda-qubic
$uv sync --dev

For QubiC hardware, install the QubiC stack as well:

$./scripts/install-qubic-stack.sh

For the Qiskit simulator path, install the optional Qiskit dependency group:

$uv pip install 'coda-qubic[qiskit]'

Configure a QubiC lab

For a real QubiC setup, collect these files from the lab:

  • qubitcfg.json: qubit calibration and gate definitions
  • channel_config.json: FPGA channel mapping
  • classifier.pkl or classifier JSON: readout discrimination

Create site/device.yaml next to those files:

1target: cnot
2num_qubits: 20
3calibration_path: ./qubitcfg.json
4channel_config_path: ./channel_config.json
5classifier_path: ./classifier.pkl
6
7runner_mode: rpc
8rpc_host: 192.168.1.120
9rpc_port: 9095

All file paths are resolved relative to the YAML file. The num_qubits value must match the device derived from the calibration file.

Simulator configuration

For end-to-end testing without hardware or QubiC vendor dependencies, use Qiskit noisy simulation:

1framework: qubic
2target: cnot
3num_qubits: 3
4runner_mode: qiskit_sim
5
6# Optional noise parameters
7# single_qubit_error_rate: 0.001
8# two_qubit_error_rate: 0.01
9# measurement_error_rate: 0.01

This mode reports a synthetic device and executes jobs with Qiskit AerSimulator.

Validate locally

Validate the YAML:

$uv run python -c "
>from coda_qubic.config import QubiCConfig
>
>config = QubiCConfig.from_yaml('site/device.yaml')
>print('OK:', config.target, config.num_qubits, 'qubits')
>"

Run a small circuit through the QubiC executor:

$uv run python -c "
>import asyncio
>from coda_node.server.ir import GateOp, IRMetadata, NativeGateIR
>from coda_qubic.config import QubiCConfig
>from coda_qubic.executor_factory import build_executor
>
>config = QubiCConfig.from_yaml('site/device.yaml')
>executor = build_executor(config)
>
>ir = NativeGateIR(
> num_qubits=config.num_qubits,
> target=config.target,
> gates=[
> GateOp(gate='x90', qubits=[0], params=[]),
> GateOp(gate='cnot', qubits=[0, 1], params=[]),
> ],
> measurements=[0, 1],
> metadata=IRMetadata(source_hash='test', compiled_at='2026-04-27T00:00:00Z'),
>)
>
>result = asyncio.run(executor.run(ir, shots=100))
>print(result.counts)
>"

Run through coda-node

If coda-qubic is the only backend package installed and ./site/device.yaml exists, coda-node can discover the executor factory automatically:

$uv run coda-node start --token <node-token>

To be explicit:

$CODA_EXECUTOR_FACTORY=coda_qubic.executor_factory:create_executor \
>CODA_DEVICE_CONFIG=./site/device.yaml \
>uv run coda-node start --token <node-token>

If your node token requires VPN mode, OpenVPN may need elevated permissions to create the tunnel interface:

$sudo env CODA_DEVICE_CONFIG=./site/device.yaml uv run coda-node start --token <node-token>

After first connect, credentials are persisted. Restart without a token:

$uv run coda-node start

How the example maps to the Node model

coda-qubic is a reference for the main integration points:

  • Device config: QubiCConfig.from_yaml("site/device.yaml")
  • Factory: coda_qubic.executor_factory:create_executor
  • Executor: QubiCJobRunner.run(ir, shots)
  • Translation: Coda native gate IR to QubiC gate programs
  • Results: QubiC counts normalized into ExecutionResult
  • Cancellation: cancel_current_job() sets a cancel flag checked by the runner

Use the same shape for your own backend package, replacing QubiC-specific config, translation, and hardware calls with your device stack.