diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5f0a139..f6e5fb2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @jfriel-oqc @keriksson-rosenqvist @owen-oqc @hamidelmaazouz @chemix-lunacy \ No newline at end of file +* @jfriel-oqc @keriksson-rosenqvist @owen-oqc @hamidelmaazouz @chemix-lunacy @bgsach \ No newline at end of file diff --git a/.github/actions/install-llvm/action.yml b/.github/actions/install-llvm/action.yml index aa8bf7c..6e36553 100644 --- a/.github/actions/install-llvm/action.yml +++ b/.github/actions/install-llvm/action.yml @@ -8,12 +8,6 @@ inputs: os: description: "The OS being built upon." required: true - directory: - description: "The directory to install LLVM binaries to." - required: true - target: - description: "The build script target." - required: true runs: using: composite @@ -22,9 +16,8 @@ runs: id: cache-llvm uses: actions/cache@v4 with: - path: ${{ inputs.directory }} - key: llvm-${{ inputs.version }}-${{ inputs.arch }}-${{ inputs.os }}-${{ inputs.target }} - restore-keys: llvm-${{ inputs.version }}-${{ inputs.arch }}-${{ inputs.os }}-${{ inputs.target }} + key: llvm-${{ inputs.version }}-${{ inputs.os }} + path: ${{ github.workspace }}/src/target/llvm${{ inputs.version }}-0 - name: Linux - Install build dependencies, ninja run: sudo apt-get install -y ninja-build @@ -48,8 +41,8 @@ runs: - name: Configure LLVM Environment run: | - Write-Output "RSQL_CACHE_DIR=${{ inputs.directory }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - Write-Output "LLVM_SYS_${{ inputs.version }}0_PREFIX=${{ inputs.directory }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "RSQL_CACHE_DIR=${{ github.workspace }}/src/target/llvm${{ inputs.version }}-0" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + Write-Output "LLVM_SYS_${{ inputs.version }}0_PREFIX=${{ github.workspace }}/src/target/llvm${{ inputs.version }}-0" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append Write-Output "RSQL_LLVM_FEATURE_VERSION=llvm${{ inputs.version }}-0" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append shell: pwsh if: ${{ steps.cache-llvm.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 333e365..3fab5a7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,21 +15,33 @@ run-name: Build from ${{ github.ref }} jobs: unit-tests: name: Unit Tests - runs-on: [ubuntu-latest] + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + py_version: [ "3.10", "3.11", "3.12" ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - name: Setup rust toolchain + uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.75.0 + components: rustfmt clippy - - name: Set up Python 3.10 + - name: Set up Python ${{ matrix.py_version }} uses: actions/setup-python@v3 with: - python-version: "3.10" + python-version: ${{ matrix.py_version }} + + - name: Install LLVM + uses: ./.github/actions/install-llvm + with: + os: "ubuntu-20.04" + version: "15" - name: Build run: | - sudo apt update - sudo apt install -y build-essential libffi-dev xz-utils powershell curl wget gnupg apt-transport-https - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - export PATH="/root/.cargo/bin:${PATH}" - export PATH="/root/.local/bin:$PATH" - pwsh ./build.ps1 \ No newline at end of file + pwsh ./build.ps1 + shell: pwsh \ No newline at end of file diff --git a/.github/workflows/cache-refresh.yaml b/.github/workflows/cache-refresh.yaml new file mode 100644 index 0000000..c13381c --- /dev/null +++ b/.github/workflows/cache-refresh.yaml @@ -0,0 +1,36 @@ +name: Cache refresh + +on: + schedule: + - cron: "0 0 1,6,11,16,21,26,31 * *" # Every 5 days, give or take. + workflow_dispatch: + +permissions: + contents: write + actions: write + +jobs: + refresh: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ "ubuntu-20.04", "windows-2019", "macos-14", "macos-latest" ] + + steps: + - uses: actions/checkout@v4 + - name: Setup rust toolchain + uses: ./.github/actions/rust-toolchain + with: + toolchain: 1.75.0 + components: rustfmt clippy + - name: Set up Python 3.9 + uses: actions/setup-python@v3 + with: + python-version: 3.9 + if: ${{ matrix.os != 'macos-14' && matrix.os != 'macos-latest' }} + - name: Install LLVM + uses: ./.github/actions/install-llvm + with: + version: "15" + os: ${{ matrix.os }} \ No newline at end of file diff --git a/.github/workflows/deploy-wheels.yml b/.github/workflows/deploy-wheels.yml index 85a9748..85de04e 100644 --- a/.github/workflows/deploy-wheels.yml +++ b/.github/workflows/deploy-wheels.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - os: ["ubuntu-20.04", "windows-2019", "macos-14", "macos-latest"] + os: ["ubuntu-20.04", "windows-2019", "macos-14"] steps: - uses: actions/checkout@v4 - name: Setup rust toolchain @@ -22,19 +22,18 @@ jobs: uses: actions/setup-python@v3 with: python-version: "3.10" - if: ${{ matrix.os != 'macos-14' && matrix.os != 'macos-latest' }} + if: ${{ matrix.os != 'macos-14' }} - name: Install LLVM uses: ./.github/actions/install-llvm with: - version: "14" + version: "15" os: ${{ matrix.os }} - directory: ${{ github.workspace }}/target/llvm - target: "pypi-build" - - name: "Run build script" + - name: Build run: | pwd ./build.ps1 -t "pypi-build" shell: pwsh + - name: Artifacts - manylinux uses: actions/upload-artifact@v4 with: diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..c43a74d --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,24 @@ +cff-version: 1.2.0 +title: Rasqal +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: John + family-names: Dumbell + email: john.dumbell42@gmail.com + affiliation: Oxford Quantum Circuits (OQC) +repository-code: "https://github.com/oqc-community/rasqal" +url: "https://github.com/oqc-community/rasqal" +abstract: >- + Rasqal is a quantum-classical hybrid runtime that leans on + dynamic execution of hybrid IRs to optimize and synthesize + the circuits it sends to the QPU while executing the + classical parts itself. This approach means all + non-quantum variables are known at runtime, including + results from embedded quantum executions. Such information + is then used to enhance classical execution paths and + optimize circuit synthesis. +license: BSD-3-Clause +license-url: "https://github.com/oqc-community/rasqal/blob/develop/LICENSE" diff --git a/README.md b/README.md index e8c1252..b6579aa 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Some of the key things this approach enables: 2. Enabling hybrid algorithms to be run on machines and tools with only a gate-level API available. This includes QASM API's if you use its simulation framework. 3. Lots of optimization potential when passed large amounts of classical context that a quantum algorithm uses to accentuate its own execution. -We also have a [full feature list and quick intro to its concepts](https://github.com/oqc-community/rasqal/blob/develop/features_and_concepts.md) as well as a [draft paper](https://github.com/oqc-community/rasqal/blob/develop/docs/Rasqal%20Draft%20v2.pdf) that covers its internals in excruciating detail. +We also have a [full feature list and quick intro to its concepts](https://github.com/oqc-community/rasqal/blob/develop/features_and_concepts.md) as well as a [draft paper](https://github.com/oqc-community/rasqal/blob/develop/docs/papers/Rasqal%20Draft%20v2.pdf) that covers its internals in excruciating detail. If you have any features or ideas you'd like to see implemented feel free to raise a [feature request](https://github.com/oqc-community/Rasqal/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.md&title=)! @@ -19,7 +19,7 @@ If you have any features or ideas you'd like to see implemented feel free to rai 1. Install Rasqal in your favourite Python venv by running `pip install rasqal`. Our current testing is done with `v3.10` of Python. 2. Read the [quick start](https://github.com/oqc-community/rasqal/blob/develop/examples.md) and look at our [Python example](https://github.com/oqc-community/Rasqal/blob/develop/docs/examples.py). -3. (Optional) Read the [paper](https://github.com/oqc-community/rasqal/blob/develop/docs/Rasqal%20Draft%20v2.pdf) for a deep-dive into Rasqals concepts and data structures. +3. (Optional) Read the [paper](https://github.com/oqc-community/rasqal/blob/develop/docs/papers/Rasqal%20Draft%20v2.pdf) for a deep-dive into Rasqals concepts and data structures. ### Contributing diff --git a/building.md b/building.md index 1803091..eae51f6 100644 --- a/building.md +++ b/building.md @@ -26,7 +26,7 @@ When these tools have been downloaded you run `build.ps1` at `/src/build.ps1`. T From this point you can build the Rust project with cargo and deal with it seperately. But if you need to redeploy the wheel and test things from Python you need to run the build script again. -If you have issues you can look at the [CI cross-OS build script](https://github.com/oqc-community/rasqal/blob/develop/.github/workflows/deploy-wheels.yaml) and see what might be missing or out of date from the documentation. +If you have issues you can look at the [CI cross-OS build script](https://github.com/oqc-community/rasqal/blob/develop/.github/workflows/deploy-wheels.yml) and see what might be missing or out of date from the documentation. #### Building LLVM from source diff --git a/docs/papers/README.md b/docs/papers/README.md new file mode 100644 index 0000000..d6c32d7 --- /dev/null +++ b/docs/papers/README.md @@ -0,0 +1,9 @@ +## Papers + +This folder containers various papers that cover the different concepts that are on show in Rasqal and can be used for full reference to its ideas. + +This is a quick summary of what each contains. + +__Rasqal (Draft).__ + +Initial paper explaining foundational concepts and data structures. Covers initial implementation of the graph which powers the systems various functionality as well as the mini-runtime and deferred execution mechanisms. \ No newline at end of file diff --git a/docs/Rasqal Draft v2.pdf b/docs/papers/Rasqal Draft v2.pdf similarity index 100% rename from docs/Rasqal Draft v2.pdf rename to docs/papers/Rasqal Draft v2.pdf diff --git a/features_and_concepts.md b/features_and_concepts.md index 414906f..f019af6 100644 --- a/features_and_concepts.md +++ b/features_and_concepts.md @@ -36,7 +36,7 @@ In fact the less the execution run looks like the incoming QIR the better, becau -If you are interested in a more thorough breakdown of its internals and concepts it has [a paper](https://github.com/oqc-community/rasqal/blob/develop/docs/Rasqal%20Draft%20v2.pdf) which goes into them in detail. +If you are interested in a more thorough breakdown of its internals and concepts it has [a paper](https://github.com/oqc-community/rasqal/blob/develop/docs/papers/Rasqal%20Draft%20v2.pdf) which goes into them in detail. ### Features diff --git a/src/rustfmt.toml b/src/.rustfmt.toml similarity index 100% rename from src/rustfmt.toml rename to src/.rustfmt.toml diff --git a/src/build-llvm/Cargo.toml b/src/build-llvm/Cargo.toml index 89373e0..13e3cf8 100644 --- a/src/build-llvm/Cargo.toml +++ b/src/build-llvm/Cargo.toml @@ -14,7 +14,7 @@ rust-version = "1.64" bitvec = "1.0" const-str = "0.5" lazy_static = "1.4" -llvm-sys-140 = { package = "llvm-sys", version = "140.0", optional = true } +llvm-sys-150 = { package = "llvm-sys", version = "150.0", optional = true } log = "0.4" mut_static = "5.0" @@ -28,22 +28,18 @@ cc = "1.0" lazy_static = "1.4" [features] -llvm14-0 = ["llvm-sys-140"] +llvm15-0 = ["llvm-sys-150"] # default to use llvm-sys for llvm linking -default = ["external-llvm-linking", "llvm14-0"] +default = ["external-llvm-linking", "llvm15-0"] external-llvm-linking = [] # disable linking for local installation or packaging # no-llvm-linking is marker used in the cfg checks -llvm14-0-no-llvm-linking = ["llvm14-0", "no-llvm-linking", "llvm-sys-140/disable-alltargets-init", "llvm-sys-140/no-llvm-linking"] +llvm15-0-no-llvm-linking = ["llvm15-0", "no-llvm-linking", "llvm-sys-150/disable-alltargets-init", "llvm-sys-150/no-llvm-linking"] no-llvm-linking = [] -# let us do the llvm linking -# internal-llvm-linking is marker used in the cfg checks -llvm14-0-rsql-llvm-linking = ["llvm14-0", "internal-llvm-linking", "llvm-sys-140/disable-alltargets-init", "llvm-sys-140/no-llvm-linking"] - internal-llvm-linking = [] download-llvm = [] build-llvm = [] diff --git a/src/build-llvm/build.rs b/src/build-llvm/build.rs index bc704b0..b4e30fc 100644 --- a/src/build-llvm/build.rs +++ b/src/build-llvm/build.rs @@ -17,78 +17,6 @@ extern crate cc; #[macro_use] extern crate lazy_static; -// Make sure one version of llvm features is used -#[cfg(all( - not(any(feature = "llvm11-0")), - not(any(feature = "llvm12-0")), - not(any(feature = "llvm13-0")), - not(any(feature = "llvm14-0")), -))] -compile_error!( - "One of the features `llvm11-0`, `llvm12-0`, `llvm13-0`, and `llvm14-0` must be used exclusive." -); - -// Make sure only one llvm option is used. -#[cfg(any( - all( - feature = "llvm11-0", - any(feature = "llvm12-0", feature = "llvm13-0", feature = "llvm14-0") - ), - all( - feature = "llvm12-0", - any(feature = "llvm11-0", feature = "llvm13-0", feature = "llvm14-0") - ), - all( - feature = "llvm13-0", - any(feature = "llvm11-0", feature = "llvm12-0", feature = "llvm14-0") - ), - all( - feature = "llvm14-0", - any(feature = "llvm11-0", feature = "llvm12-0", feature = "llvm13-0") - ), -))] -compile_error!( - "Features `llvm11-0`, `llvm12-0`, `llvm13-0`, and `llvm14-0` must be used exclusive." -); - -// Make sure one of the linking features is used -#[cfg(all( - not(any(feature = "rsql-llvm-linking")), - not(any(feature = "external-llvm-linking")), - not(any(feature = "no-llvm-linking")), -))] -compile_error!("One of the features `rsql/rsql-llvm-linking`, `rsql/external-llvm-linking`, and `rsql/no-llvm-linking` must be used exclusive."); - -// Make sure only one linking option is used. -#[cfg(any( - all( - feature = "rsql-llvm-linking", - any(feature = "external-llvm-linking", feature = "no-llvm-linking") - ), - all( - feature = "external-llvm-linking", - any(feature = "rsql-llvm-linking", feature = "no-llvm-linking") - ), - all( - feature = "no-llvm-linking", - any(feature = "rsql-llvm-linking", feature = "external-llvm-linking") - ), -))] -compile_error!("Features `rsql/rsql-llvm-linking`, `rsql/external-llvm-linking`, and `rsql/no-llvm-linking` are mutually exclusive."); - -// if we are building or downloading, we cannot be externally linking -#[cfg(any( - all( - feature = "build-llvm", - any(feature = "download-llvm", feature = "external-llvm-linking") - ), - all( - feature = "download-llvm", - any(feature = "build-llvm", feature = "external-llvm-linking") - ), -))] -compile_error!("Features `rsql/build-llvm` and `rsql/download-llvm` are mutually exclusive."); - fn main() -> Result<(), Box> { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=config.cmake"); @@ -278,6 +206,8 @@ fn get_llvm_tag() -> String { "llvmorg-13.0.1".to_owned() // 75e33f7 } else if cfg!(feature = "llvm14-0") { "llvmorg-14.0.6".to_owned() // 28c006 + } else if cfg!(feature = "llvm15-0") { + "llvmorg-15.0.7".to_owned() } else { panic!("Unsupported LLVM version. The LLVM feature flags or RSQL_LLVM_TAG must be set.") } @@ -325,6 +255,8 @@ fn locate_llvm_config() -> Option { "13" } else if cfg!(feature = "llvm14-0") { "14" + } else if cfg!(feature = "llvm15-0") { + "15" } else { "unknown" }; diff --git a/src/build-llvm/zipped/llvm14-0.7z b/src/build-llvm/zipped/llvm14-0.7z deleted file mode 100644 index ecbca43..0000000 Binary files a/src/build-llvm/zipped/llvm14-0.7z and /dev/null differ diff --git a/src/rasqal/.ruff.toml b/src/rasqal/.ruff.toml new file mode 100644 index 0000000..8eddfcd --- /dev/null +++ b/src/rasqal/.ruff.toml @@ -0,0 +1,14 @@ +fix-only = true + +[format] +skip-magic-trailing-comma = true + +[lint.flake8-unused-arguments] +ignore-variadic-names = true + +[lint.flake8-annotations] +allow-star-arg-any = true +suppress-dummy-args = true + +[lint.flake8-comprehensions] +allow-dict-calls-with-keyword-arguments = true diff --git a/src/rasqal/Cargo.toml b/src/rasqal/Cargo.toml index 715602a..b1632e3 100644 --- a/src/rasqal/Cargo.toml +++ b/src/rasqal/Cargo.toml @@ -12,14 +12,20 @@ repository = "" git = "https://github.com/TheDan64/inkwell" branch = "master" default-features = false -features = ["llvm14-0"] +features = ["llvm15-0"] + +[dependencies.pyo3] +version = "0.17" +# pyo3 has problems with running cargo test and extension modules. Group them here so that we can disable 'default' features when running cargo test. +# Add all features like normal as enabled features unless they mess with cargo test, which then means they go as a default. +features = ["num-complex"] +default = ["abi3-py37", "extension-module"] [dependencies] either = "1.8" libc = "0.2" const-str = "0.5" -pyo3 = { version = "0.17", features = ["abi3-py37", "extension-module", "num-complex"] } -llvm-sys = "140" +llvm-sys = "150" regex = "1.7.1" log = "0.4.17" env_logger = "0.9.3" diff --git a/src/rasqal/README.md b/src/rasqal/README.md index e8c1252..b6579aa 100644 --- a/src/rasqal/README.md +++ b/src/rasqal/README.md @@ -9,7 +9,7 @@ Some of the key things this approach enables: 2. Enabling hybrid algorithms to be run on machines and tools with only a gate-level API available. This includes QASM API's if you use its simulation framework. 3. Lots of optimization potential when passed large amounts of classical context that a quantum algorithm uses to accentuate its own execution. -We also have a [full feature list and quick intro to its concepts](https://github.com/oqc-community/rasqal/blob/develop/features_and_concepts.md) as well as a [draft paper](https://github.com/oqc-community/rasqal/blob/develop/docs/Rasqal%20Draft%20v2.pdf) that covers its internals in excruciating detail. +We also have a [full feature list and quick intro to its concepts](https://github.com/oqc-community/rasqal/blob/develop/features_and_concepts.md) as well as a [draft paper](https://github.com/oqc-community/rasqal/blob/develop/docs/papers/Rasqal%20Draft%20v2.pdf) that covers its internals in excruciating detail. If you have any features or ideas you'd like to see implemented feel free to raise a [feature request](https://github.com/oqc-community/Rasqal/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.md&title=)! @@ -19,7 +19,7 @@ If you have any features or ideas you'd like to see implemented feel free to rai 1. Install Rasqal in your favourite Python venv by running `pip install rasqal`. Our current testing is done with `v3.10` of Python. 2. Read the [quick start](https://github.com/oqc-community/rasqal/blob/develop/examples.md) and look at our [Python example](https://github.com/oqc-community/Rasqal/blob/develop/docs/examples.py). -3. (Optional) Read the [paper](https://github.com/oqc-community/rasqal/blob/develop/docs/Rasqal%20Draft%20v2.pdf) for a deep-dive into Rasqals concepts and data structures. +3. (Optional) Read the [paper](https://github.com/oqc-community/rasqal/blob/develop/docs/papers/Rasqal%20Draft%20v2.pdf) for a deep-dive into Rasqals concepts and data structures. ### Contributing diff --git a/src/rasqal/pyproject.toml b/src/rasqal/pyproject.toml index 1e604dd..9a2936f 100644 --- a/src/rasqal/pyproject.toml +++ b/src/rasqal/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ ] authors = [ - { name = "John Dumbell", email = "euphoricEulogy@gmail.com" } + { name = "John Dumbell", email = "john.dumbell42@gmail.com" } ] dependencies = [ diff --git a/src/rasqal/rasqal/_native.pyi b/src/rasqal/rasqal/_native.pyi index b2ccc47..bca29ea 100644 --- a/src/rasqal/rasqal/_native.pyi +++ b/src/rasqal/rasqal/_native.pyi @@ -3,8 +3,7 @@ from typing import Any, Optional, List -from .adaptors import BuilderAdaptor, RuntimeAdaptor - +from .adaptors import RuntimeAdaptor DEFAULT_LOG_FILE = "" @@ -14,50 +13,25 @@ def initialize_file_logger(file_path: str): def initialize_commandline_logger(): pass - -class Graph: - ... - +class Graph: ... class Executor: - def trace_graphs(self): - ... - - def trace_runtime(self): - ... - - def trace_projections(self): - ... - - def step_count_limit(self, limit: int): - ... - - def run( - self, - file_path: str, - runtimes: List[RuntimeAdaptor] - ) -> Any: - """ Runs this file using the automatically-detected entry-point with no arguments. """ + def trace_graphs(self): ... + def trace_runtime(self): ... + def trace_projections(self): ... + def step_count_limit(self, limit: int): ... + def run(self, file_path: str, runtimes: List[RuntimeAdaptor]) -> Any: + """Runs this file using the automatically-detected entry-point with no arguments.""" def run_with_args( - self, - file_path: str, - arguments: List[Any], - runtimes: List[RuntimeAdaptor] + self, file_path: str, arguments: List[Any], runtimes: List[RuntimeAdaptor] ) -> Any: - """ Runs this file using the automatically-detected entry-point. """ + """Runs this file using the automatically-detected entry-point.""" - def parse_file( - self, - file: str, - entry_point: Optional[str] - ) -> Graph: - """ Evaluates and builds this file into the internal execution graph and returns it. """ + def parse_file(self, file: str, entry_point: Optional[str]) -> Graph: + """Evaluates and builds this file into the internal execution graph and returns it.""" def run_graph( - self, - graph: Graph, - arguments: List[Any], - runtime_adaptor: RuntimeAdaptor + self, graph: Graph, arguments: List[Any], runtime_adaptor: RuntimeAdaptor ) -> Any: - """ Runs a pre-built execution graph with the passed-in arguments. """ \ No newline at end of file + """Runs a pre-built execution graph with the passed-in arguments.""" diff --git a/src/rasqal/rasqal/adaptors.py b/src/rasqal/rasqal/adaptors.py index 6299248..0e86e43 100644 --- a/src/rasqal/rasqal/adaptors.py +++ b/src/rasqal/rasqal/adaptors.py @@ -1,7 +1,6 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2024 Oxford Quantum Circuits Ltd -import abc from typing import Dict @@ -15,32 +14,24 @@ class BuilderAdaptor: This builder will then be passed to the provided runtime for execution. """ - def cx(self, controls, target, radii): - ... - def cz(self, controls, target, radii): - ... + def cx(self, controls, target, radii): ... - def cy(self, controls, target, radii): - ... + def cz(self, controls, target, radii): ... - def x(self, qubit, radii): - ... + def cy(self, controls, target, radii): ... - def y(self, qubit, radii): - ... + def x(self, qubit, radii): ... - def z(self, qubit, radii): - ... + def y(self, qubit, radii): ... - def swap(self, qubit1, qubit2): - ... + def z(self, qubit, radii): ... - def reset(self, qubit): - ... + def swap(self, qubit1, qubit2): ... - def measure(self, qubit): - ... + def reset(self, qubit): ... + + def measure(self, qubit): ... class RuntimeAdaptor: @@ -53,6 +44,7 @@ class RuntimeAdaptor: Every time a quantum blob needs to be executed it will query whether a particular runtime is able to support it and then use that builder/runtime combination to execute it, if applicable. """ + def execute(self, builder) -> Dict[str, int]: """ Executes the passed-in builder against the backend and returns a result distribution. @@ -62,7 +54,7 @@ def execute(self, builder) -> Dict[str, int]: return dict() def create_builder(self) -> BuilderAdaptor: - """ Creates a builder to be used with this runtime. """ + """Creates a builder to be used with this runtime.""" return BuilderAdaptor() def has_features(self, required_features: "RequiredFeatures"): diff --git a/src/rasqal/rasqal/logger.py b/src/rasqal/rasqal/logger.py index ff68131..acec775 100644 --- a/src/rasqal/rasqal/logger.py +++ b/src/rasqal/rasqal/logger.py @@ -209,7 +209,13 @@ def __init__( # Set the root to the lowest non-custom log level activated. root.setLevel( - min([val.level for val in self.loggers if (float(val.level / 10)).is_integer()]) + min( + [ + val.level + for val in self.loggers + if (float(val.level / 10)).is_integer() + ] + ) ) def add_loggers(self, loggers_or_names: List[Union[str, logging.Logger]] = ()): @@ -579,7 +585,8 @@ def get_default_logger(): stack = traceback.extract_stack() is_test_env = any( - val.filename is not None and val.filename.endswith(f"unittest\\loader.py") + val.filename is not None + and val.filename.endswith("unittest\\loader.py") for val in stack ) if is_test_env: diff --git a/src/rasqal/rasqal/routing.py b/src/rasqal/rasqal/routing.py index 6329419..abd3fdd 100644 --- a/src/rasqal/rasqal/routing.py +++ b/src/rasqal/rasqal/routing.py @@ -15,7 +15,10 @@ def build_ring_architecture(num_qubits): return [(i % num_qubits, (i + 1) % num_qubits) for i in range(num_qubits)] -def apply_routing(couplings: Union[Architecture, List[Tuple[int, int]]], runtime: Union[RasqalRunner, RuntimeAdaptor]): +def apply_routing( + couplings: Union[Architecture, List[Tuple[int, int]]], + runtime: Union[RasqalRunner, RuntimeAdaptor], +): if isinstance(runtime, RasqalRunner): runtime.runtimes = [TketRuntime(couplings, rt) for rt in runtime.runtimes] return runtime @@ -80,15 +83,20 @@ class TketRuntime(RuntimeAdaptor): Can be """ - def __init__(self, couplings: Union[Architecture, List[Tuple[int, int]]], - forwarded_runtime: RuntimeAdaptor): + def __init__( + self, + couplings: Union[Architecture, List[Tuple[int, int]]], + forwarded_runtime: RuntimeAdaptor, + ): self.forwarded = forwarded_runtime if isinstance(couplings, list): self.arch = Architecture(couplings) elif isinstance(couplings, Architecture): self.arch = couplings else: - raise ValueError(f"Invalid architecture or coupling mappings: {str(couplings)}") + raise ValueError( + f"Invalid architecture or coupling mappings: {str(couplings)}" + ) def execute(self, builder) -> Dict[str, int]: builder: TketBuilder @@ -97,7 +105,7 @@ def execute(self, builder) -> Dict[str, int]: return self.forwarded.execute(self._forward_circuit(builder)) def _forward_circuit(self, builder) -> BuilderAdaptor: - """ Forwards the Tket circuit on to the new builder to be run in the forwarding runtime. """ + """Forwards the Tket circuit on to the new builder to be run in the forwarding runtime.""" fbuilder = self.forwarded.create_builder() for gate in builder.circuit: if gate.op.type == OpType.Rz: @@ -107,11 +115,23 @@ def _forward_circuit(self, builder) -> BuilderAdaptor: elif gate.op.type == OpType.Ry: fbuilder.y(gate.qubits[0].index[0], gate.op.params[0]) elif gate.op.type == OpType.CRx: - fbuilder.cx([gate.qubits[0].index[0]], gate.qubits[1].index[0], gate.op.params[0]) + fbuilder.cx( + [gate.qubits[0].index[0]], + gate.qubits[1].index[0], + gate.op.params[0], + ) elif gate.op.type == OpType.CRy: - fbuilder.cy([gate.qubits[0].index[0]], gate.qubits[1].index[0], gate.op.params[0]) + fbuilder.cy( + [gate.qubits[0].index[0]], + gate.qubits[1].index[0], + gate.op.params[0], + ) elif gate.op.type == OpType.CRz: - fbuilder.cz([gate.qubits[0].index[0]], gate.qubits[1].index[0], gate.op.params[0]) + fbuilder.cz( + [gate.qubits[0].index[0]], + gate.qubits[1].index[0], + gate.op.params[0], + ) elif gate.op.type == OpType.SWAP: fbuilder.swap(gate.qubits[0].index[0], gate.qubits[1].index[0]) elif gate.op.type == OpType.Measure: diff --git a/src/rasqal/rasqal/runtime.py b/src/rasqal/rasqal/runtime.py index e3057c5..bb030f3 100644 --- a/src/rasqal/rasqal/runtime.py +++ b/src/rasqal/rasqal/runtime.py @@ -94,7 +94,5 @@ def run(self, file_path: str, args: List[Any] = None): Runs an .ll or .bc file with the passed-in arguments. Arguments can only be Python primitives or otherwise easily transformable to Rust objects. """ - results = self.executor.run_with_args( - file_path, args or [], self.runtimes - ) + results = self.executor.run_with_args(file_path, args or [], self.runtimes) return results diff --git a/src/rasqal/rasqal/simulators.py b/src/rasqal/rasqal/simulators.py index 4adfe70..3faadab 100644 --- a/src/rasqal/rasqal/simulators.py +++ b/src/rasqal/rasqal/simulators.py @@ -23,6 +23,7 @@ class QASMBuilder(BuilderAdaptor): """ Builder which builds Qiskit quantum circuits. """ + def __init__(self, qubit_count: int): super().__init__() self.circuit = QuantumCircuit(qubit_count, qubit_count) @@ -65,11 +66,14 @@ class QASMRuntime(RuntimeAdaptor): Qiskit-backed runtime. Builds and runs a pure QASM simulation as a backend. """ + def __init__(self, qubit_count=30): self.qubit_count = qubit_count def execute(self, builder: QASMBuilder) -> Dict[str, int]: - aer_config = QasmBackendConfiguration.from_dict(AerSimulator._DEFAULT_CONFIGURATION) + aer_config = QasmBackendConfiguration.from_dict( + AerSimulator._DEFAULT_CONFIGURATION + ) aer_config.n_qubits = builder.circuit.num_qubits qasm_sim = AerSimulator(aer_config) diff --git a/src/rasqal/rasqal/utils.py b/src/rasqal/rasqal/utils.py index 10d05f3..88dbf7e 100644 --- a/src/rasqal/rasqal/utils.py +++ b/src/rasqal/rasqal/utils.py @@ -3,7 +3,11 @@ from typing import Optional -from ._native import initialize_file_logger, initialize_commandline_logger, DEFAULT_LOG_FILE +from ._native import ( + initialize_file_logger, + initialize_commandline_logger, + DEFAULT_LOG_FILE, # noqa +) def initialize_logger(file_path: Optional[str] = None): diff --git a/src/rasqal/src/builders.rs b/src/rasqal/src/builders.rs index 199de44..a39c777 100644 --- a/src/rasqal/src/builders.rs +++ b/src/rasqal/src/builders.rs @@ -255,13 +255,6 @@ impl PyBuilderAdaptor { return Ptr::is_null(self.builder.borrow()) || self.builder.is_none(); } - pub fn ab(&self) -> Result<&PyAny, String> { - let target = self.builder.getattr("ab").expect("'ab' doesn't exist on builder"); - Python::with_gil(|py| { - target.call0().map_err(|err| err.value(py).to_string()) - }) - } - python_methods!(self.builder.x(qubit: i64, radians: f64)); python_methods!(self.builder.y(qubit: i64, radians: f64)); python_methods!(self.builder.z(qubit: i64, radians: f64)); diff --git a/src/rasqal/src/evaluator.rs b/src/rasqal/src/evaluator.rs index ab61cf4..647276b 100644 --- a/src/rasqal/src/evaluator.rs +++ b/src/rasqal/src/evaluator.rs @@ -13,16 +13,17 @@ use crate::smart_pointers::Ptr; use crate::with_mutable; use inkwell::basic_block::BasicBlock; use inkwell::module::Module; -use inkwell::types::AnyTypeEnum; +use inkwell::types::{AnyType, AnyTypeEnum}; use inkwell::values::{ - AggregateValue, AnyValue, AnyValueEnum, AsValueRef, BasicValue, FunctionValue, InstructionOpcode, - InstructionValue + AggregateValue, AnyValue, AnyValueEnum, ArrayValue, AsValueRef, BasicValue, BasicValueEnum, + FunctionValue, InstructionOpcode, InstructionValue, StructValue }; use inkwell::{FloatPredicate, IntPredicate}; use llvm_sys::core::{ LLVMConstIntGetSExtValue, LLVMGetElementType, LLVMGetNumOperands, LLVMGetOperand, LLVMGetTypeKind, LLVMPrintTypeToString, LLVMPrintValueToString, LLVMTypeOf }; +use llvm_sys::prelude::LLVMValueRef; use llvm_sys::LLVMTypeKind; use log::warn; use regex::Regex; @@ -89,33 +90,31 @@ pub fn parse_ref_id_from_instruction(inst: &InstructionValue) -> Option parse_ref_id_from_instruction_str(&inst_str) } -pub fn parse_ref_id_from_instruction_str(inst_str: &String) -> Option { +pub fn parse_ref_id_from_instruction_str(inst_str: &str) -> Option { let llvm_var_finder = Regex::new("([%@][^ ]*) =").unwrap(); - llvm_var_finder.captures(inst_str.as_str()).map_or_else( - || parse_ref_id_from_value(inst_str.clone()).or(None), + llvm_var_finder.captures(inst_str).map_or_else( + || parse_ref_id_from_value(inst_str).or(None), |capture_groups| Some(capture_groups.get(1).unwrap().as_str().to_string()) ) } -/// `var_name` -pub fn get_ref_id_from_value(ptr_string: String) -> String { +pub fn get_ref_id_from_value(ptr_string: &str) -> String { parse_ref_id_from_value(ptr_string).expect("Can't parse ref-id from value.") } -/// `var_name` /// TODO: Need a proper way to get the variables from a general state, while this works it's not /// entirely bulletproof and needs tweaking as issues come up. And issues caused from it are not /// immediately obvious. -pub fn parse_ref_id_from_value(ptr_string: String) -> Option { +pub fn parse_ref_id_from_value(ptr_string: &str) -> Option { let ptr_string = ptr_string.trim_matches('"').trim(); - let pointer_variable_finder = Regex::new("^.*\\s(%[\\w0-9\\-]+)$").unwrap(); - let capture_groups = pointer_variable_finder.captures(ptr_string); + let local_variable_finder: Regex = Regex::new("^.*\\s(%[\\w0-9\\-]+)$").unwrap(); + let capture_groups = local_variable_finder.captures(ptr_string); let mut ref_id = capture_groups.map(|val| val.get(1).unwrap().as_str().to_string()); // If we can't find a local variable, look globally. ref_id = ref_id.or_else(|| { - let pointer_variable_finder = Regex::new("^.*\\s(@[\\w0-9\\-]+)$").unwrap(); - let capture_groups = pointer_variable_finder.captures(ptr_string); + let global_variable_finder: Regex = Regex::new("^.*\\s(@[\\w0-9\\-]+)$").unwrap(); + let capture_groups = global_variable_finder.captures(ptr_string); capture_groups.and_then(|val| { let val = val.get(1).unwrap().as_str().to_string(); if val.trim().is_empty() { @@ -128,8 +127,8 @@ pub fn parse_ref_id_from_value(ptr_string: String) -> Option { // Finally check if we're a global instruction target. ref_id.or_else(|| { - let pointer_variable_finder = Regex::new("^@[^\\s*]+(\\s|$)").unwrap(); - let capture_groups = pointer_variable_finder.captures(ptr_string); + let global_instruction_finder: Regex = Regex::new("^@[^\\s*]+(\\s|$)").unwrap(); + let capture_groups = global_instruction_finder.captures(ptr_string); capture_groups.and_then(|value| { let mut value = value.get(0).unwrap().as_str(); value = value.trim(); @@ -237,7 +236,7 @@ impl QIREvaluator { // Create a callable graph with its arguments, but the values set as empty (validly). let mut callable = Ptr::from(CallableAnalysisGraph::new(&builder.graph)); for param in entry_point.get_params().iter() { - let param_ref_id = get_ref_id_from_value(param.to_string()); + let param_ref_id = get_ref_id_from_value(¶m.to_string()); callable .argument_mappings .insert(param_ref_id, Ptr::from(Value::Empty)); @@ -375,13 +374,22 @@ impl QIREvaluator { } } + /// Fixed up const_extract_value from Inkwell. + pub fn const_extract_value(&self, array: LLVMValueRef, index: u32) -> BasicValueEnum { + use llvm_sys::core::LLVMGetAggregateElement; + + unsafe { BasicValueEnum::new(LLVMGetAggregateElement(array, index as c_uint)) } + } + /// `as_value` /// almost never want to use this directly. Use [`as_value`] instead. fn _as_value_recursive( &self, graph: &Ptr, type_enum: &AnyTypeEnum, val_enum: &AnyValueEnum, context: &Ptr ) -> Option { - let ref_id = parse_ref_id_from_value(val_enum.to_string()); + let ephemeral = val_enum.to_string(); + let stringified_value = ephemeral.trim_matches('"').trim(); + let ref_id = parse_ref_id_from_value(&stringified_value); if let Some(ref_id_value) = ref_id { return Some(Value::Ref(ref_id_value, None)); } @@ -406,7 +414,9 @@ impl QIREvaluator { result.push( self .as_value_ptr( - &vec.const_extract_value(&mut [int]).as_any_value_enum(), + &self + .const_extract_value(vec.as_value_ref(), int) + .as_any_value_enum(), graph, context ) @@ -470,22 +480,50 @@ impl QIREvaluator { AnyTypeEnum::PointerType(t) => { // TODO: GEP analysis and fixing should be cleaned up as soon as Inkwell // supports it. - let full_value_str = val_enum.to_string(); - if full_value_str.contains("getelementptr") { + if stringified_value.contains("getelementptr") { self.extract_gep(val_enum, graph, context) } else { - let pval = val_enum.into_pointer_value(); - let is_struct = t.get_element_type().is_struct_type(); + let pointer_val = val_enum.into_pointer_value(); + + // This is purely for base profile support since its syntax is invalid. + let base_profile_finder: Regex = + Regex::new("^%(Qubit|Result)\\* ((inttoptr \\(i64 ([0-9]+))|(null))").unwrap(); + let capture_groups = base_profile_finder.captures(&stringified_value); + if let Some(groupings) = capture_groups { + let name = groupings.get(1).unwrap().as_str(); + let mut value = if let Some(matched) = groupings.get(4) { + matched.as_str() + } else { + groupings + .get(5) + .expect( + format!( + "Unable to find base profile value. Instruction: {}", + stringified_value.clone() + ) + .as_str() + ) + .as_str() + }; + + if value == "null" { + value = "0"; + } + + return match name { + "Qubit" => Some(Value::Qubit(Qubit::new(value.parse().unwrap()))), + "Result" => Some(Value::Int(value.parse().unwrap())), + _ => panic!("Attempted specific match on non-base-profile pointer. Instruction: {}, name: {}, value: {}", stringified_value.clone(), name.clone(), value.clone()) + }; + } // Structs, especially opaque ones, have their own rules and even if they're // null it may mean something very different. - if (pval.is_null() || pval.is_undef()) && !is_struct { + if pointer_val.is_null() || pointer_val.is_undef() { return Some(Value::Empty); } - // At this point all actual pointers should have been dealt with, so - // we simply need to roll through the pointer to its actual type. - self._as_value_recursive(graph, t.get_element_type().borrow(), val_enum, context) + panic!("Unable to resolve pointer value."); } } AnyTypeEnum::StructType(t) => { @@ -507,6 +545,8 @@ impl QIREvaluator { }; // TODO: Make custom results object, probably re-use projection results. + // TODO: With LLVM version upgrade this likely isn't needed, this is all pointer-based + // anyway. match struct_name { "Qubit" => Some(Value::Qubit(Qubit::new(index))), "Result" => Some(Value::Int(index)), @@ -532,8 +572,8 @@ impl QIREvaluator { result.push( self .as_value_ptr( - &struct_val - .const_extract_value(&mut [int]) + &self + .const_extract_value(struct_val.as_value_ref(), int) .as_any_value_enum(), graph, context @@ -1396,7 +1436,7 @@ impl QIREvaluator { let loops = inst.get_num_operands() - 1; while index < loops { let param = func.get_nth_param(index).unwrap().to_string(); - let param_ref_id = get_ref_id_from_value(param.clone()); + let param_ref_id = get_ref_id_from_value(¶m); let value = self .as_value_ptr(operand_to_value!(inst, index), graph, context) .expect("Unable to resolve value."); diff --git a/src/rasqal/src/exceptions.rs b/src/rasqal/src/exceptions.rs index 5305b8d..3571eac 100644 --- a/src/rasqal/src/exceptions.rs +++ b/src/rasqal/src/exceptions.rs @@ -24,7 +24,8 @@ pub fn catch_panics Result>(wrapped: F) -> Result Option> { + run_with_config(path, RasqalConfig::default()).expect("Execution failed.") + } + + fn run_with_args(path: &str, args: &Vec) -> Result>, String> { + let relative_path = canonicalize(path).unwrap(); let path = relative_path.to_str().unwrap(); let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( @@ -212,120 +216,68 @@ mod tests { runtimes.borrow(), None, &Ptr::from(RasqalConfig::default()) - ); + ) } - #[test] - fn execute_simplified_oracle_generator() { - let relative_path = canonicalize( - "../tests/qsharp/simplified-oracle-generator/qir/simplified-oracle-generator.ll" - ) - .unwrap(); + fn run_with_config(path: &str, config: RasqalConfig) -> Result>, String> { + let relative_path = canonicalize(path).unwrap(); let path = relative_path.to_str().unwrap(); let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( IntegrationRuntime::default() ))); + run_file( path, &Vec::new(), runtimes.borrow(), None, - &Ptr::from(RasqalConfig::default()) - ); + &Ptr::from(config) + ) } #[test] - fn execute_oracle_generator() { - let relative_path = - canonicalize("../tests/qsharp/oracle-generator/qir/oracle-generator.ll").unwrap(); - let path = relative_path.to_str().unwrap(); + fn execute_qaoa() { run("../tests/qsharp/qaoa/qir/qaoa.ll"); } - let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( - IntegrationRuntime::default() - ))); - run_file( - path, - &Vec::new(), - runtimes.borrow(), - None, - &Ptr::from(RasqalConfig::default()) - ); + #[test] + fn execute_simplified_oracle_generator() { + run("../tests/qsharp/simplified-oracle-generator/qir/simplified-oracle-generator.ll"); } #[test] - fn execute_minified_oracle_generator() { - let relative_path = - canonicalize("../tests/qsharp/minified-oracle-generator/qir/minified-oracle-generator.ll") - .unwrap(); - let path = relative_path.to_str().unwrap(); + fn execute_oracle_generator() { run("../tests/qsharp/oracle-generator/qir/oracle-generator.ll"); } - let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( - IntegrationRuntime::default() - ))); - run_file( - path, - &vec![Value::Bool(true)], - runtimes.borrow(), - None, - &Ptr::from(RasqalConfig::default()) + #[test] + fn execute_minified_oracle_generator() { + run_with_args( + "../tests/qsharp/minified-oracle-generator/qir/minified-oracle-generator.ll", + &vec![Value::from(true)] ); } #[test] - fn execute_unrestricted_bell() { - let relative_path = canonicalize("../tests/files/qir/unrestricted_bell.ll").unwrap(); - let path = relative_path.to_str().unwrap(); - - let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( - IntegrationRuntime::default() - ))); - run_file( - path, - &Vec::new(), - runtimes.borrow(), - None, - &Ptr::from(RasqalConfig::default()) - ); - } + fn execute_unrestricted_bell() { run("../tests/files/qir/unrestricted_bell.ll"); } #[test] fn test_step_count() { - let relative_path = canonicalize("../tests/files/qir/unrestricted_bell.ll").unwrap(); - let path = relative_path.to_str().unwrap(); - - let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( - IntegrationRuntime::default() - ))); - let mut config = RasqalConfig::default(); config.step_count_limit(2); - - let results = run_file( - path, - &Vec::new(), - runtimes.borrow(), - None, - &Ptr::from(config) - ); - + let results = run_with_config("../tests/files/qir/unrestricted_bell.ll", config); assert!(results.is_err()) } #[test] - fn execute_bell_int_return() { - let relative_path = canonicalize("../tests/files/qir/bell_int_return.ll").unwrap(); - let path = relative_path.to_str().unwrap(); + fn execute_bell_int_return() { run(&"../tests/files/qir/bell_int_return.ll"); } - let runtimes = Ptr::from(RuntimeCollection::from(&Ptr::from( - IntegrationRuntime::default() - ))); - run_file( - path, - &Vec::new(), - runtimes.borrow(), - None, - &Ptr::from(RasqalConfig::default()) - ); - } + #[test] + fn execute_bell_psi_minus() { run(&"../tests/files/qir/bell_psi_minus.ll"); } + + #[test] + fn execute_bell_psi_plus() { run(&"../tests/files/qir/bell_psi_plus.ll"); } + + #[test] + fn execute_bell_theta_plus() { run(&"../tests/files/qir/bell_theta_plus.ll"); } + + #[test] + fn execute_bell_theta_minus() { run(&"../tests/files/qir/bell_theta_minus.ll"); } } diff --git a/src/rasqal/src/runtime.rs b/src/rasqal/src/runtime.rs index 7f1c369..c835488 100644 --- a/src/rasqal/src/runtime.rs +++ b/src/rasqal/src/runtime.rs @@ -474,9 +474,9 @@ impl QuantumRuntime { if let Some(limit) = &self.constraints.step_limit { let stuff = context.step_count.deref().clone(); if context.step_count.deref() > limit { - return Err(String::from( - format!("Execution step count limitation of {limit} exceeded.") - )); + return Err(String::from(format!( + "Execution step count limitation of {limit} exceeded." + ))); } } diff --git a/src/rasqal/src/smart_pointers.rs b/src/rasqal/src/smart_pointers.rs index 56be86e..528d162 100644 --- a/src/rasqal/src/smart_pointers.rs +++ b/src/rasqal/src/smart_pointers.rs @@ -372,93 +372,103 @@ impl From<&mut T> for FlexiPtr { fn from(value: &mut T) -> Self { FlexiPtr::Borrow(value) } } -impl Add for FlexiPtr where for<'a> &'a T: Add<&'a T, Output = T> { +impl Add for FlexiPtr +where + for<'a> &'a T: Add<&'a T, Output = T> +{ type Output = T; - fn add(self, rhs: Self) -> Self::Output { - self.deref() + rhs.deref() - } + fn add(self, rhs: Self) -> Self::Output { self.deref() + rhs.deref() } } - -impl Sub for FlexiPtr where for<'a> &'a T: Sub<&'a T, Output = T> { +impl Sub for FlexiPtr +where + for<'a> &'a T: Sub<&'a T, Output = T> +{ type Output = T; - fn sub(self, rhs: Self) -> Self::Output { - self.deref() - rhs.deref() - } + fn sub(self, rhs: Self) -> Self::Output { self.deref() - rhs.deref() } } -impl Mul for FlexiPtr where for<'a> &'a T: Mul<&'a T, Output = T> { +impl Mul for FlexiPtr +where + for<'a> &'a T: Mul<&'a T, Output = T> +{ type Output = T; - fn mul(self, rhs: Self) -> Self::Output { - self.deref() * rhs.deref() - } + fn mul(self, rhs: Self) -> Self::Output { self.deref() * rhs.deref() } } -impl Div for FlexiPtr where for<'a> &'a T: Div<&'a T, Output = T> { +impl Div for FlexiPtr +where + for<'a> &'a T: Div<&'a T, Output = T> +{ type Output = T; - fn div(self, rhs: Self) -> Self::Output { - self.deref() / rhs.deref() - } + fn div(self, rhs: Self) -> Self::Output { self.deref() / rhs.deref() } } -impl BitOr for FlexiPtr where for<'a> &'a T: BitOr<&'a T, Output = T> { +impl BitOr for FlexiPtr +where + for<'a> &'a T: BitOr<&'a T, Output = T> +{ type Output = T; - fn bitor(self, rhs: Self) -> Self::Output { - self.deref() | rhs.deref() - } + fn bitor(self, rhs: Self) -> Self::Output { self.deref() | rhs.deref() } } -impl BitAnd for FlexiPtr where for<'a> &'a T: BitAnd<&'a T, Output = T> { +impl BitAnd for FlexiPtr +where + for<'a> &'a T: BitAnd<&'a T, Output = T> +{ type Output = T; - fn bitand(self, rhs: Self) -> Self::Output { - self.deref() & rhs.deref() - } + fn bitand(self, rhs: Self) -> Self::Output { self.deref() & rhs.deref() } } -impl BitXor for FlexiPtr where for<'a> &'a T: BitXor<&'a T, Output = T> { +impl BitXor for FlexiPtr +where + for<'a> &'a T: BitXor<&'a T, Output = T> +{ type Output = T; - fn bitxor(self, rhs: Self) -> Self::Output { - self.deref() ^ rhs.deref() - } + fn bitxor(self, rhs: Self) -> Self::Output { self.deref() ^ rhs.deref() } } -impl Shl for FlexiPtr where for<'a> &'a T: Shl<&'a T, Output = T> { +impl Shl for FlexiPtr +where + for<'a> &'a T: Shl<&'a T, Output = T> +{ type Output = T; - fn shl(self, rhs: Self) -> Self::Output { - self.deref() << rhs.deref() - } + fn shl(self, rhs: Self) -> Self::Output { self.deref() << rhs.deref() } } -impl Shr for FlexiPtr where for<'a> &'a T: Shr<&'a T, Output = T> { +impl Shr for FlexiPtr +where + for<'a> &'a T: Shr<&'a T, Output = T> +{ type Output = T; - fn shr(self, rhs: Self) -> Self::Output { - self.deref() >> rhs.deref() - } + fn shr(self, rhs: Self) -> Self::Output { self.deref() >> rhs.deref() } } -impl Rem for FlexiPtr where for<'a> &'a T: Rem<&'a T, Output = T> { +impl Rem for FlexiPtr +where + for<'a> &'a T: Rem<&'a T, Output = T> +{ type Output = T; - fn rem(self, rhs: Self) -> Self::Output { - self.deref() % rhs.deref() - } + fn rem(self, rhs: Self) -> Self::Output { self.deref() % rhs.deref() } } -impl> Index for FlexiPtr where T: Index { +impl> Index for FlexiPtr +where + T: Index +{ type Output = >::Output; - fn index(&self, index: A) -> &Self::Output { - &self.deref()[index] - } + fn index(&self, index: A) -> &Self::Output { &self.deref()[index] } } #[cfg(test)] diff --git a/src/scripts/psakefile.ps1 b/src/scripts/psakefile.ps1 index c7665b7..a706aab 100644 --- a/src/scripts/psakefile.ps1 +++ b/src/scripts/psakefile.ps1 @@ -13,17 +13,24 @@ Properties { $Target = Join-Path $Root target $Wheels = Join-Path $Target wheels $CargoConfigToml = Join-Path $Root .cargo config.toml - $RustVersion = "1.64.0" $AuditWheelTag = "manylinux_2_31_x86_64" $Python = Resolve-Python } task default -depends build -task build -depends build-llvm, build-rasqal, test-rasqal +task build -depends build-llvm, build-rasqal, test-rasqal, format task check -depends check-licenses -task format -depends format-rust +task format -depends format-rust, format-python task pypi-build -depends build, audit-rasqal, check +task format-python { + Invoke-LoggedCommand -workingDirectory $Root { + pip install ruff + ruff format + ruff check --fix --exit-zero + } +} + task format-rust { Invoke-LoggedCommand -workingDirectory $Root { cargo fmt --all @@ -31,6 +38,7 @@ task format-rust { } task build-llvm -depends init { + $env:LLVM_SYS_150_PREFIX = Resolve-InstallationDirectory Invoke-LoggedCommand -workingDirectory $BuildLlvm { cargo test --release @(Get-CargoArgs) } Invoke-LoggedCommand -workingDirectory $BuildLlvm { cargo build --release @(Get-CargoArgs) } } @@ -57,10 +65,11 @@ task audit-rasqal -depends build-rasqal { } } -task test-rasqal { -# Invoke-LoggedCommand -workingDirectory $Rasqal { -# cargo test --release @(Get-CargoArgs) -# } +task test-rasqal -depends build-rasqal { + # pyo3 has troubles with cargo test without excluding extension-module, so we do that here. + Invoke-LoggedCommand -workingDirectory $Rasqal { + cargo test --release @(Get-CargoArgs) --no-default-features + } # Force reinstall the package if it exists, but not its dependencies. $packages = Get-Wheels rasqal @@ -144,18 +153,17 @@ task init -depends check-environment { } } -task install-llvm-from-archive { - install-llvm $BuildLlvm download (Get-LLVMFeatureVersion) - $installationDirectory = Resolve-InstallationDirectory - Assert (Test-LlvmConfig $installationDirectory) "install-llvm-from-archive failed to install a usable LLVM installation" -} - task install-llvm-from-source { if ($IsWindows) { Include vcvars.ps1 } - install-llvm $BuildLlvm build (Get-LLVMFeatureVersion) + + # TODO: Need to make prefix dynamic and also fix to not use env variables. $installationDirectory = Resolve-InstallationDirectory + $env:LLVM_SYS_150_PREFIX = $installationDirectory + $env:RSQL_CACHE_DIR = $installationDirectory + + install-llvm $BuildLlvm build (Get-LLVMFeatureVersion) Assert (Test-LlvmConfig $installationDirectory) "install-llvm-from-source failed to install a usable LLVM installation" } @@ -185,21 +193,3 @@ task check-licenses { Public domain" } } - -# task update-noticefiles { -# # use cargo-about to generate a notice files -# # notice files are only for wheel distributions -# # as no bundled sources are in the sdist. -# -# # llvm special license is already in the template -# # as it is a hidden transitive dependency. -# # https://github.com/EmbarkStudios/cargo-about -# $config = Join-Path $Root notice.toml -# $template = Join-Path $Root notice.hbs -# $notice = Join-Path $Rasqal NOTICE-WHEEL.txt -# Invoke-LoggedCommand -workingDirectory $Rasqal { -# cargo about generate --config $config --all-features --output-file $notice $template -# $contents = Get-Content -Raw $notice -# [System.Web.HttpUtility]::HtmlDecode($contents) | Out-File $notice -# } -# } diff --git a/src/scripts/utils.ps1 b/src/scripts/utils.ps1 index e165ed7..637b844 100644 --- a/src/scripts/utils.ps1 +++ b/src/scripts/utils.ps1 @@ -6,8 +6,9 @@ Properties { $llvm_releases_url = "https://github.com/llvm/llvm-project/releases" $feature2releaseprefix = @{ "llvm11-0" = "/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"; "llvm12-0" = "/download/llvmorg-12.0.0/clang+llvm-12.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"; + "llvm14-0" = "/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz"; "llvm13-0" = "/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"; - "llvm14-0" = "/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz" + "llvm15-0" = "/download/llvmorg-15.0.0/clang+llvm-15.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"; } } @@ -109,12 +110,7 @@ function Resolve-InstallationDirectory { } function Get-DefaultInstallDirectory { - if (Test-Path env:\RSQL_CACHE_DIR) { - $env:RSQL_CACHE_DIR - } - else { - Join-Path $Target (Get-LLVMFeatureVersion) - } + Join-Path $Target (Get-LLVMFeatureVersion) } # Executes the supplied script block using psake's exec @@ -174,13 +170,8 @@ function Test-InVirtualEnvironment { } function Get-LLVMFeatureVersion { - if (Test-Path env:\RSQL_LLVM_FEATURE_VERSION) { - $env:RSQL_LLVM_FEATURE_VERSION - } - else { - # "llvm11-0", "llvm12-0", "llvm13-0", "llvm14-0" - "llvm14-0" - } + "llvm15-0" + } function Get-CargoArgs { @@ -220,7 +211,6 @@ function install-llvm { [ValidateSet("download", "build")] [string]$operation, [Parameter(Mandatory)] - [ValidateSet("llvm11-0", "llvm12-0", "llvm13-0", "llvm14-0")] [string]$feature ) diff --git a/src/tests/__init__.py b/src/tests/__init__.py index 1c0c539..02c1c48 100644 --- a/src/tests/__init__.py +++ b/src/tests/__init__.py @@ -4,4 +4,6 @@ from rasqal.utils import initialize_logger, DEFAULT_LOG_FILE # Automatically initialize file logging for tests. -initialize_logger(os.path.join(pathlib.Path(__file__).parent.resolve(), DEFAULT_LOG_FILE)) +initialize_logger( + os.path.join(pathlib.Path(__file__).parent.resolve(), DEFAULT_LOG_FILE) +) diff --git a/src/tests/test_rasqal.py b/src/tests/test_rasqal.py index 5c58c98..cc988ec 100644 --- a/src/tests/test_rasqal.py +++ b/src/tests/test_rasqal.py @@ -1,12 +1,10 @@ -import os.path -import pathlib import unittest from os.path import abspath, dirname, join from rasqal.routing import apply_routing, build_ring_architecture from .file_utils import get_qir_path from rasqal.simulators import fetch_qasm_runner -from rasqal.adaptors import (BuilderAdaptor, RuntimeAdaptor) +from rasqal.adaptors import BuilderAdaptor, RuntimeAdaptor from rasqal.runtime import RasqalRunner @@ -50,7 +48,7 @@ def cz(self, controls, target, radii): self.gates.append(f"cz {controls} {target} {radii}") def cy(self, controls, target, radii): - self.metrics.cy_count += 1 + self.metrics.cy_count += 1 self.gates.append(f"cy {controls} {target} {radii}") def x(self, qubit, radii): @@ -143,44 +141,119 @@ def test_oracle_gen(self): runtime, runner = fetch_mock_runner() runner.run(qir) - assert runtime.executed[0].gates == ['measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[1].gates == ['x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[2].gates == ['x 1 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[3].gates == ['x 1 3.141592653589793', 'x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[4].gates == ['x 0 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[5].gates == ['x 0 3.141592653589793', 'x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[6].gates == ['x 0 3.141592653589793', 'x 1 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[7].gates == ['x 0 3.141592653589793', 'x 1 3.141592653589793', 'x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] + assert runtime.executed[0].gates == ["measure 0", "measure 1", "measure 2"] + assert runtime.executed[1].gates == [ + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[2].gates == [ + "x 1 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[3].gates == [ + "x 1 3.141592653589793", + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[4].gates == [ + "x 0 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[5].gates == [ + "x 0 3.141592653589793", + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[6].gates == [ + "x 0 3.141592653589793", + "x 1 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[7].gates == [ + "x 0 3.141592653589793", + "x 1 3.141592653589793", + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] def test_minified_generator(self): qir = fetch_project_ll("minified-oracle-generator") runtime, runner = fetch_mock_runner() runner.run(qir, [True]) - assert runtime.builder_instructions == [ - 'x 0 3.141592653589793', - 'measure 0' - ] + assert runtime.builder_instructions == ["x 0 3.141592653589793", "measure 0"] runtime, runner = fetch_mock_runner() runner.run(qir, [False]) - assert runtime.builder_instructions == [ - 'measure 0' - ] + assert runtime.builder_instructions == ["measure 0"] def test_simplified_generator(self): qir = fetch_project_ll("simplified-oracle-generator") runtime, runner = fetch_mock_runner() runner.run(qir) - assert runtime.executed[0].gates == ['measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[1].gates == ['x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[2].gates == ['x 1 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[3].gates == ['x 1 3.141592653589793', 'x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[4].gates == ['x 0 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[5].gates == ['x 0 3.141592653589793', 'x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[6].gates == ['x 0 3.141592653589793', 'x 1 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] - assert runtime.executed[7].gates == ['x 0 3.141592653589793', 'x 1 3.141592653589793', 'x 2 3.141592653589793', 'measure 0', 'measure 1', 'measure 2'] + assert runtime.executed[0].gates == ["measure 0", "measure 1", "measure 2"] + assert runtime.executed[1].gates == [ + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[2].gates == [ + "x 1 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[3].gates == [ + "x 1 3.141592653589793", + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[4].gates == [ + "x 0 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[5].gates == [ + "x 0 3.141592653589793", + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[6].gates == [ + "x 0 3.141592653589793", + "x 1 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] + assert runtime.executed[7].gates == [ + "x 0 3.141592653589793", + "x 1 3.141592653589793", + "x 2 3.141592653589793", + "measure 0", + "measure 1", + "measure 2", + ] @unittest.skip("Need to defer measure into classical results.") def test_deferred_classical_expression(self):