Skip to content

Commit

Permalink
Add null qubit (#1179)
Browse files Browse the repository at this point in the history
**Context:** Implement a Catalyst runtime plugin that mocks out all
functions in the QuantumDevice interface and implement a PennyLane
device plugin (implemented in
[qjit_device.py](https://github.com/PennyLaneAI/catalyst/blob/main/frontend/catalyst/device/qjit_device.py))
providing access to the Null runtime device.

**Description of the Change:**
- [x] Rename the device from "dummy" to "null".
- [x] Make sure the device is up to date with the latest version of the
QuantumDevice interface.
- [x] Ensure proper unit testing of the device in C++.
- [x] Package and ship this plugin for both development builds and
wheels.

**Benefits:**
Such a device will be useful for a few reasons:

* "execute" programs without needing to install additional plugins
* Simplify and speed up integration tests when numerical results aren't
needed
* Benchmark programs while excluding the cost of simulation -> exposes
the compilation and infrastructure overhead
* Can also act as an instruction tracer / program printer

**Possible Drawbacks:** It is a surprise.

**Related GitHub Issues:**

[sc-72829] [sc-72830]

---------

Co-authored-by: erick-xanadu <[email protected]>
Co-authored-by: David Ittah <[email protected]>
  • Loading branch information
3 people authored Oct 11, 2024
1 parent 6126963 commit 0d7f99a
Show file tree
Hide file tree
Showing 35 changed files with 933 additions and 346 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-wheel-linux-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ jobs:
-DENABLE_OPENMP=OFF \
-DLQ_ENABLE_KERNEL_OMP=OFF
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_dummy
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_null_qubit
# Build OQC-Runtime
- name: Build OQC-Runtime
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-wheel-macos-arm64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ jobs:
-DENABLE_OPENMP=OFF \
-DLQ_ENABLE_KERNEL_OMP=OFF
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_dummy
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_null_qubit
- name: Test Catalyst-Runtime
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-wheel-macos-x86_64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ jobs:
-DENABLE_OPENMP=OFF \
-DLQ_ENABLE_KERNEL_OMP=OFF
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_dummy
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_null_qubit
# Build OQC-Runtime
- name: Build OQC-Runtime
Expand Down
9 changes: 0 additions & 9 deletions .github/workflows/check-catalyst.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,6 @@ jobs:
ENABLE_ASAN=OFF \
make runtime
# This is needed in the artifact for the pytests
# Note the lack of sanitizers.
# Left other flags the same.
COMPILER_LAUNCHER="" \
C_COMPILER=$(which ${{ needs.constants.outputs[format('c_compiler.{0}', matrix.compiler)] }}) \
CXX_COMPILER=$(which ${{ needs.constants.outputs[format('cxx_compiler.{0}', matrix.compiler)] }}) \
RT_BUILD_DIR="$(pwd)/runtime-build" \
make dummy_device
COMPILER_LAUNCHER="" \
C_COMPILER=$(which ${{ needs.constants.outputs[format('c_compiler.{0}', matrix.compiler)] }}) \
CXX_COMPILER=$(which ${{ needs.constants.outputs[format('cxx_compiler.{0}', matrix.compiler)] }}) \
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/scripts/linux_arm64/rh8/build_catalyst.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ export PYTHON_PACKAGE=$4
export PYTHON_ALTERNATIVE_VERSION=$5

# Install system dependencies
dnf update -y
dnf update -y
dnf install -y libzstd-devel gcc-toolset-${GCC_VERSION}
if [ "$PYTHON_VERSION" != "3.10" ]; then
dnf install -y ${PYTHON_PACKAGE} ${PYTHON_PACKAGE}-devel
fi
dnf clean all -y

# Make GCC the default compiler
source /opt/rh/gcc-toolset-${GCC_VERSION}/enable -y
export C_COMPILER=/opt/rh/gcc-toolset-${GCC_VERSION}/root/usr/bin/gcc
source /opt/rh/gcc-toolset-${GCC_VERSION}/enable -y
export C_COMPILER=/opt/rh/gcc-toolset-${GCC_VERSION}/root/usr/bin/gcc
export CXX_COMPILER=/opt/rh/gcc-toolset-${GCC_VERSION}/root/usr/bin/g++

# Set the right Python interpreter
rm -rf /usr/bin/python3
ln -s /opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/bin/python3 /usr/bin/python3
ln -s /opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/bin/python3 /usr/bin/python3
export PYTHON=/usr/bin/python3

# Add LLVM, Python and GCC to the PATH env var
Expand All @@ -51,7 +51,7 @@ cmake -S runtime -B runtime-build -G Ninja \
-DENABLE_OPENQASM=ON \
-DENABLE_OPENMP=OFF \
-DLQ_ENABLE_KERNEL_OMP=OFF
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_dummy
cmake --build runtime-build --target rt_capi rtd_lightning rtd_openqasm rtd_null_qubit

# Build OQC
export OQC_BUILD_DIR="/catalyst/oqc-build"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ frontend/mlir_quantum
doc/_build
doc/code/api
.ipynb*

# Development
venv
8 changes: 2 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ help:
@echo " mlir to build MLIR and custom Catalyst dialects"
@echo " runtime to build Catalyst Runtime"
@echo " oqc to build Catalyst-OQC Runtime"
@echo " dummy_device needed for frontend tests"
@echo " test to run the Catalyst test suites"
@echo " docs to build the documentation for Catalyst"
@echo " clean to uninstall Catalyst and delete all temporary and cache files"
Expand Down Expand Up @@ -112,9 +111,6 @@ dialects:
runtime:
$(MAKE) -C runtime runtime

dummy_device:
$(MAKE) -C runtime dummy_device

oqc:
$(MAKE) -C frontend/catalyst/third_party/oqc/src oqc

Expand Down Expand Up @@ -268,11 +264,11 @@ format-frontend:
ifdef check
$(PYTHON) ./bin/format.py --check $(if $(version:-=),--cfversion $(version)) ./frontend/catalyst/utils
black --check --verbose .
isort --check --diff .
isort --check --diff .
else
$(PYTHON) ./bin/format.py $(if $(version:-=),--cfversion $(version)) ./frontend/catalyst/utils
black .
isort .
isort .
endif

.PHONY: docs clean-docs
Expand Down
4 changes: 2 additions & 2 deletions doc/dev/custom_devices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ The Pennylane device API allows you to build a QJIT compatible device in a simpl
.. code-block:: python
class CustomDevice(qml.devices.Device):
"""Dummy Device"""
"""Custom Device"""
config = pathlib.Path("absolute/path/to/configuration/file.toml")
Expand All @@ -178,7 +178,7 @@ The Pennylane device API allows you to build a QJIT compatible device in a simpl
the location to the shared object with the C/C++ device implementation.
"""
return "CustomDevice", "absolute/path/to/librtd_dummy.so"
return "CustomDevice", "absolute/path/to/librtd_custom.so"
def __init__(self, shots=None, wires=None):
super().__init__(wires=wires, shots=shots)
Expand Down
1 change: 1 addition & 0 deletions frontend/catalyst/device/qjit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
# TODO: This should be removed after implementing `get_c_interface`
# for the following backend devices:
SUPPORTED_RT_DEVICES = {
"null.qubit": ("NullQubit", "librtd_null_qubit"),
"lightning.qubit": ("LightningSimulator", "librtd_lightning"),
"braket.aws.qubit": ("OpenQasmDevice", "librtd_openqasm"),
"braket.local.qubit": ("OpenQasmDevice", "librtd_openqasm"),
Expand Down
3 changes: 0 additions & 3 deletions frontend/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@

# pylint: disable=unused-import,wrong-import-position
import platform

import numpy as np
import pennylane as qml
import pytest


Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions frontend/test/lit/test_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ def get_c_interface():
"""
system_extension = ".dylib" if platform.system() == "Darwin" else ".so"
lib_path = (
get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/librtd_dummy" + system_extension
get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/librtd_null_qubit" + system_extension
)
return "dummy.remote", lib_path
return "NullQubit", lib_path

def execute(self, circuits, execution_config):
"""Execution."""
Expand Down
25 changes: 15 additions & 10 deletions frontend/test/lit/test_device_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

"""Test for the device API.
"""
import os
import pathlib
import platform
from typing import Optional

Expand All @@ -27,11 +29,14 @@
from catalyst import qjit
from catalyst.compiler import get_lib_path

TEST_PATH = os.path.dirname(__file__)
CONFIG_CUSTOM_DEVICE = pathlib.Path(f"{TEST_PATH}/../custom_device/custom_device.toml")

class DummyDevice(Device):
"""A dummy device from the device API."""

config = get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/backend/dummy_device.toml"
class CustomDevice(Device):
"""A custom device that does nothing."""

config = CONFIG_CUSTOM_DEVICE

def __init__(self, wires, shots=1024):
super().__init__(wires=wires, shots=shots)
Expand All @@ -42,11 +47,11 @@ def get_c_interface():
the location to the shared object with the C/C++ device implementation.
"""
system_extension = ".dylib" if platform.system() == "Darwin" else ".so"
lightning_lib_path = (
get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/librtd_lightning" + system_extension
null_qubit_lib_path = (
get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/librtd_null_qubit" + system_extension
)

return "dummy.remote", lightning_lib_path
return "Custom", null_qubit_lib_path

def execute(self, circuits, execution_config):
"""Execute"""
Expand All @@ -65,8 +70,8 @@ def preprocess(self, execution_config: Optional[ExecutionConfig] = None):
def test_circuit():
"""Test a circuit compilation to MLIR when using the new device API."""

# CHECK: quantum.device["[[PATH:.*]]librtd_lightning.{{so|dylib}}", "dummy.remote", "{'shots': 2048}"]
dev = DummyDevice(wires=2, shots=2048)
# CHECK: quantum.device["[[PATH:.*]]librtd_null_qubit.{{so|dylib}}", "Custom", "{'shots': 2048}"]
dev = CustomDevice(wires=2, shots=2048)

@qjit(target="mlir")
@qml.qnode(device=dev)
Expand All @@ -90,8 +95,8 @@ def test_preprocess():
using the new device API.
TODO: we need to readd the two check-not once we accept the device preprocessing."""

# CHECK: quantum.device["[[PATH:.*]]librtd_lightning.{{so|dylib}}", "dummy.remote", "{'shots': 2048}"]
dev = DummyDevice(wires=2, shots=2048)
# CHECK: quantum.device["[[PATH:.*]]librtd_null_qubit.{{so|dylib}}", "Custom", "{'shots': 2048}"]
dev = CustomDevice(wires=2, shots=2048)

@qjit(target="mlir")
@qml.qnode(device=dev)
Expand Down
22 changes: 22 additions & 0 deletions frontend/test/pytest/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2023 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Pytest configuration file for Catalyst test suite.
"""

import os
import pathlib

TEST_PATH = os.path.dirname(__file__)
CONFIG_CUSTOM_DEVICE = pathlib.Path(f"{TEST_PATH}/../custom_device/custom_device.toml")
4 changes: 2 additions & 2 deletions frontend/test/pytest/device/test_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ def get_c_interface():
"""
system_extension = ".dylib" if platform.system() == "Darwin" else ".so"
lib_path = (
get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/librtd_dummy" + system_extension
get_lib_path("runtime", "RUNTIME_LIB_DIR") + "/librtd_null_qubit" + system_extension
)
return "dummy.remote", lib_path
return "NullQubit", lib_path

def execute(self, circuits, execution_config):
"""Execution."""
Expand Down
18 changes: 0 additions & 18 deletions frontend/test/pytest/test_config_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from tempfile import TemporaryDirectory
from textwrap import dedent

import pennylane as qml
import pytest

from catalyst.utils.exceptions import CompileError
Expand All @@ -32,23 +31,6 @@
)


class DeviceToBeTested(qml.devices.QubitDevice):
"""Test device"""

name = "Dummy Device"
short_name = "dummy.device"
pennylane_requires = "0.33.0"
version = "0.0.1"
author = "Dummy"

operations = []
observables = []

def apply(self, operations, **kwargs):
"""Unused"""
raise RuntimeError("Only C/C++ interface is defined")


def get_test_config(config_text: str) -> TOMLDocument:
"""Parse test config into the TOMLDocument structure"""
with TemporaryDirectory() as d:
Expand Down
Loading

0 comments on commit 0d7f99a

Please sign in to comment.