{"id":18565263,"url":"https://github.com/rigetti/qcs-sdk-qir","last_synced_at":"2025-04-10T04:32:38.575Z","repository":{"id":37789454,"uuid":"432815026","full_name":"rigetti/qcs-sdk-qir","owner":"rigetti","description":"Compile \u0026 Run Quantum Intermediate Representation (QIR) Programs on Rigetti Quantum Cloud Services (QCS)","archived":false,"fork":false,"pushed_at":"2024-12-21T01:44:23.000Z","size":291,"stargazers_count":10,"open_issues_count":13,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-24T16:11:19.513Z","etag":null,"topics":["llvm","qir","quantum-computing"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rigetti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-11-28T20:15:41.000Z","updated_at":"2024-12-26T17:25:06.000Z","dependencies_parsed_at":"2022-06-23T21:10:02.533Z","dependency_job_id":null,"html_url":"https://github.com/rigetti/qcs-sdk-qir","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rigetti%2Fqcs-sdk-qir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rigetti%2Fqcs-sdk-qir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rigetti%2Fqcs-sdk-qir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rigetti%2Fqcs-sdk-qir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rigetti","download_url":"https://codeload.github.com/rigetti/qcs-sdk-qir/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248157635,"owners_count":21057045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["llvm","qir","quantum-computing"],"created_at":"2024-11-06T22:18:08.294Z","updated_at":"2025-04-10T04:32:38.143Z","avatar_url":"https://github.com/rigetti.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# QCS QIR SDK\n\nCompile \u0026 run Quantum Intermediate Representation (QIR) programs on Rigetti QCS.\n\n## Usage \n\nThe easiest way to start is by downloading the [latest release](https://github.com/rigetti/qcs-sdk-qir/releases/latest).\n\nWithin the release, you will find a `README.md` pertaining to the specific version you've downloaded,\nbased on [this template](./scripts/release/README.release.md). Those instructions cover the basics\nto compile an executable that can run quantum programs on Rigetti QPUs or the QVM.\n\n## Overview\n\n```mermaid\ngraph TD\n    A[Take QIR Input 'input.bc'] --\u003e B[Determine format: 'shot-count' OR 'unitary'] --\u003e C[Choose Output Type]\n    C --\u003e|Binary Executable?| D[transform]\n    C --\u003e|Quil?| E[transpile-to-quil]\n    D --\u003e|generates: Rigetti-targeted 'output.bc'| F[clang w/ QCS SDK + libhelper] --\u003eG[./program]\n    E --\u003e|generates: shot-count, Quil program string, output recording map| H[submit job to QCS API]\n```\n\n## Development\n\nIn order to build this crate, a supported LLVM version must be installed and available on your `PATH` \n(you must be able to run `llvm-config`). The supported versions are listed in `Cargo.toml` under \n`[features]`.\n\nTo build the CLI: \n```sh\ncargo build --bin qcs-sdk-qir --features llvm13-0\n```\n\nTo run the unit and snapshot tests: \n```sh\ncargo test --features llvm13-0\n```\n\nTo test your changes alongside the shared libraries, it might be helpful to reuse the release \nscripts and test a fully integrated toolset. To do so, it's recommended to use `cargo-make`. Install\nit by running:\n```sh\ncargo install cargo-make\n```\n\nUse the following task and configurations to build the SDK with your changes:\n```sh\ncargo make release-quick # skips the Rust unit and snapshot tests \u0026 and other checks\n```\n\nAfter this, you can skip building various components if a new build is not necessary: \n```sh\n# any or all of the following env vars can be used:\nNO_C_SDK_BUILD=1 NO_QIR_SDK_BUILD=1 NO_HELPER_LIB_BUILD=1 cargo make release-quick\n\n# NO_C_SDK_BUILD: skips the git clone \u0026 build of the `qcs-sdk-c` library (default: 0)\n# NO_QIR_SDK_BUILD: skips the build of this repo (default: 0)\n# NO_HELPER_LIB_BUILD: skips the build of the helper lib (default: 0)\n```\n\nTo reset your environment and clean up release build artifacts, run:\n```sh\ncargo make release-clean\n```\n\n## Testing\n\nRefer to the included `README.md` in your own local release build, which will help you test your\nchanges if you are looking to transform and compile QIR programs. \n\nFor a full end-to-end integration test against both `qvm` and QPUs, you can use:\n\n```sh\ncargo make release-test-e2e \u003cTARGET\u003e \u003cPROGRAM\u003e\n# TARGET = one of: qvm, Aspen-11\n# PROGRAM = path to an LLVM bitcode (.bc) file to transform and compile into executable format\n```\n\n```sh\ncargo make release-test-e2e\n# defaults to:\n# cargo make release-test-e2e qvm tests/fixtures/programs/reduction.bc\n# use any of {qvm,Aspen-11}, assuming you can access the QPUs.\n\n# using the default test program\ncargo make release-test-e2e Aspen-11\n\n# using a different test program (note: target must be provided, args are positional)\ncargo make release-test-e2e Aspen-11 ./input.bc \n```\n\n## Examples\n\nGiven an input QIR program that might look like this:\n\n```llvm\n%Qubit = type opaque\n%Result = type opaque\n\ndeclare void @__quantum__qis__h__body(%Qubit*) local_unnamed_addr\ndeclare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\ndeclare i1 @__quantum__qis__read_result__body(%Result*) local_unnamed_addr\n\n; simple function which measures a single qubit and that's it.\ndefine internal fastcc void @QuantumApplication__Run__body() unnamed_addr {\n\nentry:\n    br label %body\n\nbody:\n    ; shot count variable\n    %0 = phi i64 [ %2, %body ], [ 1, %entry ]\n\n    ; measure a given qubit index\n    tail call void @__quantum__qis__mz__body(%Qubit* nonnull inttoptr (i64 1 to %Qubit*), %Result* null)\n    %1 = tail call i1 @__quantum__qis__read_result__body(%Result* null)\n\n    ; shot count branch\n    %2 = add nuw nsw i64 %0, 1\n    %3 = icmp ult i64 %0, 42\n    br i1 %3, label %body, label %exit\n\nexit:\n    ret void\n}\n```\n\nThis library will:\n\n- Read and parse the LLVM bitcode\n- Recurse through all functions called from the entrypoint. Within each function body:\n  - Identify a shot-count loop with quantum instructions within any basic blocks traversed\n  - Transform those basic blocks to instead send an equivalent Quil program for execution using the [QCS C SDK](https://github.com/rigetti/qcs-sdk-c).\n- Output the resulting LLVM IR, which - if the input program matched the assumptions made by this library - no longer contains calls to QIR intrinsics and thus may be compiled for any supported architecture using QIR-unaware compilers such as `gcc` and `clang`.\n\nAfter this process is complete, the above snippet might look like this (once disassembled):\n\n```llvm\n; ModuleID = 'program.bc'\nsource_filename = \"./test/fixtures/programs/measure.ll\"\n\n%Qubit = type opaque\n%Result = type opaque\n%Executable = type opaque\n%ExecutionResult = type opaque\n\n@parameter_memory_region_name = private unnamed_addr constant [12 x i8] c\"__qir_param\\00\", align 1\n@quantum_processor_id = private unnamed_addr constant [9 x i8] c\"Aspen-10\\00\", align 1\n@quil_program = private unnamed_addr constant [35 x i8] c\"DECLARE ro BIT[0]\\0AMEASURE 1 ro[0]\\0A\\00\", align 1\n\ndeclare void @__quantum__qis__h__body(%Qubit*) local_unnamed_addr\n\ndeclare void @__quantum__qis__mz__body(%Qubit*, %Result*) local_unnamed_addr\n\ndeclare i1 @__quantum__qis__read_result__body(%Result*) local_unnamed_addr\n\ndefine internal fastcc void @QuantumApplication__Run__body() unnamed_addr {\nentry:\n  br label %body_execution\n\nbody:                                             ; preds = %body_execution, %body\n  %0 = phi i64 [ %2, %body ], [ 1, %body_execution ]\n  %1 = call i1 @get_readout_bit(%ExecutionResult* %5, i64 %0, i64 0)\n  %2 = add nuw nsw i64 %0, 1\n  %3 = icmp ult i64 %0, 42\n  br i1 %3, label %body, label %exit\n\nbody_execution:                                   ; preds = %entry\n  %4 = call %Executable* @executable_from_quil(i8* getelementptr inbounds ([35 x i8], [35 x i8]* @quil_program, i32 0, i32 0))\n  call void @wrap_in_shots(%Executable* %4, i32 42)\n  %5 = call %ExecutionResult* @execute_on_qpu(%Executable* %4, i8* getelementptr inbounds ([9 x i8], [9 x i8]* @quantum_processor_id, i32 0, i32 0))\n  call void @panic_on_failure(%ExecutionResult* %5)\n  br label %body\n\nexit:                                             ; preds = %body\n  ret void\n}\n\ndeclare %Executable* @executable_from_quil(i8*)\n\ndeclare %ExecutionResult* @execute_on_qpu(%Executable*, i8*)\n\ndeclare i1 @get_readout_bit(%ExecutionResult*, i64, i64)\n\ndeclare void @panic_on_failure(%ExecutionResult*)\n\ndeclare void @set_param(%Executable*, i8*, i32, double)\n\ndeclare void @wrap_in_shots(%Executable*, i32)\n\ndefine i32 @main() {\nentry:\n  call void @QuantumApplication__Run__body()\n  ret i32 0\n}\n```\n\n---\n\n_Read the following for a more detailed run-down on usage / development:_\n\n## Extra Dependencies for [Full QIR Conversion](#transform-qir)\n\n\u003e These are not required if only [transforming unitary QIR to Quil](#transpile-qir-to-quil).\n\n* A C compiler, such as `gcc` or `clang`, which supports LLVM 11 at a minimum. For OSX users, this \nmeans XCode version \u003e= 12.5.\n* The QCS SDK shared library, which may be built or downloaded as described [here](https://github.com/rigetti/qcs-sdk-c). \n\nYou'll also need to compile the shared \"helper\" library contained in the `helper` directory. This \nsmall shared library is used to reduce the complexity required within this crate's LLVM \ntransformations.\n\n```sh\ncd helper\n./build.sh\n```\n\nOnce that's compiled, make sure to set the relevant environment variables to point to it, within the \nterminal where you'll be transpiling and running your QIR programs:\n\n```sh\n# Linux\nexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/qcs-sdk-qir/helper\n\n# OSX\nexport DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/path/to/qcs-sdk-qir/helper\n\n# Windows? (To be verified)\nexport PATH=$PATH:/path/to/qcs-sdk-qir/helper\n```\n\n## Transform QIR\n\nTo transpile an input QIR program, run the CLI. Note that you **must specify your LLVM version \ncorresponding with your installed version or this will fail with dozens of errors**:\n\n```\ncargo run --features llvm13-0 transform path/to/input.bc path/to/output.bc --add-main-entrypoint\n```\n\nUse the `--help` flag to view all options, such as whether to target the QVM or a QPU:\n\n```\ncargo run --features llvm13-0 transform --help\n```\n\n### QIR Preconditions\n\nQIR may only be transpiled in this way if:\n\n- An entrypoint function exists (TBD)\n- Every function within the call tree from the entrypoint:\n  - Contains at least one basic block\n- All basic blocks within transpiled functions which contain at least one QIR intrinsic must satisfy the *basic block preconditions*:\n  - Begin with a `phi` instruction; the operand specifying the current block must reference a variable which we will call the _shot count variable_; the other operand must reference a constant value of 1. This initializes the shot count variable.\n  - At least one QIR intrinsic, and any number of classical instructions. The return value of any of these classical instructions may not be used as an operand to any QIR intrinsic invocation.\n  - A shot count termination sequence as the final three instructions of the block:\n    1. An increment: `add` of constant `1` to the _shot count variable_.\n    2. A comparison between the _shot count variable_ and a constant, which we will refer to as the _shot count_.\n    3. A conditional branch instruction, which targets the basic block if the comparison fails and a different block if it succeeds.\n- Basic blocks which do not match the pattern described here are passed through unchanged. If that leaves QIR intrinsics unaltered, then further compilation by a classical toolchain (ie `gcc`) will likely fail.\n\n## Run Your Transformed QIR\n\nNow that you've got an output `.bc` file, you can inspect it by disassembling it (optional): `llvm-dis output.bc`, assuming that's what you named your output file in the CLI command above.\n\nNext, compile it by linking to the two shared libraries in use: the helper library (here in this repo) and the QCS SDK (installed as part of [Setup](#setup)). Here's an example using `gcc`:\n\n```sh\ngcc -Lpath/to/qcs-sdk-c-shared-lib  -lqcs -L./helper -lhelper output.bc -o program\n```\n\nThat produces an executable `program` which you can then run to execute your program on QCS. Happy computing!\n\n## Transpile QIR to Quil\n\nTo transpile an input QIR program to Quil, run the CLI as shown here, following the LLVM-related instructions above. Note that this command only works for \"simple\" QIR modules which satisfy the following:\n\n- All quantum instructions are contained within a single basic block, labeled `body`, within the entrypoint function.\n- That function itself makes no function calls within the `body` block.\n- The `body` basic block satisfies the _basic block preconditions_ described above in [QIR Preconditions](#qir-preconditions).\n\n```\ncargo run --features llvm13-0 transpile-to-quil path/to/input.bc\n```\n\nThis will write the Quil program and shot count to `stdout`.\n\n## Troubleshooting\n\n### Logging\n\nRun the CLI with `RUST_LOG` set in order to view logs emitted during the transformation process. Values include the verbose `debug` as well as `info`, `warning`, and `error`.\n\n### Rust compilation error: \"No suitable version of LLVM...\"\n\nExample:\n\n```\n   Compiling llvm-sys v110.0.2\nerror: No suitable version of LLVM was found system-wide or pointed\n       to by LLVM_SYS_110_PREFIX.\n\n       Consider using `llvmenv` to compile an appropriate copy of LLVM, and\n       refer to the llvm-sys documentation for more information.\n\n       llvm-sys: https://crates.io/crates/llvm-sys\n       llvmenv: https://crates.io/crates/llvmenv\n   --\u003e .../.cargo/registry/src/github.com-1ecc6299db9ec823/llvm-sys-110.0.2/src/lib.rs:486:1\n    |\n486 | / std::compile_error!(concat!(\n487 | |     \"No suitable version of LLVM was found system-wide or pointed\n488 | |        to by LLVM_SYS_\",\n489 | |     env!(\"CARGO_PKG_VERSION_MAJOR\"),\n...   |\n496 | |        llvmenv: https://crates.io/crates/llvmenv\"\n497 | | ));\n    | |___^\n\n```\n\nFirst, make sure you do in fact have the same LLVM version installed and on your `PATH` as you've \nspecified with the `--features` option in `cargo`. Run `llvm-config --version` to confirm. If not, \nthat needs fixing first.\n\nIf you do, perhaps you first tried to build the crate before LLVM was installed and configured. Run \n`cargo clean -p llvm-sys` to clear the build and then retry.\n\nYou may also want to try setting the `LLVM_SYS_\u003cversion\u003e_PREFIX` environment variable to point to \nthe LLVM installation you want to use. For example, if you've installed LLVM 13 via Homebrew on \nmacOS, try `export LLVM_SYS_130_PREFIX=/usr/local/opt/llvm`.\n\n### gcc compilation error: ld: library not found for -lhelper\n\n```\n$ gcc -L../qcs-sdk-c  -lqcs -L./helper -lhelper program.bc -o program\nld: library not found for -lhelper\n```\n\nDid you build the helper library in `./helper`? See [Setup](#setup).\n\n### Runtime error: Library not loaded, image not found\n\nOn OSX, it might look like this:\n\n```\n$ ./program\ndyld: Library not loaded: libhelper.dylib\n  Referenced from: .../qcs-qir-sdk/./program\n  Reason: image not found\n```\n\nThe fix: set your `LD_LIBRARY_PATH` / `DYLD_LIBRARY_PATH` / `PATH` to include the relevant directory as described above in [Setup](#setup).\n\n### Runtime error: program hangs for 30 seconds on start\n\nMake sure you're running the dependencies specified by the QCS SDK, namely `quilc`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frigetti%2Fqcs-sdk-qir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frigetti%2Fqcs-sdk-qir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frigetti%2Fqcs-sdk-qir/lists"}