From 110093e41c2d73e6c8ad4a212f931b1bd1d9f694 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Mon, 27 Nov 2023 10:18:02 -0700 Subject: [PATCH 1/6] Add job for Apple Silicon MacOS build --- .github/workflows/build.yml | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 714e388..193d916 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -161,3 +161,85 @@ jobs: with: name: macos path: ${{ github.workspace }}/artifacts/* + + build-macos-arm: + name: Build Apple-Silicon libquil.dylib + runs-on: macos-latest-xlarge + + steps: + + - uses: actions/checkout@v2 + with: + path: "libquil" + + - name: Cache SBCL + id: cache-sbcl-macos + uses: actions/cache@v3 + with: + path: | + /usr/local/lib/libsbcl.so + /usr/local/lib/sbcl + /usr/local/bin/sbcl + ~/quicklisp + ~/.sbclrc + key: ${{ runner.os }}-build-${{ hashFiles('**/macos-versions' )}} + + - uses: Homebrew/actions/setup-homebrew@master + name: Setup homebrew + + - if: steps.cache-sbcl-macos.outputs.cache-hit != 'true' + name: Install SBCL, libraries, and quicklisp + run: | + brew install sbcl git + git clone --branch sbcl-2.2.4 https://git.code.sf.net/p/sbcl/sbcl /tmp/sbcl + cd /tmp/sbcl && sudo sh make.sh --without-compact-instance-header --without-immobile-space --without-immobile-code && sudo sh make-shared-library.sh + brew remove -f sbcl + sudo sh install.sh + sudo cp src/runtime/libsbcl.so /usr/local/lib/libsbcl.so + + brew install wget + wget -P /tmp/ 'https://beta.quicklisp.org/quicklisp.lisp' + sbcl --noinform --non-interactive --load /tmp/quicklisp.lisp --eval "(quicklisp-quickstart:install :dist-url \"http://beta.quicklisp.org/dist/quicklisp/2022-04-01/distinfo.txt\")" + sbcl --noinform --non-interactive --load ~/quicklisp/setup.lisp --eval '(ql-util:without-prompting (ql:add-to-init-file))' + echo "#+quicklisp(push (truename \"$GITHUB_WORKSPACE\") ql:*local-project-directories*)" >> ~/.sbclrc + rm -f /tmp/quicklisp.lisp + cat ~/.sbclrc + + - name: Pull Lisp dependencies + run: | + git clone https://github.com/notmgsk/quilc.git $GITHUB_WORKSPACE/quilc && cd $GITHUB_WORKSPACE/quilc && git checkout bffea7fdb972cc9b3a50b790246b6a3143c88c7a + git clone https://github.com/quil-lang/qvm.git $GITHUB_WORKSPACE/qvm && cd $GITHUB_WORKSPACE/qvm && git checkout 4617625cb6053b1adfd3f7aea9cd2be328b225f6 + git clone https://github.com/quil-lang/magicl.git $GITHUB_WORKSPACE/magicl + git clone https://github.com/stylewarning/cl-permutation $GITHUB_WORKSPACE/cl-permutation + git clone https://github.com/quil-lang/sbcl-librarian.git $GITHUB_WORKSPACE/sbcl-librarian && cd $GITHUB_WORKSPACE/sbcl-librarian && git checkout 04f7e390c777084ac43b8df9e90593a041da2381 + + - name: Install quilc dependencies + run: brew install lapack openblas libffi gfortran + + - name: Build libquil.dylib + run: | + cd $GITHUB_WORKSPACE/libquil + ls + sbcl --noinform --non-interactive --eval '(ql:quickload :sbcl-librarian)' + sbcl --dynamic-space-size 8192 --noinform --non-interactive --eval '(ql:quickload :libquil)' + make + + - name: ls + run: | + ls $GITHUB_WORKSPACE/libquil/libquil.dylib + + - name: Test + run: | + cd $GITHUB_WORKSPACE/libquil/examples/qvm && DYLD_LIBRARY_PATH=$GITHUB_WORKSPACE/libquil make test + cd $GITHUB_WORKSPACE/libquil/examples/quilc && DYLD_LIBRARY_PATH=$GITHUB_WORKSPACE/libquil make test + + - name: Package files + run: | + mkdir -p $GITHUB_WORKSPACE/artifacts/libquil && cp $GITHUB_WORKSPACE/libquil/libquil.h $GITHUB_WORKSPACE/libquil/libquil.dylib $GITHUB_WORKSPACE/libquil/libquil.core /usr/local/lib/libsbcl.so $GITHUB_WORKSPACE/artifacts/libquil + + - name: Store artifact + uses: actions/upload-artifact@v3 + with: + name: macos + path: ${{ github.workspace }}/artifacts/* + From 07373f92ae1b69a14467d73303e116fc84e51bf9 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Tue, 11 Aug 2026 13:52:04 -0600 Subject: [PATCH 2/6] Claude gets it working, maybe --- .gitignore | 2 ++ Makefile | 60 ++++++++++++++++++++++++++++++++++-- README.md | 41 +++++++++++++++++++++++- src/build-image.lisp | 31 +++++++++++++++++-- src/quilc/api.lisp | 2 +- src/quilc/compile.lisp | 6 ++++ src/qvm/qvm-app-imports.lisp | 8 +++-- 7 files changed, 141 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 9e5309c..3387c30 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ libquil.c libquil.core libquil.h libquil.so +libquil.dylib +libquil.py diff --git a/Makefile b/Makefile index c2d9223..8720580 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ .PHONY: all clean -OS:=$(shell uname -s) +OS := $(shell uname -s) + +SBCL ?= sbcl + ifeq ($(OS), Darwin) LIBQUIL_TARGET = libquil.dylib CCFLAGS = -dynamiclib @@ -9,13 +12,64 @@ else CCFLAGS = -shared endif +# The library needs to embed the SBCL runtime. `make.sh` does not build a +# linkable runtime, so it has to be produced separately by running +# `make-shared-library.sh` in the SBCL source tree, and neither `install.sh` nor +# most package managers install the result. Look in the usual places for it, and +# let the user point at it directly with e.g. `make LIBSBCL=/path/to/libsbcl.a`. +# +# Which file to expect depends on the platform: on arm64 macOS the SBCL runtime +# is only built as a static archive (see SBCL's Config.arm64-darwin), so a +# shared libsbcl does not exist there at all. +SBCL_CORE_DIR := $(dir $(shell $(SBCL) --noinform --no-sysinit --no-userinit --non-interactive \ + --eval '(princ (namestring sb-ext:*core-pathname*))' 2>/dev/null)) +LIBSBCL_SEARCH_DIRS := $(SBCL_HOME) $(SBCL_CORE_DIR) /usr/local/lib /usr/lib /opt/homebrew/lib +LIBSBCL_CANDIDATES := \ + $(foreach dir,$(LIBSBCL_SEARCH_DIRS),\ + $(dir)/libsbcl.a $(dir)/libsbcl.dylib $(dir)/libsbcl.so) + +LIBSBCL ?= $(firstword $(wildcard $(LIBSBCL_CANDIDATES))) + +# A static runtime must be linked whole: the entry points the generated +# libquil.c calls are reached only through the core, so without this the linker +# drops most of the archive. Its own dependencies have to be named explicitly +# too, since an archive records none. A shared runtime carries both properties +# already and just needs to be linked normally. +SBCL_STATIC_DEPS ?= $(shell pkg-config --libs libzstd 2>/dev/null || echo -lzstd) -lm -ldl -lpthread + +ifeq ($(suffix $(LIBSBCL)), .a) +ifeq ($(OS), Darwin) + LIBSBCL_LDFLAGS = -Wl,-force_load,$(LIBSBCL) $(SBCL_STATIC_DEPS) +else + LIBSBCL_LDFLAGS = -Wl,--whole-archive $(LIBSBCL) -Wl,--no-whole-archive $(SBCL_STATIC_DEPS) +endif +else ifeq ($(OS), Darwin) +# Link by path rather than -lsbcl: make-shared-library.sh names its output +# libsbcl.so even on macOS, and the -l flag only ever looks for libsbcl.dylib or +# libsbcl.a. + LIBSBCL_LDFLAGS = $(LIBSBCL) +else + LIBSBCL_LDFLAGS = -L$(dir $(LIBSBCL)) -lsbcl +endif + all: $(LIBQUIL_TARGET) libquil.core libquil.c libquil.h libquil.py: src/libquil.lisp src/qvm/*.lisp src/quilc/*.lisp - sbcl --dynamic-space-size 8192 --load "src/build-image.lisp" + $(SBCL) --dynamic-space-size 8192 --load "src/build-image.lisp" $(LIBQUIL_TARGET): libquil.core libquil.c - gcc $(CCFLAGS) -o $@ libquil.c -lsbcl +ifeq ($(LIBSBCL),) + @echo "error: no linkable SBCL runtime found." >&2 + @echo "Searched for libsbcl.a, libsbcl.dylib and libsbcl.so in:" >&2 + @$(foreach dir,$(LIBSBCL_SEARCH_DIRS),echo " $(dir)" >&2;) + @echo "" >&2 + @echo "Build one from an SBCL source tree of the SAME version as $(SBCL):" >&2 + @echo " sh make.sh --with-sb-linkable-runtime && sh make-shared-library.sh" >&2 + @echo "then point make at the result, e.g.:" >&2 + @echo " make LIBSBCL=/path/to/sbcl/src/runtime/libsbcl.a" >&2 + @exit 1 +endif + $(CC) $(CCFLAGS) -o $@ libquil.c $(LIBSBCL_LDFLAGS) clean: rm -f libquil.so libquil.c libquil.h libquil.core libquil.py libquil.dylib example diff --git a/README.md b/README.md index 703950c..40f79e5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ We provide pre-built binaries for the following systems - Linux x64 (tested specifically on Ubuntu) - macOS x64 -Note that ARM is not yet supported on any of the above. +Note that we do not publish pre-built binaries for ARM. ARM machines, including +Apple Silicon, are supported when building from source — see [Building from +source](#building-from-source). ## Requirements @@ -59,6 +61,43 @@ replacing `` with the desired version, e.g. `0.3.0`. If you would like to manually install the library (for example in the case where you want to install the library to a non-standard location), find the appropriate version and operating system from the [releases page](https://github.com/rigetti/libquil/releases). Within the `.zip` archive you will find the library and header files that are required to use the library. Move these into your file system. +## Building from source + +Building requires an SBCL that has a *linkable runtime* — a `libsbcl.a` or +`libsbcl.so`/`libsbcl.dylib` that gets embedded into the library. Neither +`make.sh` nor most package managers produce one (Homebrew's `sbcl` bottle, for +instance, does not), so it has to be built from an SBCL source tree of the same +version as the `sbcl` used for the rest of the build: + +```bash +sh make.sh --with-sb-linkable-runtime && sh make-shared-library.sh && sh install.sh +``` + +`install.sh` places the runtime in SBCL's home directory, where the `Makefile` +finds it automatically. To use one from elsewhere, pass it explicitly: + +```bash +make LIBSBCL=/path/to/sbcl/src/runtime/libsbcl.a +``` + +Note that on arm64 macOS, SBCL only builds its runtime as the static +`libsbcl.a`; there is no shared `libsbcl.dylib` on that platform. The `Makefile` +handles either form. + +The Lisp dependencies (`quilc`, `qvm`, `magicl`, `sbcl-librarian`) are expected +in your Quicklisp local-projects directory. Then: + +```bash +make +``` + +### Linear algebra backend on arm64 macOS + +Homebrew's reference `lapack` computes incorrect eigenvectors on arm64, which +surfaces as `Could not find diagonalizer for matrix ... after 16 attempts` +during compilation. Install OpenBLAS (`brew install openblas`) and ensure +`magicl` loads it in preference to `lapack`. + # C API Reference ## Libquil functions and types diff --git a/src/build-image.lisp b/src/build-image.lisp index 95833a5..0656060 100644 --- a/src/build-image.lisp +++ b/src/build-image.lisp @@ -1,7 +1,34 @@ (require '#:asdf) -(asdf:load-system '#:sbcl-librarian) -(asdf:load-system '#:libquil) +;; libquil depends on systems that come from Quicklisp (cffi, bordeaux-threads, +;; ...). ASDF alone will not fetch those, so prefer Quicklisp when it is +;; available and fall back to plain ASDF for setups that vendor the +;; dependencies themselves. +(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))) + (when (and (null (find-package '#:quicklisp)) + (probe-file quicklisp-init)) + (load quicklisp-init))) + +(defun load-system (system) + ;; libquil intentionally redefines some of the alien callables that + ;; SBCL-LIBRARIAN generates, in order to give them types SBCL-LIBRARIAN cannot + ;; express yet (see the definition of quilc_compile_protoquil). SBCL signals a + ;; continuable error for that; taking the CONTINUE restart installs the new + ;; definition, which is what an interactive build does. Without this the build + ;; drops into the debugger and cannot run unattended. + (handler-bind ((error + (lambda (condition) + (let ((restart (find-restart 'continue condition))) + (when (and restart + (search "redefine alien callable" + (princ-to-string condition))) + (invoke-restart restart)))))) + (if (find-package '#:quicklisp) + (funcall (read-from-string "quicklisp:quickload") system) + (asdf:load-system system)))) + +(load-system '#:sbcl-librarian) +(load-system '#:libquil) (in-package #:libquil) diff --git a/src/quilc/api.lisp b/src/quilc/api.lisp index 0941969..5bbf90f 100644 --- a/src/quilc/api.lisp +++ b/src/quilc/api.lisp @@ -135,7 +135,7 @@ (("parse_quil" cl-quil.frontend:safely-parse-quil) quil-program ((source :string))) (("program_memory_type" parsed-program-get-memory-region-type) :void ((program quil-program) (region-name :string) (region-type-ptr :pointer))) (("print_program" cl-quil.frontend:print-parsed-program) :void ((program quil-program))) - (("compile_quil" cl-quil:compiler-hook) quil-program ((program quil-program) (chip-spec chip-specification))) + (("compile_quil" compile-quil) quil-program ((program quil-program) (chip-spec chip-specification))) (("compilation_metadata_len" compilation-metadata-len) :int ((metadata compilation-metadata))) (("compilation_metadata_get_final_rewiring" compilation-metadata-get-final-rewiring) :void diff --git a/src/quilc/compile.lisp b/src/quilc/compile.lisp index 76f1196..751b7de 100644 --- a/src/quilc/compile.lisp +++ b/src/quilc/compile.lisp @@ -68,6 +68,12 @@ (coerce runtime 'double-float))) present-p)) +(defun compile-quil (parsed-program chip-specification) + ;; As in COMPILE-PROTOQUIL, the LAPACK library for macOS will sometimes hit a + ;; division-by-zero. Mask those interrupts so they can be handled in Lisp. + (magicl:with-blapack + (cl-quil:compiler-hook parsed-program chip-specification))) + (defun compile-protoquil (parsed-program chip-specification metadata-ptr) (multiple-value-bind (compiled-program metadata) (magicl:with-blapack (process-program parsed-program chip-specification :protoquil t)) diff --git a/src/qvm/qvm-app-imports.lisp b/src/qvm/qvm-app-imports.lisp index c601ffb..a918f6a 100644 --- a/src/qvm/qvm-app-imports.lisp +++ b/src/qvm/qvm-app-imports.lisp @@ -12,14 +12,18 @@ ) (defun compress-program-qubits (quil) + "Remap the qubits of QUIL to a minimal sequential set from 0 to (num-qubits-used - 1). Return two values: the processed Quil code and the mapping vector. + +The mapping vector V specifies that the qubit as specified in the program V[i] has been mapped to qubit i." (let* ((quil (cl-quil:copy-instance quil)) + (mapping (cl-quil::compute-qubit-mapping quil)) (trivial-mapping-p - (loop :for x :across (cl-quil::compute-qubit-mapping quil) + (loop :for x :across mapping :for i :from 0 :always (= x i)))) (unless trivial-mapping-p (cl-quil::transform 'cl-quil::compress-qubits quil)) - quil)) + (values quil mapping))) (defun get-random-state (arg) (etypecase arg From 32e35588fbb9d0d8825649afd6d96937edb6706b Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Tue, 11 Aug 2026 15:05:43 -0600 Subject: [PATCH 3/6] fix README notes on aarch64 --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 40f79e5..2b1fd2a 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,7 @@ We provide pre-built binaries for the following systems - Linux x64 (tested specifically on Ubuntu) - macOS x64 - -Note that we do not publish pre-built binaries for ARM. ARM machines, including -Apple Silicon, are supported when building from source — see [Building from -source](#building-from-source). +- macOS aarch64 ## Requirements @@ -80,7 +77,7 @@ finds it automatically. To use one from elsewhere, pass it explicitly: make LIBSBCL=/path/to/sbcl/src/runtime/libsbcl.a ``` -Note that on arm64 macOS, SBCL only builds its runtime as the static +Note that on aarch64 macOS, SBCL only builds its runtime as the static `libsbcl.a`; there is no shared `libsbcl.dylib` on that platform. The `Makefile` handles either form. @@ -91,9 +88,9 @@ in your Quicklisp local-projects directory. Then: make ``` -### Linear algebra backend on arm64 macOS +### Linear algebra backend on aarch64 macOS -Homebrew's reference `lapack` computes incorrect eigenvectors on arm64, which +Homebrew's reference `lapack` computes incorrect eigenvectors on aarch64, which surfaces as `Could not find diagonalizer for matrix ... after 16 attempts` during compilation. Install OpenBLAS (`brew install openblas`) and ensure `magicl` loads it in preference to `lapack`. From 975064e9a5eb8b4aa4adfcdf29f34ca3b4cfb402 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Tue, 11 Aug 2026 15:06:11 -0600 Subject: [PATCH 4/6] GHA fixes --- .github/workflows/build.yml | 26 ++++++++++++++------------ .github/workflows/release.yml | 6 +++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 193d916..e38296d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,13 +13,13 @@ jobs: - name: root suid tar run: sudo chown root /bin/tar && sudo chmod u+s /bin/tar - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: path: "libquil" - name: Cache SBCL id: cache-sbcl - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | /usr/local/lib/libsbcl.so @@ -76,7 +76,7 @@ jobs: mkdir -p $GITHUB_WORKSPACE/artifacts/libquil && cp $GITHUB_WORKSPACE/libquil/libquil.h $GITHUB_WORKSPACE/libquil/libquil.so $GITHUB_WORKSPACE/libquil/libquil.core /usr/local/lib/libsbcl.so $GITHUB_WORKSPACE/artifacts/libquil - name: Store artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux-amd64 path: ${{ github.workspace }}/artifacts/* @@ -87,13 +87,13 @@ jobs: steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: path: "libquil" - name: Cache SBCL id: cache-sbcl-macos - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | /usr/local/lib/libsbcl.so @@ -101,7 +101,7 @@ jobs: /usr/local/bin/sbcl ~/quicklisp ~/.sbclrc - key: ${{ runner.os }}-build-${{ hashFiles('**/macos-versions' )}} + key: ${{ runner.os }}-${{ runner.arch }}-build-${{ hashFiles('**/macos-versions' )}} - uses: Homebrew/actions/setup-homebrew@master name: Setup homebrew @@ -157,7 +157,7 @@ jobs: mkdir -p $GITHUB_WORKSPACE/artifacts/libquil && cp $GITHUB_WORKSPACE/libquil/libquil.h $GITHUB_WORKSPACE/libquil/libquil.dylib $GITHUB_WORKSPACE/libquil/libquil.core /usr/local/lib/libsbcl.so $GITHUB_WORKSPACE/artifacts/libquil - name: Store artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: macos path: ${{ github.workspace }}/artifacts/* @@ -168,13 +168,13 @@ jobs: steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: path: "libquil" - name: Cache SBCL id: cache-sbcl-macos - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | /usr/local/lib/libsbcl.so @@ -182,7 +182,7 @@ jobs: /usr/local/bin/sbcl ~/quicklisp ~/.sbclrc - key: ${{ runner.os }}-build-${{ hashFiles('**/macos-versions' )}} + key: ${{ runner.os }}-${{ runner.arch }}-build-${{ hashFiles('**/macos-versions' )}} - uses: Homebrew/actions/setup-homebrew@master name: Setup homebrew @@ -238,8 +238,10 @@ jobs: mkdir -p $GITHUB_WORKSPACE/artifacts/libquil && cp $GITHUB_WORKSPACE/libquil/libquil.h $GITHUB_WORKSPACE/libquil/libquil.dylib $GITHUB_WORKSPACE/libquil/libquil.core /usr/local/lib/libsbcl.so $GITHUB_WORKSPACE/artifacts/libquil - name: Store artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: macos + # Must differ from the x64 macOS job artifact name: upload-artifact + # v4 rejects two artifacts sharing a name within a single run. + name: macos-arm64 path: ${{ github.workspace }}/artifacts/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1dbdde4..9c4990a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,12 +25,12 @@ jobs: git config --global user.name "${{ github.triggering_actor }}" git config --global user.email "${{ github.triggering_actor }}@users.noreply.github.com" - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Download linux artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: linux-amd64 path: artifacts/ @@ -40,7 +40,7 @@ jobs: cd artifacts && zip linux-amd64.zip libquil/libsbcl.so libquil/libquil.so libquil/libquil.core libquil/libquil.h - name: Download macos artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: macos path: artifacts/ From b659ea153c31bc799351263011810c778c700270 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Tue, 11 Aug 2026 15:12:16 -0600 Subject: [PATCH 5/6] Drop Intel macOS support Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/build.yml | 93 +++-------------------------------- .github/workflows/release.yml | 19 ++++--- README.md | 9 ++-- install.sh | 26 +++++++--- knope.toml | 2 +- 5 files changed, 45 insertions(+), 104 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e38296d..162aed4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,92 +79,13 @@ jobs: uses: actions/upload-artifact@v4 with: name: linux-amd64 - path: ${{ github.workspace }}/artifacts/* - - build-macos: - name: Build libquil.dylib - runs-on: macos-latest - - steps: - - - uses: actions/checkout@v4 - with: - path: "libquil" - - - name: Cache SBCL - id: cache-sbcl-macos - uses: actions/cache@v4 - with: - path: | - /usr/local/lib/libsbcl.so - /usr/local/lib/sbcl - /usr/local/bin/sbcl - ~/quicklisp - ~/.sbclrc - key: ${{ runner.os }}-${{ runner.arch }}-build-${{ hashFiles('**/macos-versions' )}} - - - uses: Homebrew/actions/setup-homebrew@master - name: Setup homebrew - - - if: steps.cache-sbcl-macos.outputs.cache-hit != 'true' - name: Install SBCL, libraries, and quicklisp - run: | - brew install sbcl git - git clone --branch x86-null-tn https://git.code.sf.net/p/sbcl/sbcl /tmp/sbcl - cd /tmp/sbcl && sudo sh make.sh --without-compact-instance-header --without-immobile-space --without-immobile-code && sudo sh make-shared-library.sh - brew remove -f sbcl - sudo sh install.sh - sudo cp src/runtime/libsbcl.so /usr/local/lib/libsbcl.so - - brew install wget - wget -P /tmp/ 'https://beta.quicklisp.org/quicklisp.lisp' - sbcl --noinform --non-interactive --load /tmp/quicklisp.lisp --eval "(quicklisp-quickstart:install :dist-url \"http://beta.quicklisp.org/dist/quicklisp/2022-04-01/distinfo.txt\")" - sbcl --noinform --non-interactive --load ~/quicklisp/setup.lisp --eval '(ql-util:without-prompting (ql:add-to-init-file))' - echo "#+quicklisp(push (truename \"$GITHUB_WORKSPACE\") ql:*local-project-directories*)" >> ~/.sbclrc - rm -f /tmp/quicklisp.lisp - cat ~/.sbclrc - - - name: Pull Lisp dependencies - run: | - git clone https://github.com/notmgsk/quilc.git $GITHUB_WORKSPACE/quilc && cd $GITHUB_WORKSPACE/quilc && git checkout bffea7fdb972cc9b3a50b790246b6a3143c88c7a - git clone https://github.com/quil-lang/qvm.git $GITHUB_WORKSPACE/qvm && cd $GITHUB_WORKSPACE/qvm && git checkout 4617625cb6053b1adfd3f7aea9cd2be328b225f6 - git clone https://github.com/quil-lang/magicl.git $GITHUB_WORKSPACE/magicl - git clone https://github.com/stylewarning/cl-permutation $GITHUB_WORKSPACE/cl-permutation - git clone https://github.com/quil-lang/sbcl-librarian.git $GITHUB_WORKSPACE/sbcl-librarian && cd $GITHUB_WORKSPACE/sbcl-librarian && git checkout 04f7e390c777084ac43b8df9e90593a041da2381 - - - name: Install quilc dependencies - run: brew install lapack openblas libffi gfortran - - - name: Build libquil.dylib - run: | - cd $GITHUB_WORKSPACE/libquil - ls - sbcl --noinform --non-interactive --eval '(ql:quickload :sbcl-librarian)' - sbcl --dynamic-space-size 8192 --noinform --non-interactive --eval '(ql:quickload :libquil)' - make - - - name: ls - run: | - ls $GITHUB_WORKSPACE/libquil/libquil.dylib - - - name: Test - run: | - cd $GITHUB_WORKSPACE/libquil/examples/qvm && DYLD_LIBRARY_PATH=$GITHUB_WORKSPACE/libquil make test - cd $GITHUB_WORKSPACE/libquil/examples/quilc && DYLD_LIBRARY_PATH=$GITHUB_WORKSPACE/libquil make test - - - name: Package files - run: | - mkdir -p $GITHUB_WORKSPACE/artifacts/libquil && cp $GITHUB_WORKSPACE/libquil/libquil.h $GITHUB_WORKSPACE/libquil/libquil.dylib $GITHUB_WORKSPACE/libquil/libquil.core /usr/local/lib/libsbcl.so $GITHUB_WORKSPACE/artifacts/libquil - - - name: Store artifact - uses: actions/upload-artifact@v4 - with: - name: macos - path: ${{ github.workspace }}/artifacts/* + # A directory (not a glob): upload-artifact v4 preserves structure + # relative to it, keeping the libquil/ prefix the release job unzips. + path: ${{ github.workspace }}/artifacts build-macos-arm: name: Build Apple-Silicon libquil.dylib - runs-on: macos-latest-xlarge + runs-on: macos-latest steps: @@ -240,8 +161,8 @@ jobs: - name: Store artifact uses: actions/upload-artifact@v4 with: - # Must differ from the x64 macOS job artifact name: upload-artifact - # v4 rejects two artifacts sharing a name within a single run. name: macos-arm64 - path: ${{ github.workspace }}/artifacts/* + # A directory (not a glob): upload-artifact v4 preserves structure + # relative to it, keeping the libquil/ prefix the release job unzips. + path: ${{ github.workspace }}/artifacts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c4990a..033f220 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,31 +29,34 @@ jobs: with: fetch-depth: 0 + # Each artifact is downloaded into its own directory: the two macOS + # artifacts contain identically named files, so a shared directory would + # have one overwrite the other. - name: Download linux artifacts uses: actions/download-artifact@v4 with: name: linux-amd64 - path: artifacts/ + path: artifacts/linux-amd64/ - name: Archive linux artifacts run: | - cd artifacts && zip linux-amd64.zip libquil/libsbcl.so libquil/libquil.so libquil/libquil.core libquil/libquil.h + cd artifacts/linux-amd64 && zip ../linux-amd64.zip libquil/libsbcl.so libquil/libquil.so libquil/libquil.core libquil/libquil.h - - name: Download macos artifacts + - name: Download macos arm64 artifacts uses: actions/download-artifact@v4 with: - name: macos - path: artifacts/ + name: macos-arm64 + path: artifacts/macos-arm64/ - - name: Archive linux artifacts + - name: Archive macos arm64 artifacts run: | - cd artifacts && zip macos.zip libquil/libsbcl.so libquil/libquil.dylib libquil/libquil.core libquil/libquil.h + cd artifacts/macos-arm64 && zip ../macos-arm64.zip libquil/libsbcl.so libquil/libquil.dylib libquil/libquil.core libquil/libquil.h - name: List artifacts run: | ls -R artifacts/ unzip -l artifacts/linux-amd64.zip - unzip -l artifacts/macos.zip + unzip -l artifacts/macos-arm64.zip - name: Install Knope uses: knope-dev/action@v2.0.0 diff --git a/README.md b/README.md index 2b1fd2a..c2b83e5 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,11 @@ We provide pre-built binaries for the following systems - Linux x64 (tested specifically on Ubuntu) -- macOS x64 -- macOS aarch64 +- macOS aarch64 (Apple Silicon) + +Intel macOS binaries are no longer published. Other platforms, including Intel +macOS, can be built from source — see [Building from +source](#building-from-source). ## Requirements @@ -23,7 +26,7 @@ These libraries are required by `libquil`: On systems which use `apt` to install packages (e.g. Ubuntu), these libraries can be installed with the command ``` -sudo apt install libblas-dev libffi-dev libffi7 liblapack-dev libz-dev +sudo apt install libblas-dev libffi-dev liblapack-dev libz-dev ``` On systems which use `brew` to install packages (e.g macOS), these libraries can be installed with the command diff --git a/install.sh b/install.sh index 9012dcf..d4e4061 100755 --- a/install.sh +++ b/install.sh @@ -7,10 +7,6 @@ err() { exit 1 } -if [[ "$(uname -p)" =~ "arm" ]]; then - err "Unsupported CPU architecture: $(uname -p)." -fi - if [[ -n "${1-}" ]] then LIBQUIL_URL_PREFIX="https://github.com/rigetti/libquil/releases/download/v${1}" @@ -19,13 +15,31 @@ else fi OS="$(uname)" +ARCH="$(uname -m)" if [[ "${OS}" == "Linux" ]] then IS_LINUX=1 - LIBQUIL_RELEASE_FILE="linux-amd64.zip" + case "${ARCH}" in + x86_64 | amd64) + LIBQUIL_RELEASE_FILE="linux-amd64.zip" + ;; + *) + err "Unsupported CPU architecture for Linux: ${ARCH}. Only x86_64 is supported." \ + "You can build libquil from source; see https://github.com/rigetti/libquil#building-from-source" + ;; + esac elif [[ "${OS}" == "Darwin" ]] then - LIBQUIL_RELEASE_FILE="macos.zip" + case "${ARCH}" in + arm64 | aarch64) + LIBQUIL_RELEASE_FILE="macos-arm64.zip" + ;; + *) + err "Unsupported CPU architecture for macOS: ${ARCH}. Only Apple Silicon (arm64) is supported." \ + "Intel macOS builds are no longer published. You can build libquil from source; see" \ + "https://github.com/rigetti/libquil#building-from-source" + ;; + esac else err "Unsupported operating system. Supported operating systems are Linux and macOS." fi diff --git a/knope.toml b/knope.toml index 78193a2..366aa5d 100644 --- a/knope.toml +++ b/knope.toml @@ -7,7 +7,7 @@ changelog = "CHANGELOG.md" path = "artifacts/linux-amd64.zip" [[package.assets]] -path = "artifacts/macos.zip" +path = "artifacts/macos-arm64.zip" [[workflows]] name = "release" From ff1fb635676267ec31a37d659a9d5579eb42c216 Mon Sep 17 00:00:00 2001 From: Kyle J Strand Date: Tue, 11 Aug 2026 16:32:50 -0600 Subject: [PATCH 6/6] small fixes --- .github/workflows/build.yml | 9 ++-- src/quilc/compile.lisp | 2 +- src/quilc/quilc-imports.lisp | 86 +++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 162aed4..00753bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,14 +49,17 @@ jobs: - name: Pull Lisp dependencies run: | - git clone https://github.com/notmgsk/quilc.git $GITHUB_WORKSPACE/quilc && cd $GITHUB_WORKSPACE/quilc && git checkout bffea7fdb972cc9b3a50b790246b6a3143c88c7a + git clone https://github.com/quil-lang/quilc.git $GITHUB_WORKSPACE/quilc && cd $GITHUB_WORKSPACE/quilc && git checkout 3d83b665b3addc33be2f696b1cdebc0c4c5a04f9 git clone https://github.com/quil-lang/qvm.git $GITHUB_WORKSPACE/qvm && cd $GITHUB_WORKSPACE/qvm && git checkout 4617625cb6053b1adfd3f7aea9cd2be328b225f6 git clone https://github.com/quil-lang/magicl.git $GITHUB_WORKSPACE/magicl git clone https://github.com/stylewarning/cl-permutation $GITHUB_WORKSPACE/cl-permutation git clone https://github.com/quil-lang/sbcl-librarian.git $GITHUB_WORKSPACE/sbcl-librarian && cd $GITHUB_WORKSPACE/sbcl-librarian && git checkout 04f7e390c777084ac43b8df9e90593a041da2381 - name: Install quilc dependencies - run: sudo apt update && sudo apt install -y libblas-dev libffi-dev libffi7 liblapack-dev libz-dev gfortran + # libffi-dev pulls in whichever libffi runtime the image ships; naming + # it explicitly breaks whenever the runner image moves (libffi7 does not + # exist on ubuntu 24.04, which ubuntu-latest now resolves to). + run: sudo apt update && sudo apt install -y libblas-dev libffi-dev liblapack-dev libz-dev gfortran - name: Build libquil.so run: | @@ -128,7 +131,7 @@ jobs: - name: Pull Lisp dependencies run: | - git clone https://github.com/notmgsk/quilc.git $GITHUB_WORKSPACE/quilc && cd $GITHUB_WORKSPACE/quilc && git checkout bffea7fdb972cc9b3a50b790246b6a3143c88c7a + git clone https://github.com/quil-lang/quilc.git $GITHUB_WORKSPACE/quilc && cd $GITHUB_WORKSPACE/quilc && git checkout 3d83b665b3addc33be2f696b1cdebc0c4c5a04f9 git clone https://github.com/quil-lang/qvm.git $GITHUB_WORKSPACE/qvm && cd $GITHUB_WORKSPACE/qvm && git checkout 4617625cb6053b1adfd3f7aea9cd2be328b225f6 git clone https://github.com/quil-lang/magicl.git $GITHUB_WORKSPACE/magicl git clone https://github.com/stylewarning/cl-permutation $GITHUB_WORKSPACE/cl-permutation diff --git a/src/quilc/compile.lisp b/src/quilc/compile.lisp index 751b7de..813d8ed 100644 --- a/src/quilc/compile.lisp +++ b/src/quilc/compile.lisp @@ -81,5 +81,5 @@ (let ((handle (sbcl-librarian::make-handle metadata))) (setf (sb-alien:deref metadata-ptr) handle))) - (cl-quil.frontend::transform 'cl-quil.frontend::process-protoquil compiled-program) + (cl-quil.frontend::transform 'process-protoquil compiled-program) compiled-program)) diff --git a/src/quilc/quilc-imports.lisp b/src/quilc/quilc-imports.lisp index 9095569..0aaf033 100644 --- a/src/quilc/quilc-imports.lisp +++ b/src/quilc/quilc-imports.lisp @@ -32,6 +32,90 @@ :documentation "The git hash of the quilc repo.") ;;; borrowed from quilc: app/src/entry-point.lisp +;;; +;;; PROCESS-PROTOQUIL and STRIP-FINAL-HALT-RESPECTING-REWIRINGS live in quilc's +;;; *application*, which we do not depend on, so they are vendored here rather +;;; than requiring a quilc fork that exposes them from the library. +;;; TODO(https://github.com/quil-lang/quilc/pull/933): remove these once the logic is upstreamed + +(cl-quil.frontend::define-transform process-protoquil (process-protoquil) + "Removes HALT, DEFCIRCUIT, and DEFGATE instructions.") + +(defun process-protoquil (parsed-program) + (setf (cl-quil.frontend::parsed-program-circuit-definitions parsed-program) nil + (cl-quil.frontend::parsed-program-gate-definitions parsed-program) nil) + + ;; if we're supposed to output protoQuil, we also need to + ;; strip the final HALT instructions from the output + (setf (cl-quil:parsed-program-executable-code parsed-program) + (strip-final-halt-respecting-rewirings parsed-program))) + +(defun strip-final-halt-respecting-rewirings (processed-program) + "Remove the final HALT instruction, if any, from PROCESSED-PROGRAM, retaining any attached rewiring comments." + (let* ((instructions (cl-quil:parsed-program-executable-code processed-program)) + (last-instruction (and (plusp (length instructions)) + (cl-quil::nth-instr 0 processed-program :from-end t))) + (penultimate-instruction (and (< 1 (length instructions)) + (cl-quil::nth-instr 1 processed-program :from-end t))) + (must-transfer-comment-p (and (not (null penultimate-instruction)) + (cl-quil.frontend::comment last-instruction)))) + + (unless (cl-quil::haltp last-instruction) + (return-from strip-final-halt-respecting-rewirings instructions)) + + (when must-transfer-comment-p + ;; Transfer the rewiring comment from LAST-INSTRUCTION to + ;; PENULTIMATE-INSTRUCTION. + (multiple-value-bind (last-entering last-exiting) + (cl-quil::instruction-rewirings last-instruction) + (multiple-value-bind (penultimate-entering penultimate-exiting) + (cl-quil::instruction-rewirings penultimate-instruction) + (flet ((assert-rewirings-compatible (rewiring-type last-rewiring penultimate-rewiring) + ;; This bit of hoop-jumping guards against the + ;; unlikely event that both PENULTIMATE-INSTRUCTION + ;; and LAST-INSTRUCTION have rewiring comments + ;; attached which might be incompatible. We check + ;; to ensure that either one of the rewirings is + ;; NULL, or else they are EQUALP and can safely be + ;; merged. + (assert (or (or (null last-rewiring) + (null penultimate-rewiring)) + (equalp last-rewiring penultimate-rewiring)) + () + "Failed to strip final HALT. Instructions have incompatible ~A rewirings:~@ + LAST: ~A ~A~@ + PREV: ~A ~A" + rewiring-type last-instruction last-rewiring + penultimate-instruction penultimate-rewiring))) + (assert-rewirings-compatible ':ENTERING last-entering penultimate-entering) + (assert-rewirings-compatible ':EXITING last-exiting penultimate-exiting)) + ;; Consider the following cases for the :ENTERING rewirings + ;; (the same case analysis applies to the :EXITING rewiring + ;; pair as well). + ;; + ;; 1) If both the rewirings are non-NIL, then the + ;; ASSERT-REWIRINGS-COMPATIBLE check above guarantees + ;; that they are EQUALP, and it doesn't matter which one + ;; we select. + ;; + ;; 2) If only one is non-NIL, the OR selects it. + ;; + ;; 3) If both are NIL, then MAKE-REWIRING-COMMENT just + ;; ignores that keyword argument, and returns an :EXITING + ;; rewiring. + ;; + ;; Finally, (COMMENT LAST-INSTRUCTION) is non-NIL (otherwise + ;; MUST-TRANSFER-COMMENT-P would be NIL), so at least one of + ;; LAST-ENTERING and LAST-EXITING is non-NIL, which means + ;; that at least one of the :ENTERING and :EXITING keyword + ;; args to MAKE-REWIRING-COMMENT is non-NIL and hence the + ;; call will produce a rewiring comment. + (setf (cl-quil.frontend::comment penultimate-instruction) + (cl-quil::make-rewiring-comment :entering (or last-entering penultimate-entering) + :exiting (or last-exiting penultimate-exiting)))))) + + ;; Strip the final HALT instruction. + (subseq instructions 0 (1- (length instructions))))) (defun process-program (program chip-specification &key @@ -63,7 +147,7 @@ Returns a values tuple (PROCESSED-PROGRAM, STATISTICS), where PROCESSED-PROGRAM (cl-quil:compiler-hook program chip-specification :protoquil protoquil :destructive t) (when protoquil - (cl-quil.frontend::transform 'cl-quil::process-protoquil processed-program) + (cl-quil.frontend::transform 'process-protoquil processed-program) ;; Compute statistics for protoquil program (compute-statistics processed-program chip-specification statistics :gate-whitelist gate-whitelist :gate-blacklist gate-blacklist)