Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/device-access.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ qpus:
fake-iqm-20q:
provider: fake-iqm
provider-device-id: fake-iqm-20q
backline-local:
provider: backline
provider-device-id: backline-local
ibm-156-nh:
provider: ibm
provider-device-id: ibm_156_nh
Expand Down
3 changes: 3 additions & 0 deletions config/qfw-slurm/plugin.conf
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ service_id=shim-aws-qpm

[resource "fake-iqm-20q"]
service_id=fake-iqm

[resource "backline-local"]
service_id=backline-local
2 changes: 2 additions & 0 deletions config/qfw-slurm/resources.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ return {
["aws-ionq-aria-1"] = "shim-aws-qpm",
["aws-rigetti-ankaa"] = "shim-aws-qpm",
["fake-iqm-20q"] = "fake-iqm",
["backline-local"] = "backline-local",
},
partitions = {
normal = {
Expand All @@ -18,6 +19,7 @@ return {
["aws-ionq-aria-1"] = true,
["aws-rigetti-ankaa"] = true,
["fake-iqm-20q"] = true,
["backline-local"] = true,
},
},
},
Expand Down
9 changes: 9 additions & 0 deletions config/qpu-users.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"fake-iqm-20q": {
"enabled": true
},
"backline-local": {
"enabled": true
},
"ibm-156-nh": {
"enabled": true,
"api_key": "",
Expand Down Expand Up @@ -48,6 +51,9 @@
"fake-iqm-20q": {
"enabled": true
},
"backline-local": {
"enabled": true
},
"ibm-156-nh": {
"enabled": true,
"api_key": "",
Expand Down Expand Up @@ -86,6 +92,9 @@
"fake-iqm-20q": {
"enabled": true
},
"backline-local": {
"enabled": true
},
"ibm-156-nh": {
"enabled": true,
"api_key": "",
Expand Down
15 changes: 15 additions & 0 deletions config/site-services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ services:
provider-launch:
type: internal

# Client-executed: reserves Backline controllers, decoders and transports and
# returns a placement; the application runs the program itself.
- name: backline-local
credential-mode: no-secret
module: svc_backline_qpm
load-modules: svc_backline_qpm,api_launcher
environment-modules:
- backline
agent-prefix: qpm_backline
device-id: backline-local
listen-port: 8690
telnet-port: 8691
provider-launch:
type: internal

- name: iqm-ornl-20q
credential-mode: required
module: svc_iqm_qpm
Expand Down
23 changes: 23 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,29 @@ services:
networks:
- slurm-network

qec-backline-head:
<<: *slurmd-resources
image: ${IMAGE_NAME}:${IMAGE_TAG}
init: true
command: ["slurmd"]
hostname: qec-backline-head
container_name: qec-backline-head
volumes:
- root_ssh:/root/.ssh
- etc_munge:/etc/munge
- etc_slurm:/etc/slurm
- slurm_jobdir:/data
- var_log_slurm:/var/log/slurm
- ./shared-dir:/mnt:z
- ${QFW_CONTAINER_BASE}:/workspace/qfw-container-base:z
- ${QFW_CONTAINER_BASE}/home:/workspace/home:z
expose:
- "6818"
depends_on:
- "slurmctld"
networks:
- slurm-network

volumes:
etc_munge:
etc_slurm:
Expand Down
78 changes: 78 additions & 0 deletions docs/backline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Backline QPM (`backline-local`)

`backline-local` is a client-executed QPM (QFw `services/svc_backline_qpm`). A
reservation picks a Backline controller, QEC decoder coprocessor and
transport from the site inventory; the application reads that placement and runs its
PennyLane/Catalyst program on it. It never sends circuits through QRI.

It runs on `qec-backline-head` and is opt-in: `qfw-site-services start` (target `all`) does not
start it.

## Set up once

The Backline QPM is in [openQSE/QFw](https://github.com/openQSE/QFw). The image keeps its
stock QFw; the Backline QPM runs from a development override built from QFw `main` (see
"Build and use a development QFw override" in the README).

On the host:

```bash
git clone https://github.com/openQSE/QFw-SLURM-Cluster
cd QFw-SLURM-Cluster
SHARED=/path/to/shared-dir
./do_configure.sh --prefix "$SHARED"
git clone --recursive https://github.com/openQSE/QFw "$SHARED/QFw"
./do_build.sh
./do_startup.sh
./do_qfw_build.sh --clean # the override, in $SHARED/qfw-install
```

In the cluster:

```bash
./do_ssh.sh # into the cluster, as root
/path/to/QFw-SLURM-Cluster/tools/install-backline-venv.sh # shared venv: Catalyst 0.16.0b1 wheels
qfw-site-services start # directory, gateway and the default QPMs
export QFW_INSTALL_PREFIX=/workspace/qfw-container-base/qfw-install # the override
qfw-site-services --target backline start
qfw-site-services --target backline status
```

The `backline` environment module (`modulefiles/backline`) points `CATALYST_LIB` at the
shared venv, so the QPM's inventory finds the precompiled decoders.

## Run a Backline program

The job script is `qri-backline/examples/job.sh`, cloned to
`/workspace/qfw-container-base/qri-backline` by `tools/install-backline-venv.sh`:

```bash
sbatch -A <account> /workspace/qfw-container-base/qri-backline/examples/job.sh
```

qfw-slurm requires a Slurm account (`-A`); the provisioned test users use `qfw-test`.

The job makes two requests:

1. **Quantum resources.** The `#SBATCH --qpu=backline-local ...` lines. qfw-slurm reserves
`backline-local`'s quantum budget while the job is allocated and passes the reservation to
each step in `QFW_RESERVATIONS`. The allocation's teardown releases it.
2. **Classical QEC resources.** The program asks the QPM for them against that reservation
(`reserve` with `for_reservation` and the `resource_intent` in
`intents/cpu-steane-memcpy.yaml`: a CPU controller, a Steane decoder, memcpy). The QPM
starts a `catalyst-executor` on `qec-backline-head` for the reservation and returns its
address in the placement; the program compiles locally and runs controller and decoder
there. The executor stops when the reservation ends.

`--max-qubits` counts logical qubits.

## Files

| File | Role |
| --- | --- |
| `docker-compose.yml`, `slurm.conf`, `tools/provision-qfw-cluster.sh` | the `qec-backline-head` node |
| `config/site-services.yaml` | the `backline-local` service, loading the `backline` module |
| `config/device-access.yaml`, `config/qpu-users.json` | device record and user entitlement |
| `config/qfw-slurm/resources.lua`, `config/qfw-slurm/plugin.conf` | `--qpu=backline-local` |
| `modulefiles/backline`, `tools/install-backline-venv.sh` | the shared Backline venv and the demo checkout |
| `tools/qfw-site-services` | `--target backline` start, stop, status |
17 changes: 17 additions & 0 deletions modulefiles/backline
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#%Module1.0
##
## Backline (PennyLane + Catalyst) venv on the shared QFw volume, created by
## tools/install-backline-venv.sh. Loaded by the backline-local QPM, so its
## inventory finds the decoder libraries, and by Backline applications.
##

proc ModulesHelp { } {
puts stderr "Adds the shared Backline virtual environment to the environment."
}

module-whatis "Backline for QFw"

set venv /workspace/qfw-container-base/backline-venv

setenv BACKLINE_VENV $venv
setenv CATALYST_LIB $venv/lib/python3.12/site-packages/catalyst/lib
3 changes: 2 additions & 1 deletion slurm.conf
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ NodeName=shim-head Features=qfw-service,shim,qpm,qpm-shim-ornl-20q CPUs=4 Socket
NodeName=ibm-156-nh Features=qfw-service,ibm,qpm,qpm-shim-ibm-156-nh CPUs=4 Sockets=1 CoresPerSocket=4 ThreadsPerCore=1 RealMemory=6000 State=UNKNOWN
NodeName=aws Features=qfw-service,aws,qpm,qpm-shim-aws-qpm CPUs=4 Sockets=1 CoresPerSocket=4 ThreadsPerCore=1 RealMemory=6000 State=UNKNOWN
NodeName=fake-iqm-head Features=qfw-service,fake-iqm,qpm,qpm-fake-iqm CPUs=4 Sockets=1 CoresPerSocket=4 ThreadsPerCore=1 RealMemory=6000 State=UNKNOWN
NodeName=qec-backline-head Features=qfw-service,backline,qpm,qpm-backline-local CPUs=4 Sockets=1 CoresPerSocket=4 ThreadsPerCore=1 RealMemory=6000 State=UNKNOWN

#
# PARTITIONS
PartitionName=normal Default=YES Nodes=c[1-8] Priority=50 DefMemPerCPU=200 OverSubscribe=EXCLUSIVE MaxTime=5-00:00:00 DefaultTime=5-00:00:00 State=UP
PartitionName=qfw-services Default=NO Nodes=nwqsim-head,nwqsim-worker-[1-2],iqm-head,shim-head,ibm-156-nh,aws,fake-iqm-head AllowGroups=root Priority=10 DefMemPerCPU=200 OverSubscribe=EXCLUSIVE MaxTime=1-00:00:00 DefaultTime=01:00:00 State=UP
PartitionName=qfw-services Default=NO Nodes=nwqsim-head,nwqsim-worker-[1-2],iqm-head,shim-head,ibm-156-nh,aws,fake-iqm-head,qec-backline-head AllowGroups=root Priority=10 DefMemPerCPU=200 OverSubscribe=EXCLUSIVE MaxTime=1-00:00:00 DefaultTime=01:00:00 State=UP
30 changes: 30 additions & 0 deletions tools/install-backline-venv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
# Create the shared Backline venv used by the backline-local QPM and by Backline
# applications. Run once inside any cluster container (the volume is shared):
#
# docker exec slurmctld /mnt/../tools/install-backline-venv.sh # or ./do_ssh.sh, then run it
#
# The venv must use the same Python as the QFw venv (3.12 on the Rocky 10 image):
# applications run through qfw-srun, whose embedded DEFw interpreter checks this.
set -euo pipefail

venv="${BACKLINE_VENV:-/workspace/qfw-container-base/backline-venv}"
python="${PYTHON:-python3}"
release="https://github.com/PennyLaneAI"

"${python}" -c 'import sys; assert sys.version_info >= (3, 12), "Catalyst 0.16 wheels need Python 3.12+"'
"${python}" -m venv "${venv}"
"${venv}/bin/pip" install -q --upgrade pip
"${venv}/bin/pip" install -q pennylane-catalyst==0.16.0b1 \
-f "${release}/pennylane/releases/expanded_assets/v0.46.0b1" \
-f "${release}/pennylane-lightning/releases/expanded_assets/v0.46.0b1" \
-f "${release}/catalyst/releases/expanded_assets/v0.16.0b1"
# DEFw's client-side imports, for applications that reach QFw services.
"${venv}/bin/pip" install -q PyYAML "jsonschema>=4" netifaces psutil paramiko rich

lib="$("${venv}/bin/python" -c 'import importlib.util, pathlib; print(pathlib.Path(list(importlib.util.find_spec("catalyst").submodule_search_locations)[0]) / "lib")')"
test -f "${lib}/libsteane_coprocessor_cpu.so"
# The demo program, on the shared volume so jobs on any compute node can run it.
demo="${QRI_BACKLINE_DIR:-/workspace/qfw-container-base/qri-backline}"
[ -d "${demo}/.git" ] || git clone -q https://github.com/josephleekl/qri-backline "${demo}"
echo "backline venv ready: ${venv} (decoders in ${lib})"
2 changes: 1 addition & 1 deletion tools/provision-qfw-cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ containers=(
slurmdbd slurmctld slurmrestd
c1 c2 c3 c4 c5 c6 c7 c8
nwqsim-head nwqsim-worker-1 nwqsim-worker-2
iqm-head shim-head ibm-156-nh aws fake-iqm-head
iqm-head shim-head ibm-156-nh aws fake-iqm-head qec-backline-head
)

die() {
Expand Down
47 changes: 44 additions & 3 deletions tools/qfw-site-services
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ QFW_SHIM_NODE="${QFW_SHIM_NODE:-shim-head}"
QFW_IBM_NODE="${QFW_IBM_NODE:-ibm-156-nh}"
QFW_AWS_NODE="${QFW_AWS_NODE:-aws}"
QFW_FAKE_IQM_NODE="${QFW_FAKE_IQM_NODE:-fake-iqm-head}"
QFW_BACKLINE_NODE="${QFW_BACKLINE_NODE:-qec-backline-head}"
QFW_SIMULATOR_NODES="${QFW_SIMULATOR_NODES:-nwqsim-head,nwqsim-worker-1,nwqsim-worker-2}"
QFW_GATEWAY_HOST="${QFW_GATEWAY_HOST:-slurmctld}"
QFW_GATEWAY_PORT="${QFW_GATEWAY_PORT:-18095}"
Expand All @@ -29,6 +30,7 @@ shim_run_dir="${QFW_SITE_RUN_ROOT}/qpm/shim-ornl-20q"
ibm_run_dir="${QFW_SITE_RUN_ROOT}/qpm/shim-ibm-156-nh"
aws_run_dir="${QFW_SITE_RUN_ROOT}/qpm/shim-aws-qpm"
fake_iqm_run_dir="${QFW_SITE_RUN_ROOT}/qpm/fake-iqm"
backline_run_dir="${QFW_SITE_RUN_ROOT}/qpm/backline-local"
gateway_run_dir="${QFW_SITE_RUN_ROOT}/gateway"
gateway_pid_file="${gateway_run_dir}/supervisor.pid"
gateway_log_file="${QFW_GATEWAY_LOG_DIR}/gateway.log"
Expand All @@ -52,8 +54,10 @@ Actions:
Options:
--target TARGET
Select all, directory, nwqsim, iqm, shim, ibm, aws, fake-iqm,
or gateway
(default: all)
backline, or gateway
(default: all). backline is opt-in: "all" starts and reports
without it, and stops it only when it is running
(needs tools/install-backline-venv.sh first)
--json Emit one machine-readable status document
--dry-run Print the commands without executing them
EOF
Expand Down Expand Up @@ -371,6 +375,11 @@ stop_fake_iqm() {
--run-dir "${fake_iqm_run_dir}"
}

stop_backline() {
run_qfw "${QFW_BACKLINE_NODE}" qfw-qpm-svc stop \
--run-dir "${backline_run_dir}"
}

start_gateway() {
require_directory || return 1
if prepare_component_start \
Expand Down Expand Up @@ -427,6 +436,11 @@ fake_iqm_ready() {
--run-dir "${fake_iqm_run_dir}"
}

backline_ready() {
run_qfw "${QFW_BACKLINE_NODE}" qfw-qpm-svc status \
--run-dir "${backline_run_dir}"
}

require_directory() {
if directory_ready >/dev/null 2>&1; then
return 0
Expand Down Expand Up @@ -459,6 +473,9 @@ require_directory_dependents_stopped() {
if fake_iqm_ready >/dev/null 2>&1; then
active+=(fake-iqm)
fi
if backline_ready >/dev/null 2>&1; then
active+=(backline)
fi
if gateway_managed_ready >/dev/null 2>&1; then
active+=(gateway)
fi
Expand Down Expand Up @@ -579,6 +596,20 @@ start_fake_iqm() {
--timeout "${QFW_SITE_START_TIMEOUT}"
}

start_backline() {
require_directory || return 1
if prepare_component_start \
"Backline QPM" backline_ready stop_backline; then
return 0
fi
run_qfw "${QFW_BACKLINE_NODE}" qfw-qpm-svc start \
--scope site \
--run-dir "${backline_run_dir}" \
--site-config "${QFW_SITE_CONFIG}" \
--service-id backline-local \
--timeout "${QFW_SITE_START_TIMEOUT}"
}

start_services() {
local directory_started=false
local nwqsim_started=false
Expand Down Expand Up @@ -964,6 +995,9 @@ stop_services() {

stop_gateway || status=1
stop_fake_iqm || status=1
if ${dry_run} || backline_ready >/dev/null 2>&1; then
stop_backline || status=1
fi
stop_aws || status=1
stop_ibm || status=1
stop_shim || status=1
Expand All @@ -974,6 +1008,11 @@ stop_services() {
}

target_status() {
if [[ "${target}" == backline ]]; then
# Opt-in, so not part of the "all" status table.
backline_ready
return
fi
service_status
}

Expand All @@ -987,6 +1026,7 @@ start_target() {
ibm) start_ibm ;;
aws) start_aws ;;
fake-iqm) start_fake_iqm ;;
backline) start_backline ;;
gateway) start_gateway ;;
esac
}
Expand All @@ -1004,6 +1044,7 @@ stop_target() {
ibm) stop_ibm ;;
aws) stop_aws ;;
fake-iqm) stop_fake_iqm ;;
backline) stop_backline ;;
gateway) stop_gateway ;;
esac
}
Expand Down Expand Up @@ -1065,7 +1106,7 @@ main() {
return 2
}
case "${target}" in
all|directory|nwqsim|iqm|shim|ibm|aws|fake-iqm|gateway) ;;
all|directory|nwqsim|iqm|shim|ibm|aws|fake-iqm|backline|gateway) ;;
*) die "unknown target: ${target}" ;;
esac
if ! ${dry_run} && [[ ${EUID} -ne 0 ]]; then
Expand Down