From 3fafd0fe62d99752832a98afc4e4640be0d5866f Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 22 Aug 2022 23:17:48 -0700 Subject: [PATCH 01/55] Add parallel Rust QIR Runtime --- .cargo/config.toml | 6 +- Cargo.toml | 1 + build/build.ps1 | 5 + build/test.ps1 | 6 + src/Qir/Runtime/lib/CMakeLists.txt | 1 + .../Runtime/lib/range_support/CMakeLists.txt | 35 ++ .../Runtime/lib/range_support/bridge-rt.cpp | 4 + .../Runtime/lib/range_support/bridge-rt.ll | 38 ++ src/Qir/riqir/Cargo.toml | 16 + src/Qir/riqir/build-qir-runtime.ps1 | 68 ++++ src/Qir/riqir/src/arrays.rs | 218 ++++++++++ src/Qir/riqir/src/bigints.rs | 272 +++++++++++++ src/Qir/riqir/src/callables.rs | 211 ++++++++++ src/Qir/riqir/src/lib.rs | 124 ++++++ src/Qir/riqir/src/math.rs | 119 ++++++ src/Qir/riqir/src/output_recording.rs | 47 +++ src/Qir/riqir/src/range_support.rs | 105 +++++ src/Qir/riqir/src/result_bool.rs | 25 ++ src/Qir/riqir/src/strings.rs | 383 ++++++++++++++++++ src/Qir/riqir/src/tuples.rs | 143 +++++++ src/Qir/riqir/test-qir-runtime.ps1 | 26 ++ 21 files changed, 1850 insertions(+), 3 deletions(-) create mode 100644 src/Qir/Runtime/lib/range_support/CMakeLists.txt create mode 100644 src/Qir/Runtime/lib/range_support/bridge-rt.cpp create mode 100644 src/Qir/Runtime/lib/range_support/bridge-rt.ll create mode 100644 src/Qir/riqir/Cargo.toml create mode 100644 src/Qir/riqir/build-qir-runtime.ps1 create mode 100644 src/Qir/riqir/src/arrays.rs create mode 100644 src/Qir/riqir/src/bigints.rs create mode 100644 src/Qir/riqir/src/callables.rs create mode 100644 src/Qir/riqir/src/lib.rs create mode 100644 src/Qir/riqir/src/math.rs create mode 100644 src/Qir/riqir/src/output_recording.rs create mode 100644 src/Qir/riqir/src/range_support.rs create mode 100644 src/Qir/riqir/src/result_bool.rs create mode 100644 src/Qir/riqir/src/strings.rs create mode 100644 src/Qir/riqir/src/tuples.rs create mode 100644 src/Qir/riqir/test-qir-runtime.ps1 diff --git a/.cargo/config.toml b/.cargo/config.toml index ea5cc6b6ada..0db2a34893d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,3 @@ -[target.'cfg(target_os = "windows")'] -rustflags = ["-C", "control-flow-guard"] - +[target.'cfg(target_os = "windows")'] +rustflags = ["-C", "control-flow-guard"] + diff --git a/Cargo.toml b/Cargo.toml index 2a73b23a1ad..38b3e3a1f5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "src/Simulation/qdk_sim_rs", + "src/Qir/riqir", ] [profile.release] diff --git a/build/build.ps1 b/build/build.ps1 index ab02fd81e7a..ef4417484b0 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -12,6 +12,11 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if ($LastExitCode -ne 0) { $script:all_ok = $False } + $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") + & "$qirRuntime/build-qir-runtime.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } } else { Write-Host "Skipping build of qir runtime because ENABLE_QIRRUNTIME variable is set to: $Env:ENABLE_QIRRUNTIME" } diff --git a/build/test.ps1 b/build/test.ps1 index 09b08eb316d..89094b63d8f 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -49,6 +49,12 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { $script:all_ok = $False } + $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") + & "$qirRuntime/test-qir-runtime.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } + $qirTests = (Join-Path $PSScriptRoot "../src/Qir/Tests") & "$qirTests/test-qir-tests.ps1" if ($LastExitCode -ne 0) { diff --git a/src/Qir/Runtime/lib/CMakeLists.txt b/src/Qir/Runtime/lib/CMakeLists.txt index 343d7162ebc..38525545c07 100644 --- a/src/Qir/Runtime/lib/CMakeLists.txt +++ b/src/Qir/Runtime/lib/CMakeLists.txt @@ -3,3 +3,4 @@ add_subdirectory(QSharpFoundation) add_subdirectory(QSharpCore) add_subdirectory(Simulators) add_subdirectory(Tracer) +add_subdirectory(range_support) diff --git a/src/Qir/Runtime/lib/range_support/CMakeLists.txt b/src/Qir/Runtime/lib/range_support/CMakeLists.txt new file mode 100644 index 00000000000..291f6e9c590 --- /dev/null +++ b/src/Qir/Runtime/lib/range_support/CMakeLists.txt @@ -0,0 +1,35 @@ +#=============================================================================== +# Produce the qir_range_support dynamic library +# +add_library(qir_range_support SHARED bridge-rt.cpp) + +target_source_from_qir(qir_range_support bridge-rt.ll) + +if(WIN32) + set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/win-x64/native") +elseif(APPLE) + set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/osx-x64/native") +else() + set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/linux-x64/native") +endif() + + +target_link_libraries(qir_range_support + ${CMAKE_DL_LIBS} + "-L${QIR_LIB_PATH}" + -lqir_runtime + ${SPECTRE_LIBS} +) + +target_include_directories(qir_range_support PUBLIC + ${public_includes} + ${common_includes} +) + +set_property(TARGET qir_range_support PROPERTY POSITION_INDEPENDENT_CODE ON) + +install(TARGETS qir_range_support + RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" + LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" + ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" +) \ No newline at end of file diff --git a/src/Qir/Runtime/lib/range_support/bridge-rt.cpp b/src/Qir/Runtime/lib/range_support/bridge-rt.cpp new file mode 100644 index 00000000000..f6e9440b83e --- /dev/null +++ b/src/Qir/Runtime/lib/range_support/bridge-rt.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "QirRuntime.hpp" diff --git a/src/Qir/Runtime/lib/range_support/bridge-rt.ll b/src/Qir/Runtime/lib/range_support/bridge-rt.ll new file mode 100644 index 00000000000..8db79e4bfb6 --- /dev/null +++ b/src/Qir/Runtime/lib/range_support/bridge-rt.ll @@ -0,0 +1,38 @@ +; Copyright (c) Microsoft Corporation. +; Licensed under the MIT License. + +%Array = type opaque +%Range = type { i64, i64, i64 } +%String = type opaque + +%"struct.QirArray" = type opaque +%"struct.QirRange" = type { i64, i64, i64 } +%"struct.QirString" = type opaque + +declare %"struct.QirArray"* @quantum__rt__array_slice_1d(%"struct.QirArray"*, %"struct.QirRange"* dereferenceable(24), + i1 %forceNewInstance) +declare %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) + +; NOTE: These three functions can be converted to extern C once the spec and compiler are updated to pass %Range by +; pointer instead of by value (see https://github.com/microsoft/qsharp-language/issues/108). Once that +; happens, this file can be removed. + +define dllexport %Array* @__quantum__rt__array_slice_1d(%Array* %.ar, %Range %.range, i1 %forceNewInstance) { + %ar = bitcast %Array* %.ar to %"struct.QirArray"* + %.prange = alloca %Range + store %Range %.range, %Range* %.prange + %range = bitcast %Range* %.prange to %"struct.QirRange"* + %slice = call %"struct.QirArray"* @quantum__rt__array_slice_1d( + %"struct.QirArray"* %ar, %"struct.QirRange"* dereferenceable(24) %range, i1 %forceNewInstance) + %.slice = bitcast %"struct.QirArray"* %slice to %Array* + ret %Array* %.slice +} + +define dllexport %String* @__quantum__rt__range_to_string(%Range %.range) { + %.prange = alloca %Range + store %Range %.range, %Range* %.prange + %range = bitcast %Range* %.prange to %"struct.QirRange"* + %str = call %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) + %.str = bitcast %"struct.QirString"* %str to %String* + ret %String* %.str +} \ No newline at end of file diff --git a/src/Qir/riqir/Cargo.toml b/src/Qir/riqir/Cargo.toml new file mode 100644 index 00000000000..40ba0c78b2b --- /dev/null +++ b/src/Qir/riqir/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "qir-runtime" +version = "0.1.0" +authors = ["Microsoft"] +edition = "2021" + +[dependencies] +num-bigint = { version = "0.4.3", default-features = false } +rand = "0.8.5" + +[build-dependencies] +cc = "1" +which = "4.2.2" + +[lib] +crate-type = ["cdylib","rlib"] diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 new file mode 100644 index 00000000000..f457606e0fb --- /dev/null +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -0,0 +1,68 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +#Requires -PSEdition Core + +& (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) + +$IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; + +Push-Location $PSScriptRoot +try { + # Start with the quick check first and make sure that Rust sources + # meet formatting and style guide rules. + cargo fmt -- --check + if ($LASTEXITCODE -ne 0) { throw "Failed cargo fmt check on QIR Runtime." } + + # Check linting rules defined by clippy, a linting tool provided with the + # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy + # and https://rust-lang.github.io/rust-clippy/master/index.html + # for more information. + # If there's a false positive, that check should be explicitly disabled + # at the point where the false positive occurs with an explanation as to + # why it's OK. + cargo clippy -- -D warnings + if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR Runtime." } + + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # Actually run the build. + cargo build @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR Runtime." } + + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; + } + + # Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so + # they can be included in pipeline artifacts. + $osDir = "win-x64" + if ($IsLinux) { + $osDir = "linux-x64" + } elseif ($IsMacOS) { + $osDir = "osx-x64" + } + $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $osDir native) + if (-not (Test-Path $Env:QIR_DROPS)) { + New-Item -Path $Env:QIR_DROPS -ItemType "directory" + } + if (-not (Test-Path $qirDropsBin)) { + New-Item -Path $qirDropsBin -ItemType "directory" + } + $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) + Copy-Item $qirBinaries $qirDropsBin -Include "qir_runtime*" -Exclude "*.rlib","*.d","*.exp" + + # For Windows platforms, make sure to update the extension of the lib file used for linking + # from *.dll.lib to just .lib + $rustlib = (Join-Path $qirDropsBin qir_runtime.dll.lib) + if (Test-Path $rustlib) { + Remove-Item (Join-Path $qirDropsBin qir_runtime.lib) -ErrorAction SilentlyContinue + Rename-Item $rustlib qir_runtime.lib + } + +} +finally { + Pop-Location +} \ No newline at end of file diff --git a/src/Qir/riqir/src/arrays.rs b/src/Qir/riqir/src/arrays.rs new file mode 100644 index 00000000000..373b135d7dd --- /dev/null +++ b/src/Qir/riqir/src/arrays.rs @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use crate::update_counts; +use std::{rc::Rc, usize}; + +/// # Panics +/// +/// This function panics if the passed in sizes do not fit into the usize type for the +/// current platform. +#[no_mangle] +pub extern "C" fn __quantum__rt__array_create_1d( + elem_size: u32, + size: u64, +) -> *const (usize, Vec) { + let elem_size_size: usize = elem_size.try_into().unwrap(); + let size_size: usize = size.try_into().unwrap(); + let array = vec![0_u8; elem_size_size * size_size]; + Rc::into_raw(Rc::new((elem_size_size, array))) +} + +/// # Safety +/// +/// This function should only be called with an array created by `__quantum__rt__array_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_copy( + arr: *const (usize, Vec), + force: bool, +) -> *const (usize, Vec) { + let rc = Rc::from_raw(arr); + if force || Rc::weak_count(&rc) > 0 { + let copy = rc.as_ref().clone(); + let _ = Rc::into_raw(rc); + Rc::into_raw(Rc::new(copy)) + } else { + let _ = Rc::into_raw(Rc::clone(&rc)); + let _ = Rc::into_raw(rc); + arr + } +} + +/// # Safety +/// +/// This function should only be called with arrays created by `__quantum__rt__array_*` functions. +/// # Panics +/// +/// This function will panic if the given arrays use different element sizes. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_concatenate( + arr1: *const (usize, Vec), + arr2: *const (usize, Vec), +) -> *const (usize, Vec) { + let array1 = Rc::from_raw(arr1); + let array2 = Rc::from_raw(arr2); + assert!( + array1.0 == array2.0, + "Cannot concatenate arrays with differing element sizes: {} vs {}", + array1.0, + array2.0 + ); + + let mut new_array = (array1.0, Vec::new()); + new_array.1.resize(array1.1.len(), 0_u8); + new_array.1.copy_from_slice(array1.1.as_slice()); + + let mut copy = Vec::new(); + copy.resize(array2.1.len(), 0_u8); + copy.copy_from_slice(array2.1.as_slice()); + + new_array.1.append(&mut copy); + let _ = Rc::into_raw(array1); + let _ = Rc::into_raw(array2); + Rc::into_raw(Rc::new(new_array)) +} + +/// # Safety +/// +/// This function should only be called with an array created by `__quantum__rt__array_*` functions. +/// # Panics +/// +/// This function panics if the array size is larger than u64. This shouldn't happen. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const (usize, Vec)) -> u64 { + let array = Rc::from_raw(arr); + let size = array.1.len() / array.0; + let _ = Rc::into_raw(array); + size.try_into().unwrap() +} + +/// # Safety +/// +/// This function should only be called with an array created by `__quantum__rt__array_*` functions. +/// # Panics +/// +/// This function panics if the given index is larger than the usize type for the current platform. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( + arr: *const (usize, Vec), + index: u64, +) -> *mut i8 { + let array = Rc::from_raw(arr); + let i: usize = index.try_into().unwrap(); + let ptr = array.1.as_ptr().wrapping_add(array.0 * i) as *mut i8; + let _ = Rc::into_raw(array); + ptr +} + +/// # Safety +/// +/// This function should only be called with an array created by `__quantum__rt__array_*` functions. +/// If the reference count after update is less than or equal to zero, the array is cleaned up +/// and the pointer is no longer valid. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_update_reference_count( + arr: *const (usize, Vec), + update: i32, +) { + update_counts(arr, update, false); +} + +/// # Safety +/// +/// This function should only be called with an array created by `__quantum__rt__array_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__array_update_alias_count( + arr: *const (usize, Vec), + update: i32, +) { + update_counts(arr, update, true); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::range_support::{quantum__rt__array_slice_1d, Range}; + + #[test] + fn test_array_1d_basics() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + let arr3 = __quantum__rt__array_concatenate(arr, arr2); + assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + let arr4 = quantum__rt__array_slice_1d( + arr3, + Range { + start: 0, + step: 2, + end: 5, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr4), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + let arr5 = quantum__rt__array_slice_1d( + arr3, + Range { + start: 4, + step: -2, + end: 0, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr5), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + let arr6 = quantum__rt__array_slice_1d( + arr5, + Range { + start: 0, + step: 1, + end: -1, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr6), 0); + __quantum__rt__array_update_reference_count(arr, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + __quantum__rt__array_update_reference_count(arr2, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + __quantum__rt__array_update_reference_count(arr3, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + __quantum__rt__array_update_reference_count(arr4, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + __quantum__rt__array_update_reference_count(arr5, -1); + __quantum__rt__array_update_reference_count(arr6, -1); + } + } +} diff --git a/src/Qir/riqir/src/bigints.rs b/src/Qir/riqir/src/bigints.rs new file mode 100644 index 00000000000..1081a217567 --- /dev/null +++ b/src/Qir/riqir/src/bigints.rs @@ -0,0 +1,272 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use crate::update_counts; +use num_bigint::BigInt; +use std::{mem::ManuallyDrop, rc::Rc}; + +#[no_mangle] +pub extern "C" fn __quantum__rt__bigint_create_i64(input: i64) -> *const BigInt { + Rc::into_raw(Rc::new(input.into())) +} + +/// # Safety +/// +/// This function expects the second argument to be a well-formed C-style array of signed big-endian bytes +/// with the size of that array passed as the first argument. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_create_array( + size: u32, + input: *const u8, +) -> *const BigInt { + Rc::into_raw(Rc::new(BigInt::from_signed_bytes_be( + std::slice::from_raw_parts(input, size as usize), + ))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_get_data(input: *const BigInt) -> *const u8 { + ManuallyDrop::new((*input).to_signed_bytes_be()).as_ptr() +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +/// # Panics +/// +/// This function will panic if the length of the QIR bigint as an array is larger than can fit in a u32. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_get_length(input: *const BigInt) -> u32 { + let size = (*input).to_signed_bytes_be().len(); + size.try_into().unwrap() +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +/// If the reference count after update is less than or equal to zero, the QIR bigint is cleaned up +/// and the pointer is no longer valid. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_update_reference_count( + input: *const BigInt, + update: i32, +) { + update_counts(input, update, false); +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_negate(input: *const BigInt) -> *const BigInt { + Rc::into_raw(Rc::new(&(*input) * -1)) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_add( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) + &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_subtract( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) - &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_multiply( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) * &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_divide( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) / &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_modulus( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) % &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_power( + base: *const BigInt, + exponent: u32, +) -> *const BigInt { + Rc::into_raw(Rc::new((*base).pow(exponent))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitand( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) & &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitor( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) | &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitxor( + lhs: *const BigInt, + rhs: *const BigInt, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*lhs) ^ &(*rhs))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_bitnot(input: *const BigInt) -> *const BigInt { + Rc::into_raw(Rc::new(!&(*input))) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_shiftleft( + input: *const BigInt, + amount: u64, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*input) << amount)) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_shiftright( + input: *const BigInt, + amount: u64, +) -> *const BigInt { + Rc::into_raw(Rc::new(&(*input) >> amount)) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_equal( + lhs: *const BigInt, + rhs: *const BigInt, +) -> bool { + (*lhs) == (*rhs) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_greater( + lhs: *const BigInt, + rhs: *const BigInt, +) -> bool { + (*lhs) > (*rhs) +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_greater_eq( + lhs: *const BigInt, + rhs: *const BigInt, +) -> bool { + (*lhs) >= (*rhs) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bigint_basics() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bytes = 42_i64.to_be_bytes(); + unsafe { + let bigint_1 = + __quantum__rt__bigint_create_array(bytes.len().try_into().unwrap(), bytes.as_ptr()); + assert!(__quantum__rt__bigint_equal(bigint_0, bigint_1)); + let bigint_2 = __quantum__rt__bigint_add(bigint_0, bigint_1); + let bigint_3 = __quantum__rt__bigint_create_i64(84); + assert!((*bigint_2) == 84.try_into().unwrap()); + assert!(__quantum__rt__bigint_equal(bigint_2, bigint_3)); + let bigint_4 = __quantum__rt__bigint_negate(bigint_0); + assert!((*bigint_4) == (-42).try_into().unwrap()); + let bigint_5 = __quantum__rt__bigint_add(bigint_2, bigint_4); + assert!((*bigint_5) == (42).try_into().unwrap()); + assert!(__quantum__rt__bigint_greater(bigint_2, bigint_5)); + assert!(__quantum__rt__bigint_greater(bigint_5, bigint_4)); + let bigint_6 = __quantum__rt__bigint_create_i64(2); + let bigint_7 = __quantum__rt__bigint_multiply(bigint_5, bigint_6); + assert!(__quantum__rt__bigint_equal(bigint_7, bigint_2)); + let bigint_8 = __quantum__rt__bigint_shiftleft(bigint_5, 1); + assert!(__quantum__rt__bigint_equal(bigint_7, bigint_8)); + let bigint_9 = __quantum__rt__bigint_shiftright(bigint_8, 1); + assert!(__quantum__rt__bigint_equal(bigint_9, bigint_0)); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_3, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_5, -1); + __quantum__rt__bigint_update_reference_count(bigint_6, -1); + __quantum__rt__bigint_update_reference_count(bigint_7, -1); + __quantum__rt__bigint_update_reference_count(bigint_8, -1); + __quantum__rt__bigint_update_reference_count(bigint_9, -1); + } + } +} diff --git a/src/Qir/riqir/src/callables.rs b/src/Qir/riqir/src/callables.rs new file mode 100644 index 00000000000..cdd09026bda --- /dev/null +++ b/src/Qir/riqir/src/callables.rs @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use crate::{ + arrays::{__quantum__rt__array_concatenate, __quantum__rt__array_update_reference_count}, + tuples::{__quantum__rt__tuple_copy, __quantum__rt__tuple_update_reference_count}, + update_counts, +}; +use std::{cell::RefCell, rc::Rc, usize}; + +#[derive(Clone)] +pub struct Callable { + func_table: *mut *mut u8, + mem_table: *mut *mut u8, + cap_tuple: *mut u8, + is_adj: RefCell, + is_ctl: RefCell, +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__callable_create( + func_table: *mut *mut u8, + mem_table: *mut *mut u8, + cap_tuple: *mut u8, +) -> *const Callable { + Rc::into_raw(Rc::new(Callable { + func_table, + mem_table, + cap_tuple, + is_adj: RefCell::new(false), + is_ctl: RefCell::new(0), + })) +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn __quantum__rt__callable_invoke( + callable: *const Callable, + args_tup: *mut u8, + res_tup: *mut u8, +) { + let call = Rc::from_raw(callable); + let index = (if *call.is_adj.borrow() { 1 } else { 0 }) + + (if *call.is_ctl.borrow() > 0 { 2 } else { 0 }); + + // Collect any nested controls into a single control list. + let mut args_copy: *mut *const Vec = std::ptr::null_mut(); + if !args_tup.is_null() { + // Copy the tuple so we can potentially edit it. + args_copy = __quantum__rt__tuple_copy(args_tup.cast::<*const Vec>(), true); + + if *call.is_ctl.borrow() > 0 { + // If there are any controls, increment the reference count on the control list. This is just + // to balance the decrement that will happen in the loop and at the end of invoking the callable + // to ensure the original, non-owned list does not get incorrectly cleaned up. + __quantum__rt__array_update_reference_count( + *args_copy.cast::<*const (usize, Vec)>(), + 1, + ); + + let mut ctl_count = *call.is_ctl.borrow(); + while ctl_count > 1 { + let ctls = *args_copy.cast::<*const (usize, Vec)>(); + let inner_tuple = *args_copy + .cast::<*const (usize, Vec)>() + .wrapping_add(1) + .cast::<*mut *const Vec>(); + let inner_ctls = *inner_tuple.cast::<*const (usize, Vec)>(); + let new_ctls = __quantum__rt__array_concatenate(ctls, inner_ctls); + let new_args = __quantum__rt__tuple_copy(inner_tuple, true); + *new_args.cast::<*const (usize, Vec)>() = new_ctls; + + // Decrementing the reference count is either the extra count added above or the new + // list created when performing concatenate above. In the latter case, the concatenated + // list will get cleaned up, preventing memory from leaking. + __quantum__rt__array_update_reference_count( + *args_copy.cast::<*const (usize, Vec)>(), + -1, + ); + // Decrement the count on the copy to clean it up as well, since we created a new copy + // with the updated controls list. + __quantum__rt__tuple_update_reference_count(args_copy, -1); + args_copy = new_args; + ctl_count -= 1; + } + } + } + + (*call + .func_table + .wrapping_add(index) + .cast::())( + call.cap_tuple, + args_copy.cast::(), + res_tup, + ); + if *call.is_ctl.borrow() > 0 { + __quantum__rt__array_update_reference_count( + *args_copy.cast::<*const (usize, Vec)>(), + -1, + ); + } + if !args_copy.is_null() { + __quantum__rt__tuple_update_reference_count(args_copy, -1); + } + let _ = Rc::into_raw(call); +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_copy( + callable: *const Callable, + force: bool, +) -> *const Callable { + let rc = Rc::from_raw(callable); + if force || Rc::weak_count(&rc) > 0 { + let copy = rc.as_ref().clone(); + let _ = Rc::into_raw(rc); + Rc::into_raw(Rc::new(copy)) + } else { + let _ = Rc::into_raw(Rc::clone(&rc)); + let _ = Rc::into_raw(rc); + callable + } +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_make_adjoint(callable: *const Callable) { + let call = Rc::from_raw(callable); + let _ = call.is_adj.replace_with(|&mut old| !old); + let _ = Rc::into_raw(call); +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_make_controlled(callable: *const Callable) { + let call = Rc::from_raw(callable); + let _ = call.is_ctl.replace_with(|&mut old| old + 1); + let _ = Rc::into_raw(call); +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +/// If the reference count after update is less than or equal to zero, the callable is cleaned up +/// and the pointer is no longer valid. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_update_reference_count( + callable: *const Callable, + update: i32, +) { + update_counts(callable, update, false); +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__callable_update_alias_count( + callable: *const Callable, + update: i32, +) { + update_counts(callable, update, true); +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +/// If the reference count after update is less than or equal to zero, the capture tuple is cleaned up +/// and the pointer is no longer valid. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__capture_update_reference_count( + callable: *const Callable, + update: i32, +) { + let call = Rc::from_raw(callable); + if !call.mem_table.is_null() && !(*(call.mem_table)).is_null() { + (*call.mem_table.cast::())(call.cap_tuple, update); + } + let _ = Rc::into_raw(call); +} + +/// # Safety +/// +/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__capture_update_alias_count( + callable: *const Callable, + update: i32, +) { + let call = Rc::from_raw(callable); + if !call.mem_table.is_null() && !(*(call.mem_table.wrapping_add(1))).is_null() { + let _val = **(call.mem_table.cast::<*mut usize>().wrapping_add(1)); + (*call + .mem_table + .wrapping_add(1) + .cast::())(call.cap_tuple, update); + } + let _ = Rc::into_raw(call); +} diff --git a/src/Qir/riqir/src/lib.rs b/src/Qir/riqir/src/lib.rs new file mode 100644 index 00000000000..d849cc0ad21 --- /dev/null +++ b/src/Qir/riqir/src/lib.rs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +// Rust Implementation for Quantum Intermediate Representation + +pub mod arrays; +pub mod bigints; +pub mod callables; +pub mod math; +pub mod output_recording; +pub mod range_support; +pub mod result_bool; +pub mod strings; +pub mod tuples; + +use std::{ + ffi::CString, + rc::{Rc, Weak}, +}; + +/// Utility used for managing refcounted items. +unsafe fn update_counts(raw_rc: *const T, update: i32, is_alias: bool) { + let mut remaining = update; + while remaining != 0 { + let rc = Rc::from_raw(raw_rc); + if remaining > 0 { + // Create and leak new instances to increment the count on the contained item. + if is_alias { + let _ = Weak::into_raw(Rc::downgrade(&rc)); + } else { + let _ = Rc::into_raw(Rc::clone(&rc)); + } + + remaining -= 1; + } else { + // Create and drop instances to decrement the count on contained item. + if is_alias { + let w = Weak::into_raw(Rc::downgrade(&rc)); + + // Need to drop two for a net decrement, since above line increments. + drop(Weak::from_raw(w)); + drop(Weak::from_raw(w)); + } else { + drop(Rc::from_raw(raw_rc)); + } + + remaining += 1; + } + + let _ = Rc::into_raw(rc); + } +} + +#[derive(Copy, Clone)] +#[repr(i8)] +pub enum Pauli { + I = 0, + X = 1, + Z = 2, + Y = 3, +} + +/// # Panics +/// +/// Will panic if unable to allocate memory. +#[no_mangle] +pub extern "C" fn __quantum__rt__memory_allocate(size: u64) -> *mut u8 { + (vec![0_u8; size.try_into().unwrap()]).leak().as_mut_ptr() +} + +/// # Safety +/// +/// This function should only be called with a string created by `__quantum__rt__string_*` functions. +/// # Panics +/// +/// Panics unconditionally with the given message. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) { + panic!("{}", (&*str).to_str().expect("Unable to convert string")); +} + +/// # Safety +/// +/// This function should only be called with a string created by `__quantum__rt__string_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__message(str: *const CString) { + println!("{}", (&*str).to_str().expect("Unable to convert string")); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_memory_allocate() { + let size = 10; + let mem = __quantum__rt__memory_allocate(size as u64); + unsafe { + for val in Vec::from_raw_parts(mem, size, size) { + assert_eq!(val, 0); + } + } + } + + #[test] + #[should_panic(expected = "FAIL")] + fn test_fail() { + let str = CString::new("FAIL").unwrap(); + unsafe { + __quantum__rt__fail(&str); + } + } + + #[test] + fn test_message() { + let str = CString::new("Message").unwrap(); + unsafe { + __quantum__rt__message(&str); + } + // message should not consume string, so check that it's value is still correct. + assert_eq!(str.to_str().unwrap(), "Message"); + } +} diff --git a/src/Qir/riqir/src/math.rs b/src/Qir/riqir/src/math.rs new file mode 100644 index 00000000000..3a0aa17406e --- /dev/null +++ b/src/Qir/riqir/src/math.rs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +// TODO: transition math functions to `__quantum__rt` once compiler support is ready. + +use rand::Rng; +use std::os::raw::c_double; + +use crate::{__quantum__rt__fail, strings::convert}; + +#[no_mangle] +pub extern "C" fn __quantum__qis__nan__body() -> c_double { + c_double::NAN +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__isnan__body(val: c_double) -> bool { + val.is_nan() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__infinity__body() -> c_double { + c_double::INFINITY +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__isinf__body(val: c_double) -> bool { + val.is_infinite() && val.is_sign_positive() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__isnegativeinfinity__body(val: c_double) -> bool { + val.is_infinite() && val.is_sign_negative() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__sin__body(val: c_double) -> c_double { + val.sin() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__cos__body(val: c_double) -> c_double { + val.cos() +} +#[no_mangle] +pub extern "C" fn __quantum__qis__tan__body(val: c_double) -> c_double { + val.tan() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arctan2__body(y: c_double, x: c_double) -> c_double { + y.atan2(x) +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__sinh__body(val: c_double) -> c_double { + val.sinh() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__cosh__body(val: c_double) -> c_double { + val.cosh() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__tanh__body(val: c_double) -> c_double { + val.tanh() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arcsin__body(val: c_double) -> c_double { + val.asin() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arccos__body(val: c_double) -> c_double { + val.acos() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__arctan__body(val: c_double) -> c_double { + val.atan() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__sqrt__body(val: c_double) -> c_double { + val.sqrt() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__log__body(val: c_double) -> c_double { + val.ln() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__ieeeremainder__body(x: c_double, y: c_double) -> c_double { + x - y * (x / y).round() +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__drawrandomint__body(min: i64, max: i64) -> i64 { + if min > max { + unsafe { + __quantum__rt__fail(convert(&"Invalid Argument: minimum > maximum".to_string())); + } + } + rand::thread_rng().gen_range(min..=max) +} + +#[no_mangle] +pub extern "C" fn __quantum__qis__drawrandomdouble__body(min: c_double, max: c_double) -> f64 { + if min > max { + unsafe { + __quantum__rt__fail(convert(&"Invalid Argument: minimum > maximum".to_string())); + } + } + rand::thread_rng().gen_range(min..=max) +} diff --git a/src/Qir/riqir/src/output_recording.rs b/src/Qir/riqir/src/output_recording.rs new file mode 100644 index 00000000000..d9bdc070c00 --- /dev/null +++ b/src/Qir/riqir/src/output_recording.rs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use std::os::raw::c_double; + +#[no_mangle] +pub extern "C" fn __quantum__rt__array_start_record_output() { + println!("RESULT\tARRAY_START"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__array_end_record_output() { + println!("RESULT\tARRAY_END"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__tuple_start_record_output() { + println!("RESULT\tTUPLE_START"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__tuple_end_record_output() { + println!("RESULT\tTUPLE_END"); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__int_record_output(val: i64) { + println!("RESULT\t{}", val); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__double_record_output(val: c_double) { + println!( + "RESULT\t{}", + if (val.floor() - val.ceil()).abs() < c_double::EPSILON { + format!("{:.1}", val) + } else { + format!("{}", val) + } + ); +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__bool_record_output(val: bool) { + println!("RESULT\t{}", val); +} diff --git a/src/Qir/riqir/src/range_support.rs b/src/Qir/riqir/src/range_support.rs new file mode 100644 index 00000000000..dea3ca046a2 --- /dev/null +++ b/src/Qir/riqir/src/range_support.rs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +// Functionality in this file can be removed when range support is dropped from the QIR runtime. + +use crate::strings::convert; +use std::{ffi::CString, rc::Rc}; + +#[repr(C)] +pub struct Range { + pub start: i64, + pub step: i64, + pub end: i64, +} + +#[no_mangle] +pub extern "C" fn quantum__rt__range_to_string(input: Range) -> *const CString { + let mut range_str = input.start.to_string() + ".."; + if input.step != 1 { + range_str += &(input.step.to_string() + ".."); + } + range_str += &input.end.to_string(); + + convert(&range_str) +} + +/// # Safety +/// +/// This function should only be called with an array created by `__quantum__rt__array_*` functions. +/// # Panics +/// +/// This function will panic if the item size in the array or the range step size is larger than what can be stored in an +/// u64. This should never happen. +#[no_mangle] +pub unsafe extern "C" fn quantum__rt__array_slice_1d( + arr: *const (usize, Vec), + range: Range, +) -> *const (usize, Vec) { + let array = Rc::from_raw(arr); + let item_size: i64 = array.0.try_into().unwrap(); + let mut slice = (array.0, Vec::new()); + let iter: Box> = if range.step > 0 { + Box::new(range.start * item_size..=range.end * item_size) + } else { + Box::new((range.end * item_size..=range.start * item_size).rev()) + }; + + let step: i64 = range.step.abs(); + for i in iter.step_by((step * item_size).try_into().unwrap()) { + let index = i.try_into().unwrap(); + let mut copy = Vec::new(); + copy.resize(array.0, 0_u8); + copy.copy_from_slice(&array.1[index..index + array.0]); + slice.1.append(&mut copy); + } + + let _ = Rc::into_raw(array); + Rc::into_raw(Rc::new(slice)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::strings::{ + __quantum__rt__string_get_data, __quantum__rt__string_update_reference_count, + }; + use std::ffi::CStr; + + #[test] + fn test_to_string() { + let input4 = Range { + start: 0, + step: 1, + end: 9, + }; + let str4 = quantum__rt__range_to_string(input4); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str4)) + .to_str() + .unwrap(), + "0..9" + ); + } + let input5 = Range { + start: 0, + step: 2, + end: 12, + }; + let str5 = quantum__rt__range_to_string(input5); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str5)) + .to_str() + .unwrap(), + "0..2..12" + ); + } + unsafe { + __quantum__rt__string_update_reference_count(str4, -1); + __quantum__rt__string_update_reference_count(str5, -1); + } + } +} diff --git a/src/Qir/riqir/src/result_bool.rs b/src/Qir/riqir/src/result_bool.rs new file mode 100644 index 00000000000..e45aaa5f69c --- /dev/null +++ b/src/Qir/riqir/src/result_bool.rs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use std::ffi::c_void; + +#[no_mangle] +pub extern "C" fn __quantum__rt__result_get_zero() -> *mut c_void { + std::ptr::null_mut() +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__result_get_one() -> *mut c_void { + 1 as *mut c_void +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__result_equal(r1: *mut c_void, r2: *mut c_void) -> bool { + r1 == r2 +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__result_update_reference_count(_res: *mut c_void, _update: i32) { + // no-op +} diff --git a/src/Qir/riqir/src/strings.rs b/src/Qir/riqir/src/strings.rs new file mode 100644 index 00000000000..563f68a39ae --- /dev/null +++ b/src/Qir/riqir/src/strings.rs @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use crate::update_counts; +use crate::Pauli; +use num_bigint::BigInt; +use std::{ + ffi::{CStr, CString}, + os::raw::{c_char, c_double}, + rc::Rc, +}; + +/// # Safety +/// +/// This function should only be called with a valid, null-terminated, C-style string. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_create(str: *mut c_char) -> *const CString { + let cstring = CString::new(CStr::from_ptr(str).to_owned()).expect("Failed to create %String"); + Rc::into_raw(Rc::new(cstring)) +} + +/// # Safety +/// +/// This function should only be called with a string created by `__quantum__rt__string_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_get_data(str: *const CString) -> *const c_char { + (&*str).as_bytes_with_nul().as_ptr().cast::() +} + +/// # Safety +/// +/// This function should only be called with a string created by `__quantum__rt__string_*` functions. +/// # Panics +/// +/// Will panic if length is larger than will fit in an 32-bit integer. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_get_length(str: *const CString) -> u32 { + (&*str).as_bytes().len().try_into().unwrap() +} + +/// # Safety +/// +/// This function should only be called with a string created by `__quantum__rt__string_*` functions. +/// If the reference count after update is less than or equal to zero, the string is cleaned up +/// and the pointer is no longer valid. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_update_reference_count( + str: *const CString, + update: i32, +) { + update_counts(str, update, false); +} + +/// # Safety +/// +/// This function should only be called with strings created by `__quantum__rt__string_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_concatenate( + s1: *const CString, + s2: *const CString, +) -> *const CString { + let mut new_str = (&*s1) + .clone() + .into_string() + .expect("Unable to convert string"); + + new_str.push_str( + (&*s2) + .clone() + .into_string() + .expect("Unable to convert string") + .as_str(), + ); + + Rc::into_raw(Rc::new( + CString::new(new_str.as_bytes()).expect("Unable to convert string"), + )) +} + +/// # Safety +/// +/// This function should only be called with strings created by `__quantum__rt__string_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__string_equal( + s1: *const CString, + s2: *const CString, +) -> bool { + (&*s1).to_str().expect("Unable to convert string") + == (&*s2).to_str().expect("Unable to convert string") +} + +pub(crate) fn convert(input: &T) -> *const CString +where + T: Sized + ToString, +{ + unsafe { + __quantum__rt__string_create( + CString::new(input.to_string()) + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ) + } +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__int_to_string(input: i64) -> *const CString { + convert(&input) +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString { + if (input.floor() - input.ceil()).abs() < c_double::EPSILON { + convert(&format!("{:.1}", input)) + } else { + convert(&input) + } +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__bool_to_string(input: bool) -> *const CString { + convert(&input) +} + +#[no_mangle] +pub extern "C" fn __quantum__rt__pauli_to_string(input: Pauli) -> *const CString { + match input { + Pauli::I => convert(&"PauliI"), + Pauli::X => convert(&"PauliX"), + Pauli::Y => convert(&"PauliY"), + Pauli::Z => convert(&"PauliZ"), + } +} + +/// # Safety +/// +/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__bigint_to_string(input: *const BigInt) -> *const CString { + convert(&*input) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bigints::{ + __quantum__rt__bigint_create_i64, __quantum__rt__bigint_update_reference_count, + }; + + #[test] + fn test_string_create() { + let orig_str = CString::new("Test String").unwrap(); + let str = unsafe { + __quantum__rt__string_create( + orig_str.as_bytes_with_nul().as_ptr() as *mut std::os::raw::c_char + ) + }; + // string_create should make a copy, not consume original. + assert_eq!(orig_str.to_str().unwrap(), "Test String"); + drop(orig_str); + assert!(!str.is_null()); + unsafe { + // Copy should be valid after original is dropped. + assert_eq!( + Rc::from_raw(str) + .to_str() + .expect("Unable to convert input string"), + "Test String" + ); + } + } + + #[test] + fn test_string_get_data() { + let str = unsafe { + __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8 + ) + }; + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str)) + .to_str() + .unwrap(), + "Data" + ); + } + unsafe { + __quantum__rt__string_update_reference_count(str, -1); + } + } + + #[test] + fn test_string_get_length() { + let str = unsafe { + __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8 + ) + }; + assert_eq!(unsafe { __quantum__rt__string_get_length(str) }, 4); + unsafe { + __quantum__rt__string_update_reference_count(str, -1); + } + } + + #[test] + fn test_string_update_reference_count() { + unsafe { + let str = __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let rc = Rc::from_raw(str); + assert_eq!(Rc::strong_count(&rc), 1); + __quantum__rt__string_update_reference_count(str, 2); + assert_eq!(Rc::strong_count(&rc), 3); + __quantum__rt__string_update_reference_count(str, -2); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + __quantum__rt__string_update_reference_count(str, -1); + } + } + + #[test] + fn test_string_concatenate() { + unsafe { + let str1 = __quantum__rt__string_create( + CString::new("Hello").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let str2 = __quantum__rt__string_create( + CString::new(", World!") + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ); + let str3 = __quantum__rt__string_concatenate(str1, str2); + // Concatenated string should have combined value. + let rc = Rc::from_raw(str3); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str3)) + .to_str() + .unwrap(), + "Hello, World!" + ); + __quantum__rt__string_update_reference_count(str3, -1); + // After decrement and drop, original strings should still be valid. + let rc = Rc::from_raw(str2); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str2)) + .to_str() + .unwrap(), + ", World!" + ); + __quantum__rt__string_update_reference_count(str2, -1); + let rc = Rc::from_raw(str1); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1)) + .to_str() + .unwrap(), + "Hello" + ); + __quantum__rt__string_update_reference_count(str1, -1); + } + } + + #[test] + fn test_string_equal() { + unsafe { + let str1 = __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let str2 = __quantum__rt__string_create( + CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, + ); + let str3 = __quantum__rt__string_create( + CString::new("Not Data") + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ); + assert!(__quantum__rt__string_equal(str1, str2)); + assert!(!__quantum__rt__string_equal(str1, str3)); + // Confirm data is still valid and not consumed by the check. + let rc = Rc::from_raw(str3); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str3)) + .to_str() + .unwrap(), + "Not Data" + ); + __quantum__rt__string_update_reference_count(str3, -1); + let rc = Rc::from_raw(str2); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str2)) + .to_str() + .unwrap(), + "Data" + ); + __quantum__rt__string_update_reference_count(str2, -1); + let rc = Rc::from_raw(str1); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1)) + .to_str() + .unwrap(), + "Data" + ); + __quantum__rt__string_update_reference_count(str1, -1); + } + } + + #[test] + fn test_to_string() { + let input0 = 42; + let str0 = __quantum__rt__int_to_string(input0); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str0)) + .to_str() + .unwrap(), + "42" + ); + } + let input1 = 4.2; + let str1 = __quantum__rt__double_to_string(input1); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1)) + .to_str() + .unwrap(), + "4.2" + ); + } + let input2 = false; + let str2 = __quantum__rt__bool_to_string(input2); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str2)) + .to_str() + .unwrap(), + "false" + ); + } + let input3 = Pauli::Z; + let str3 = __quantum__rt__pauli_to_string(input3); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str3)) + .to_str() + .unwrap(), + "PauliZ" + ); + } + let input4 = __quantum__rt__bigint_create_i64(400_002); + unsafe { + let str4 = __quantum__rt__bigint_to_string(input4); + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str4)) + .to_str() + .unwrap(), + "400002" + ); + + __quantum__rt__string_update_reference_count(str0, -1); + __quantum__rt__string_update_reference_count(str1, -1); + __quantum__rt__string_update_reference_count(str2, -1); + __quantum__rt__string_update_reference_count(str3, -1); + __quantum__rt__string_update_reference_count(str4, -1); + __quantum__rt__bigint_update_reference_count(input4, -1); + } + } +} diff --git a/src/Qir/riqir/src/tuples.rs b/src/Qir/riqir/src/tuples.rs new file mode 100644 index 00000000000..e40f5b4d650 --- /dev/null +++ b/src/Qir/riqir/src/tuples.rs @@ -0,0 +1,143 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use crate::update_counts; +use std::{mem::size_of, rc::Rc, usize}; + +/// # Panics +/// +/// Will panic if the given size is larger than pointer width for the platform. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn __quantum__rt__tuple_create(size: u64) -> *mut *const Vec { + let mut mem = vec![ + 0_u8; + >::try_from(size).unwrap() + + size_of::<*const Vec>() + ]; + + unsafe { + let header = mem.as_mut_ptr().cast::<*const std::vec::Vec>(); + *header = Rc::into_raw(Rc::new(mem)); + header.wrapping_add(1) + } +} + +/// # Safety +/// +/// This function should only be called with a tuple created by `__quantum__rt__tuple_*` functions. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__tuple_copy( + raw_tup: *mut *const Vec, + force: bool, +) -> *mut *const Vec { + let rc = Rc::from_raw(*(raw_tup).wrapping_sub(1)); + if force || Rc::weak_count(&rc) > 0 { + let mut copy = rc.as_ref().clone(); + let _ = Rc::into_raw(rc); + let header = copy.as_mut_ptr().cast::<*const std::vec::Vec>(); + *header = Rc::into_raw(Rc::new(copy)); + header.wrapping_add(1) + } else { + let _ = Rc::into_raw(Rc::clone(&rc)); + let _ = Rc::into_raw(rc); + raw_tup + } +} + +/// # Safety +/// +/// This function should only be called with a tuple created by `__quantum__rt__tuple_*` functions. +/// If the reference count after update is less than or equal to zero, the tuple is be cleaned up +/// and the pointer is no longer be valid. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__tuple_update_reference_count( + raw_tup: *mut *const Vec, + update: i32, +) { + update_counts(*raw_tup.wrapping_sub(1), update, false); +} + +/// # Safety +/// +/// This function should only be called with a tuple created by `__quantum__rt__tuple_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__tuple_update_alias_count( + raw_tup: *mut *const Vec, + update: i32, +) { + update_counts(*raw_tup.wrapping_sub(1), update, true); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tuple_create() { + let tup = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + *tup.cast::() = 42; + __quantum__rt__tuple_update_reference_count(tup, -1); + } + } + + #[test] + fn test_tuple_update_reference_count() { + let tup = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + let rc = Rc::from_raw(*tup.cast::<*const std::vec::Vec>().wrapping_sub(1)); + assert_eq!(Rc::strong_count(&rc), 1); + __quantum__rt__tuple_update_reference_count(tup, 2); + assert_eq!(Rc::strong_count(&rc), 3); + __quantum__rt__tuple_update_reference_count(tup, -2); + assert_eq!(Rc::strong_count(&rc), 1); + let _ = Rc::into_raw(rc); + __quantum__rt__tuple_update_reference_count(tup, -1); + } + } + + #[test] + fn test_tuple_update_alias_count() { + let tup = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + let rc = Rc::from_raw(*tup.cast::<*const std::vec::Vec>().wrapping_sub(1)); + assert_eq!(Rc::strong_count(&rc), 1); + assert_eq!(Rc::weak_count(&rc), 0); + __quantum__rt__tuple_update_alias_count(tup, 2); + assert_eq!(Rc::weak_count(&rc), 2); + __quantum__rt__tuple_update_alias_count(tup, -2); + assert_eq!(Rc::weak_count(&rc), 0); + let _ = Rc::into_raw(rc); + __quantum__rt__tuple_update_reference_count(tup, -1); + } + } + + #[test] + fn test_tuple_copy() { + let tup1 = __quantum__rt__tuple_create(size_of::() as u64); + unsafe { + *tup1.cast::() = 42; + let tup2 = __quantum__rt__tuple_copy(tup1, false); + assert_eq!(tup2, tup1); + assert_eq!(*tup2.cast::(), 42); + __quantum__rt__tuple_update_reference_count(tup2, -1); + assert_eq!(*tup1.cast::(), 42); + let tup3 = __quantum__rt__tuple_copy(tup1, true); + assert_ne!(tup3, tup1); + assert_eq!(*tup3.cast::(), 42); + __quantum__rt__tuple_update_reference_count(tup3, -1); + assert_eq!(*tup1.cast::(), 42); + __quantum__rt__tuple_update_alias_count(tup1, 1); + let tup4 = __quantum__rt__tuple_copy(tup1, false); + assert_ne!(tup4, tup1); + assert_eq!(*tup4.cast::(), 42); + __quantum__rt__tuple_update_reference_count(tup4, -1); + assert_eq!(*tup1.cast::(), 42); + __quantum__rt__tuple_update_alias_count(tup1, -1); + __quantum__rt__tuple_update_reference_count(tup1, -1); + } + } +} diff --git a/src/Qir/riqir/test-qir-runtime.ps1 b/src/Qir/riqir/test-qir-runtime.ps1 new file mode 100644 index 00000000000..c56d9eced0e --- /dev/null +++ b/src/Qir/riqir/test-qir-runtime.ps1 @@ -0,0 +1,26 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Push-Location $PSScriptRoot +try { + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) + # for interoperating Rust and C. + # NB: CFG is only supported on Windows, but the Rust flag is supported on + # all platforms; it's ignored on platforms without CFG functionality. + $Env:RUSTFLAGS = "-C control-flow-guard"; + + # Actually run the test. + cargo test @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo test on QIR Runtime." } + + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; + } +} +finally { + Pop-Location +} \ No newline at end of file From 27465c743e1d5f08d00b1160b36af2d14b7104f8 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 23 Aug 2022 10:07:09 -0700 Subject: [PATCH 02/55] Remove old dependencies --- src/Qir/riqir/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Qir/riqir/Cargo.toml b/src/Qir/riqir/Cargo.toml index 40ba0c78b2b..df3da8a8e30 100644 --- a/src/Qir/riqir/Cargo.toml +++ b/src/Qir/riqir/Cargo.toml @@ -8,9 +8,5 @@ edition = "2021" num-bigint = { version = "0.4.3", default-features = false } rand = "0.8.5" -[build-dependencies] -cc = "1" -which = "4.2.2" - [lib] crate-type = ["cdylib","rlib"] From 84608872cbdafa37e129e40c553104e172bf1683 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 25 Aug 2022 13:31:15 -0700 Subject: [PATCH 03/55] Use --all-targets clippy, corresponding fixes --- .../rust-toolchain => rust-toolchain | 0 src/Qir/riqir/src/lib.rs | 5 +++-- src/Qir/riqir/src/strings.rs | 12 +++++----- .../qdk_sim_rs/build-qdk-sim-rs.ps1 | 2 +- .../src/linalg/decompositions/eig.rs | 7 ++++-- src/Simulation/qdk_sim_rs/src/math/sp_func.rs | 2 +- .../src/processes/generators/mod.rs | 22 ++++++++++++++++--- .../qdk_sim_rs/tests/serialization_tests.rs | 2 +- 8 files changed, 36 insertions(+), 16 deletions(-) rename src/Simulation/qdk_sim_rs/rust-toolchain => rust-toolchain (100%) diff --git a/src/Simulation/qdk_sim_rs/rust-toolchain b/rust-toolchain similarity index 100% rename from src/Simulation/qdk_sim_rs/rust-toolchain rename to rust-toolchain diff --git a/src/Qir/riqir/src/lib.rs b/src/Qir/riqir/src/lib.rs index d849cc0ad21..97d471d0565 100644 --- a/src/Qir/riqir/src/lib.rs +++ b/src/Qir/riqir/src/lib.rs @@ -7,6 +7,7 @@ pub mod arrays; pub mod bigints; pub mod callables; +pub mod conditionals; pub mod math; pub mod output_recording; pub mod range_support; @@ -77,7 +78,7 @@ pub extern "C" fn __quantum__rt__memory_allocate(size: u64) -> *mut u8 { /// Panics unconditionally with the given message. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) { - panic!("{}", (&*str).to_str().expect("Unable to convert string")); + panic!("{}", (*str).to_str().expect("Unable to convert string")); } /// # Safety @@ -85,7 +86,7 @@ pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) { /// This function should only be called with a string created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__message(str: *const CString) { - println!("{}", (&*str).to_str().expect("Unable to convert string")); + println!("{}", (*str).to_str().expect("Unable to convert string")); } #[cfg(test)] diff --git a/src/Qir/riqir/src/strings.rs b/src/Qir/riqir/src/strings.rs index 563f68a39ae..a8a0fa0b943 100644 --- a/src/Qir/riqir/src/strings.rs +++ b/src/Qir/riqir/src/strings.rs @@ -25,7 +25,7 @@ pub unsafe extern "C" fn __quantum__rt__string_create(str: *mut c_char) -> *cons /// This function should only be called with a string created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_get_data(str: *const CString) -> *const c_char { - (&*str).as_bytes_with_nul().as_ptr().cast::() + (*str).as_bytes_with_nul().as_ptr().cast::() } /// # Safety @@ -36,7 +36,7 @@ pub unsafe extern "C" fn __quantum__rt__string_get_data(str: *const CString) -> /// Will panic if length is larger than will fit in an 32-bit integer. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_get_length(str: *const CString) -> u32 { - (&*str).as_bytes().len().try_into().unwrap() + (*str).as_bytes().len().try_into().unwrap() } /// # Safety @@ -60,13 +60,13 @@ pub unsafe extern "C" fn __quantum__rt__string_concatenate( s1: *const CString, s2: *const CString, ) -> *const CString { - let mut new_str = (&*s1) + let mut new_str = (*s1) .clone() .into_string() .expect("Unable to convert string"); new_str.push_str( - (&*s2) + (*s2) .clone() .into_string() .expect("Unable to convert string") @@ -86,8 +86,8 @@ pub unsafe extern "C" fn __quantum__rt__string_equal( s1: *const CString, s2: *const CString, ) -> bool { - (&*s1).to_str().expect("Unable to convert string") - == (&*s2).to_str().expect("Unable to convert string") + (*s1).to_str().expect("Unable to convert string") + == (*s2).to_str().expect("Unable to convert string") } pub(crate) fn convert(input: &T) -> *const CString diff --git a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 index 303e3e30fe7..5901bb1c913 100644 --- a/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 +++ b/src/Simulation/qdk_sim_rs/build-qdk-sim-rs.ps1 @@ -14,7 +14,7 @@ Push-Location $PSScriptRoot # If there's a false positive, that check should be explicitly disabled # at the point where the false positive occurs with an explanation as to # why it's OK. - cargo clippy -- -D warnings + cargo clippy --all-targets -- -D warnings $script:allOk = $script:allOk -and $LASTEXITCODE -eq 0; $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); diff --git a/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs b/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs index 5049d78d51b..5b34f06a724 100644 --- a/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs +++ b/src/Simulation/qdk_sim_rs/src/linalg/decompositions/eig.rs @@ -118,12 +118,14 @@ mod tests { use cauchy::c64; use super::{EigenvalueDecomposition, ExplicitEigenvalueDecomposition}; + use core::f64::consts::FRAC_1_SQRT_2; #[test] pub fn ident_applies_to_eig_x() -> Result<(), Box> { let eigs: ExplicitEigenvalueDecomposition = ExplicitEigenvalueDecomposition { values: array![c64!(-1.0), c64!(1.0)], - vectors: c64!(0.70710678) * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], + vectors: c64::new(FRAC_1_SQRT_2, 0.0) + * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], }; let actual = eigs.apply_mtx_fn(|x| *x)?; let expected = array![[c64!(0.0), c64!(1.0)], [c64!(1.0), c64!(0.0),],]; @@ -137,7 +139,8 @@ mod tests { pub fn mtx_fn_applies_to_eig_x() -> Result<(), Box> { let eigs: ExplicitEigenvalueDecomposition = ExplicitEigenvalueDecomposition { values: array![c64!(-1.0), c64!(1.0)], - vectors: c64!(0.70710678) * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], + vectors: c64::new(FRAC_1_SQRT_2, 0.0) + * array![[c64!(1.0), c64!(-1.0)], [c64!(1.0), c64!(1.0)],], }; let actual = eigs.apply_mtx_fn(|x| (c64!(-1.0 i) * x).exp())?; let expected = array![ diff --git a/src/Simulation/qdk_sim_rs/src/math/sp_func.rs b/src/Simulation/qdk_sim_rs/src/math/sp_func.rs index 9a6b19ce26f..815db2e2111 100644 --- a/src/Simulation/qdk_sim_rs/src/math/sp_func.rs +++ b/src/Simulation/qdk_sim_rs/src/math/sp_func.rs @@ -55,7 +55,7 @@ mod tests { // We expect approximate_factorial(123.0) to return 123! ≈ 1.214 × 10²⁰⁵. // https://www.wolframalpha.com/input?i=123%21 - let expected = 1.214630436702533e+205; + let expected = 1.214630436702533e205; assert_abs_diff_eq!( approximate_factorial::(123.0), expected, diff --git a/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs b/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs index 4cd5c1d90a6..f8e8a6cd5cc 100644 --- a/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs +++ b/src/Simulation/qdk_sim_rs/src/processes/generators/mod.rs @@ -144,6 +144,7 @@ impl From for GeneratorCoset { #[cfg(test)] mod tests { use approx::assert_abs_diff_eq; + use core::f64::consts::FRAC_1_SQRT_2; use crate::{c64, ProcessData, VariantName}; @@ -254,8 +255,18 @@ mod tests { vectors: array![ [c64!(0.0), c64!(1.0), c64!(0.0), c64!(0.0)], [c64!(0.0), c64!(0.0), c64!(1.0), c64!(0.0)], - [c64!(-0.70710678), c64!(0.0), c64!(0.0), c64!(0.70710678)], - [c64!(0.70710678), c64!(0.0), c64!(0.0), c64!(0.70710678)], + [ + c64::new(-FRAC_1_SQRT_2, 0.0), + c64!(0.0), + c64!(0.0), + c64::new(FRAC_1_SQRT_2, 0.0) + ], + [ + c64::new(FRAC_1_SQRT_2, 0.0), + c64!(0.0), + c64!(0.0), + c64::new(FRAC_1_SQRT_2, 0.0) + ], ], }, } @@ -312,7 +323,12 @@ mod tests { vectors: array![ [c64!(0.0), c64!(1.0), c64!(0.0), c64!(0.0)], [c64!(0.0), c64!(0.0), c64!(1.0), c64!(0.0)], - [c64!(-0.70710678), c64!(0.0), c64!(0.0), c64!(0.70710678)], + [ + c64::new(-FRAC_1_SQRT_2, 0.0), + c64!(0.0), + c64!(0.0), + c64::new(FRAC_1_SQRT_2, 0.0) + ], ], }, } diff --git a/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs index 6253b47f2c5..78162d7d4bd 100644 --- a/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs +++ b/src/Simulation/qdk_sim_rs/tests/serialization_tests.rs @@ -10,7 +10,7 @@ static IDEAL_NOISE_MODEL_JSON: &str = include_str!("data/ideal-noise-model.json" fn ideal_noise_model_serializes_correctly() { let noise_model = NoiseModel::ideal(); let expected: Value = - serde_json::from_str(&*(serde_json::to_string(&noise_model).unwrap())).unwrap(); + serde_json::from_str(&(serde_json::to_string(&noise_model).unwrap())).unwrap(); assert_json_eq!(noise_model, expected); } From 297ff7eedec038f97cd785f9667d21534572c2e5 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 25 Aug 2022 13:31:52 -0700 Subject: [PATCH 04/55] Add conditionals instrinc support, result_to_string --- src/Qir/riqir/src/conditionals.rs | 64 +++++++++++++++++++++++++++++++ src/Qir/riqir/src/result_bool.rs | 21 +++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/Qir/riqir/src/conditionals.rs diff --git a/src/Qir/riqir/src/conditionals.rs b/src/Qir/riqir/src/conditionals.rs new file mode 100644 index 00000000000..cceefa32872 --- /dev/null +++ b/src/Qir/riqir/src/conditionals.rs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] + +use crate::{ + __quantum__rt__fail, + arrays::{__quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, + callables::{Callable, __quantum__rt__callable_invoke}, + result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one}, + strings::convert, +}; +use std::{ffi::c_void, ptr::null_mut}; + +/// # Safety +/// +/// This function should only be called with results and callables generated by this runtime. +#[no_mangle] +pub unsafe extern "C" fn __quantum__qis__applyifelseintrinsic__body( + result: *mut c_void, + true_callable: *const Callable, + false_callable: *const Callable, +) { + __quantum__rt__callable_invoke( + if __quantum__rt__result_equal(result, __quantum__rt__result_get_one()) { + true_callable + } else { + false_callable + }, + null_mut(), + null_mut(), + ); +} + +/// # Safety +/// +/// This function should only be called with arrays of results and callables generated by this runtime. +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn __quantum__qis__applyconditionallyintrinsic__body( + results: *const (usize, Vec), + expected: *const (usize, Vec), + true_callable: *const Callable, + false_callable: *const Callable, +) { + let result_len = __quantum__rt__array_get_size_1d(results); + if result_len != __quantum__rt__array_get_size_1d(expected) { + __quantum__rt__fail(convert( + &"Invalid Argument: expected and actual result arrays must have the same size." + .to_string(), + )); + } + + for index in 0..result_len { + let res = *__quantum__rt__array_get_element_ptr_1d(results, index).cast::<*mut c_void>(); + let expect = + *__quantum__rt__array_get_element_ptr_1d(expected, index).cast::<*mut c_void>(); + if !__quantum__rt__result_equal(res, expect) { + // We've found a mismatch, so invoke false and early return. + return __quantum__rt__callable_invoke(false_callable, null_mut(), null_mut()); + } + } + + __quantum__rt__callable_invoke(true_callable, null_mut(), null_mut()); +} diff --git a/src/Qir/riqir/src/result_bool.rs b/src/Qir/riqir/src/result_bool.rs index e45aaa5f69c..cbaf1d4faeb 100644 --- a/src/Qir/riqir/src/result_bool.rs +++ b/src/Qir/riqir/src/result_bool.rs @@ -2,7 +2,8 @@ // Licensed under the MIT License. #![deny(clippy::all, clippy::pedantic)] -use std::ffi::c_void; +use crate::strings::__quantum__rt__string_create; +use std::ffi::{c_void, CString}; #[no_mangle] pub extern "C" fn __quantum__rt__result_get_zero() -> *mut c_void { @@ -23,3 +24,21 @@ pub extern "C" fn __quantum__rt__result_equal(r1: *mut c_void, r2: *mut c_void) pub extern "C" fn __quantum__rt__result_update_reference_count(_res: *mut c_void, _update: i32) { // no-op } + +#[no_mangle] +pub extern "C" fn __quantum__rt__result_to_string(res: *mut c_void) -> *const CString { + unsafe { + __quantum__rt__string_create( + CString::new( + if __quantum__rt__result_equal(res, __quantum__rt__result_get_one()) { + "1" + } else { + "0" + }, + ) + .expect("Failed to allocate memory for result string.") + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ) + } +} From 826287cfbbef02988635cb2ca9b6aea7357f2cec Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 26 Aug 2022 13:34:28 -0700 Subject: [PATCH 05/55] Add QIR simulator support --- src/Qir/riqir/Cargo.toml | 13 + src/Qir/riqir/src/lib.rs | 5 +- src/Qir/riqir/src/sim/common_matrices.rs | 280 ++++++++ src/Qir/riqir/src/sim/mod.rs | 879 +++++++++++++++++++++++ src/Qir/riqir/src/sim/simulator.rs | 687 ++++++++++++++++++ src/Qir/riqir/src/sim/sparsestate.rs | 268 +++++++ 6 files changed, 2131 insertions(+), 1 deletion(-) create mode 100644 src/Qir/riqir/src/sim/common_matrices.rs create mode 100644 src/Qir/riqir/src/sim/mod.rs create mode 100644 src/Qir/riqir/src/sim/simulator.rs create mode 100644 src/Qir/riqir/src/sim/sparsestate.rs diff --git a/src/Qir/riqir/Cargo.toml b/src/Qir/riqir/Cargo.toml index df3da8a8e30..edcc7b31566 100644 --- a/src/Qir/riqir/Cargo.toml +++ b/src/Qir/riqir/Cargo.toml @@ -8,5 +8,18 @@ edition = "2021" num-bigint = { version = "0.4.3", default-features = false } rand = "0.8.5" +rustc-hash = {version = "1.1.0", optional = true } +num-complex = {version = "0.4", optional = true } +num-traits = {version = "0.2.14", optional = true } +rayon = {version = "1.5.1", optional = true } +ndarray = { version = "0.15.4", features = [ "rayon", "matrixmultiply-threading" ], optional = true } +mimalloc = { version = "0.1.27", default-features = false, optional = true } +lazy_static = {version = "1.4.0", optional = true } +bitvec = {version = "1.0.0", optional = true } + [lib] crate-type = ["cdylib","rlib"] + +[features] +default = ["sim"] +sim = ["rustc-hash", "num-complex", "num-traits", "rayon", "ndarray", "lazy_static", "bitvec"] \ No newline at end of file diff --git a/src/Qir/riqir/src/lib.rs b/src/Qir/riqir/src/lib.rs index 97d471d0565..5b70e153d91 100644 --- a/src/Qir/riqir/src/lib.rs +++ b/src/Qir/riqir/src/lib.rs @@ -15,6 +15,9 @@ pub mod result_bool; pub mod strings; pub mod tuples; +#[cfg(feature = "sim")] +pub mod sim; + use std::{ ffi::CString, rc::{Rc, Weak}, @@ -53,7 +56,7 @@ unsafe fn update_counts(raw_rc: *const T, update: i32, is_alias: bool) { } } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] #[repr(i8)] pub enum Pauli { I = 0, diff --git a/src/Qir/riqir/src/sim/common_matrices.rs b/src/Qir/riqir/src/sim/common_matrices.rs new file mode 100644 index 00000000000..548e4fd2f55 --- /dev/null +++ b/src/Qir/riqir/src/sim/common_matrices.rs @@ -0,0 +1,280 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use core::f64::consts::FRAC_1_SQRT_2; +use ndarray::{array, s, Array2}; +use num_complex::Complex64; +use num_traits::{One, Zero}; + +/// Returns a unitary matrix representing the `X` operation. +#[must_use] +pub fn x() -> Array2 { + array![ + [Complex64::zero(), Complex64::one()], + [Complex64::one(), Complex64::zero()] + ] +} + +/// Returns a unitary matrix representing the `Y` operation. +#[must_use] +pub fn y() -> Array2 { + array![ + [Complex64::zero(), -Complex64::i()], + [Complex64::i(), Complex64::zero()] + ] +} + +/// Returns a unitary matrix representing the `Z` operation. +#[must_use] +pub fn z() -> Array2 { + array![ + [Complex64::one(), Complex64::zero()], + [Complex64::zero(), -Complex64::one()] + ] +} + +/// Returns a unitary matrix representing the single-qubit Hadamard transformation. +#[must_use] +pub fn h() -> Array2 { + array![ + [Complex64::one(), Complex64::one()], + [Complex64::one(), -Complex64::one()] + ] * FRAC_1_SQRT_2 +} + +/// Returns a unitary matrix representing the `T` operation. +#[must_use] +pub fn t() -> Array2 { + array![ + [Complex64::one(), Complex64::zero()], + [ + Complex64::zero(), + Complex64::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2) + ] + ] +} + +/// Returns a unitary matrix representing the `S` operation. +#[must_use] +pub fn s() -> Array2 { + array![ + [Complex64::one(), Complex64::zero()], + [Complex64::zero(), Complex64::new(0.0_f64, 1.0_f64)] + ] +} + +/// Returns a unitary matrix representing the `Rx` operation with the given angle. +#[must_use] +pub fn rx(theta: f64) -> Array2 { + let cos_theta = f64::cos(theta / 2.0); + let sin_theta = f64::sin(theta / 2.0); + array![ + [ + Complex64::new(cos_theta, 0.0), + Complex64::new(0.0, -sin_theta) + ], + [ + Complex64::new(0.0, -sin_theta), + Complex64::new(cos_theta, 0.0) + ] + ] +} + +/// Returns a unitary matrix representing the `Ry` operation with the given angle. +#[must_use] +pub fn ry(theta: f64) -> Array2 { + let cos_theta = f64::cos(theta / 2.0); + let sin_theta = f64::sin(theta / 2.0); + array![ + [ + Complex64::new(cos_theta, 0.0), + Complex64::new(-sin_theta, 0.0) + ], + [ + Complex64::new(sin_theta, 0.0), + Complex64::new(cos_theta, 0.0) + ] + ] +} + +/// Returns a unitary matrix representing the `Rz` operation with the given angle. +#[must_use] +pub fn rz(theta: f64) -> Array2 { + let exp_theta = Complex64::exp(Complex64::new(0.0, theta / 2.0)); + let neg_exp_theta = Complex64::exp(Complex64::new(0.0, -theta / 2.0)); + array![ + [neg_exp_theta, Complex64::zero()], + [Complex64::zero(), exp_theta] + ] +} + +/// Returns a unitary matrix representing the `G` or `GlobalPhase` operation with the given angle. +#[must_use] +pub fn g(theta: f64) -> Array2 { + let neg_exp_theta = Complex64::exp(Complex64::new(0.0, -theta / 2.0)); + array![ + [neg_exp_theta, Complex64::zero()], + [Complex64::zero(), neg_exp_theta] + ] +} + +/// Returns a unitary matrix representing the `SWAP` operation. +#[must_use] +pub fn swap() -> Array2 { + array![ + [ + Complex64::one(), + Complex64::zero(), + Complex64::zero(), + Complex64::zero() + ], + [ + Complex64::zero(), + Complex64::zero(), + Complex64::one(), + Complex64::zero() + ], + [ + Complex64::zero(), + Complex64::one(), + Complex64::zero(), + Complex64::zero() + ], + [ + Complex64::zero(), + Complex64::zero(), + Complex64::zero(), + Complex64::one() + ] + ] +} + +/// Transforms the given matrix into it's adjoint using the transpose of the complex conjugate. +#[must_use] +pub fn adjoint(u: &Array2) -> Array2 { + u.t().map(Complex64::conj) +} + +/// Extends the given unitary matrix into a matrix corresponding to the same unitary with a given number of controls +/// by inserting it into an identity matrix. +#[must_use] +pub fn controlled(u: &Array2, num_ctrls: u32) -> Array2 { + let mut controlled_u = Array2::eye(u.nrows() * 2_usize.pow(num_ctrls)); + controlled_u + .slice_mut(s![ + (controlled_u.nrows() - u.nrows()).., + (controlled_u.ncols() - u.ncols()).. + ]) + .assign(u); + controlled_u +} + +#[cfg(test)] +mod tests { + use super::*; + use core::f64::consts::PI; + + fn is_self_adjoint(arr: &Array2) -> bool { + arr == adjoint(arr) + } + + fn are_equal_to_precision(actual: Array2, expected: Array2) -> bool { + // If we use assert_eq here, we'll get bitten by finite precision. + // We also can't use LAPACK, since that greatly complicates bindings, + // so we do an ad hoc implementation here. + (actual - expected).map(|x| x.norm()).sum() <= 1e-10 + } + + #[test] + fn h_is_self_adjoint() { + assert!(is_self_adjoint(&h())); + } + + #[test] + fn x_is_self_adjoint() { + assert!(is_self_adjoint(&x())); + } + + #[test] + fn y_is_self_adjoint() { + assert!(is_self_adjoint(&y())); + } + + #[test] + fn z_is_self_adjoint() { + assert!(is_self_adjoint(&z())); + } + + #[test] + fn swap_is_self_adjoint() { + assert!(is_self_adjoint(&swap())); + } + + #[test] + fn s_squares_to_z() { + assert_eq!(s().dot(&s()), z()); + } + + #[test] + fn t_squares_to_s() { + assert!(are_equal_to_precision(t().dot(&t()), s())); + } + + #[test] + fn rx_pi_is_x() { + assert!(are_equal_to_precision(Complex64::i() * rx(PI), x())); + } + + #[test] + fn ry_pi_is_y() { + assert!(are_equal_to_precision(Complex64::i() * ry(PI), y())); + } + + #[test] + fn rz_pi_is_z() { + assert!(are_equal_to_precision(Complex64::i() * rz(PI), z())); + } + + #[test] + fn gate_multiplication() { + assert!(are_equal_to_precision(x().dot(&y()), Complex64::i() * z())); + } + + #[test] + fn controlled_extension() { + fn cnot() -> Array2 { + array![ + [ + Complex64::one(), + Complex64::zero(), + Complex64::zero(), + Complex64::zero() + ], + [ + Complex64::zero(), + Complex64::one(), + Complex64::zero(), + Complex64::zero() + ], + [ + Complex64::zero(), + Complex64::zero(), + Complex64::zero(), + Complex64::one() + ], + [ + Complex64::zero(), + Complex64::zero(), + Complex64::one(), + Complex64::zero() + ] + ] + } + assert!(are_equal_to_precision(controlled(&x(), 1), cnot())); + assert!(are_equal_to_precision( + controlled(&x(), 2), + controlled(&cnot(), 1) + )); + assert_eq!(controlled(&x(), 3).nrows(), 2_usize.pow(4)); + } +} diff --git a/src/Qir/riqir/src/sim/mod.rs b/src/Qir/riqir/src/sim/mod.rs new file mode 100644 index 00000000000..a5d21bd5ece --- /dev/null +++ b/src/Qir/riqir/src/sim/mod.rs @@ -0,0 +1,879 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Module defining QIR compliant APIs for quantum simulation. + +mod common_matrices; +mod simulator; +mod sparsestate; + +use bitvec::prelude::*; +use lazy_static::lazy_static; +use sparsestate::SparseStateQuantumSim; +use std::convert::TryInto; +use std::ffi::{c_void, CString}; +use std::mem::size_of; +use std::os::raw::c_double; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::sync::Mutex; + +use super::{ + Pauli, __quantum__rt__fail, + arrays::{ + __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d, + __quantum__rt__array_get_size_1d, __quantum__rt__array_update_alias_count, + }, + result_bool::{ + __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero, + }, + strings::__quantum__rt__string_create, + tuples::{__quantum__rt__tuple_create, __quantum__rt__tuple_update_reference_count}, +}; + +lazy_static! { + static ref SIM: Mutex> = Mutex::new(None); + static ref RESULTS: Mutex = Mutex::new(bitvec![]); + static ref MAX_QUBIT_ID: AtomicUsize = AtomicUsize::new(0); +} + +fn ensure_sufficient_qubits(sim: &mut SparseStateQuantumSim, qubit_id: usize) { + while qubit_id + 1 > (*MAX_QUBIT_ID).load(Relaxed) { + let _ = sim.allocate(); + (*MAX_QUBIT_ID).fetch_add(1, Relaxed); + } +} + +#[allow(clippy::cast_ptr_alignment)] +unsafe fn map_paulis( + sim: &mut SparseStateQuantumSim, + paulis: *const (usize, Vec), + qubits: *const (usize, Vec), +) -> Vec<(Pauli, usize)> { + let paulis_size = __quantum__rt__array_get_size_1d(paulis); + let qubits_size = __quantum__rt__array_get_size_1d(qubits); + if paulis_size != qubits_size { + __quantum__rt__fail(__quantum__rt__string_create( + CString::new("Pauli array and Qubit array for Measurement must be the same size.") + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + )); + } + + let combined_list: Vec<(Pauli, usize)> = (0..paulis_size) + .filter_map(|index| { + let p = + *__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::() as Pauli; + let q = *__quantum__rt__array_get_element_ptr_1d(qubits, index as u64) + .cast::<*mut c_void>() as usize; + if let Pauli::I = p { + None + } else { + ensure_sufficient_qubits(sim, q); + Some((p, q)) + } + }) + .collect(); + + for (pauli, qubit) in &combined_list { + match pauli { + Pauli::X => sim.apply(&common_matrices::h(), &[*qubit], None), + Pauli::Y => { + sim.apply(&common_matrices::h(), &[*qubit], None); + sim.apply(&common_matrices::s(), &[*qubit], None); + sim.apply(&common_matrices::h(), &[*qubit], None); + } + _ => (), + } + } + + combined_list +} + +fn unmap_paulis(sim: &mut SparseStateQuantumSim, combined_list: Vec<(Pauli, usize)>) { + for (pauli, qubit) in combined_list { + match pauli { + Pauli::X => sim.apply(&common_matrices::h(), &[qubit], None), + Pauli::Y => { + sim.apply(&common_matrices::h(), &[qubit], None); + sim.apply( + &common_matrices::adjoint(&common_matrices::s()), + &[qubit], + None, + ); + sim.apply(&common_matrices::h(), &[qubit], None); + } + _ => (), + } + } +} + +macro_rules! single_qubit_gate { + ($(#[$meta:meta])* + $qir_name:ident, $gate_matrix:expr) => { + $(#[$meta])* + #[no_mangle] + pub extern "C" fn $qir_name(qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + + sim.apply(&$gate_matrix, &[qubit as usize], None); + + *sim_lock = Some(sim); + } + }; +} + +single_qubit_gate!( + /// QIR API for performing the H gate on the given qubit. + __quantum__qis__h__body, + common_matrices::h() +); +single_qubit_gate!( + /// QIR API for performing the S gate on the given qubit. + __quantum__qis__s__body, + common_matrices::s() +); +single_qubit_gate!( + /// QIR API for performing the Adjoint S gate on the given qubit. + __quantum__qis__s__adj, + common_matrices::adjoint(&common_matrices::s()) +); +single_qubit_gate!( + /// QIR API for performing the T gate on the given qubit. + __quantum__qis__t__body, + common_matrices::t() +); +single_qubit_gate!( + /// QIR API for performing the Adjoint T gate on the given qubit. + __quantum__qis__t__adj, + common_matrices::adjoint(&common_matrices::t()) +); +single_qubit_gate!( + /// QIR API for performing the X gate on the given qubit. + __quantum__qis__x__body, + common_matrices::x() +); +single_qubit_gate!( + /// QIR API for performing the Y gate on the given qubit. + __quantum__qis__y__body, + common_matrices::y() +); +single_qubit_gate!( + /// QIR API for performing the Z gate on the given qubit. + __quantum__qis__z__body, + common_matrices::z() +); + +macro_rules! controlled_qubit_gate { + ($(#[$meta:meta])* + $qir_name:ident, $gate_matrix:expr, 1) => { + $(#[$meta])* + #[no_mangle] + pub extern "C" fn $qir_name(control: *mut c_void, target: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, target as usize); + ensure_sufficient_qubits(&mut sim, control as usize); + + sim.apply(&$gate_matrix, &[target as usize], Some(&[control as usize])); + + *sim_lock = Some(sim); + } + }; + ($(#[$meta:meta])* + $qir_name:ident, $gate_matrix:expr, 2) => { + $(#[$meta])* + #[no_mangle] + pub extern "C" fn $qir_name( + control_1: *mut c_void, + control_2: *mut c_void, + target: *mut c_void, + ) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, target as usize); + ensure_sufficient_qubits(&mut sim, control_1 as usize); + ensure_sufficient_qubits(&mut sim, control_2 as usize); + + sim.apply( + &$gate_matrix, + &[target as usize], + Some(&[control_1 as usize, control_2 as usize]), + ); + + *sim_lock = Some(sim); + } + }; +} + +controlled_qubit_gate!( + /// QIR API for performing the CNOT gate with the given qubits. + __quantum__qis__cnot__body, + common_matrices::x(), + 1 +); +controlled_qubit_gate!( + /// QIR API for performing the CX gate with the given qubits. + __quantum__qis__cx__body, + common_matrices::x(), + 1 +); +controlled_qubit_gate!( + /// QIR API for performing the CCX gate with the given qubits. + __quantum__qis__ccx__body, + common_matrices::x(), + 2 +); +controlled_qubit_gate!( + /// QIR API for performing the CZ gate with the given qubits. + __quantum__qis__cz__body, + common_matrices::z(), + 1 +); + +macro_rules! single_qubit_rotation { + ($(#[$meta:meta])* + $qir_name:ident, $gate_matrix:expr) => { + $(#[$meta])* + #[no_mangle] + pub extern "C" fn $qir_name(theta: c_double, qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + + sim.apply(&$gate_matrix(theta), &[qubit as usize], None); + + *sim_lock = Some(sim); + } + }; +} + +single_qubit_rotation!( + /// QIR API for applying a global phase with the given angle and qubit. + __quantum__qis__ri__body, + common_matrices::g +); +single_qubit_rotation!( + /// QIR API for applying a Pauli-X rotation with the given angle and qubit. + __quantum__qis__rx__body, + common_matrices::rx +); +single_qubit_rotation!( + /// QIR API for applying a Pauli-Y rotation with the given angle and qubit. + __quantum__qis__ry__body, + common_matrices::ry +); +single_qubit_rotation!( + /// QIR API for applying a Pauli-Z rotation with the given angle and qubit. + __quantum__qis__rz__body, + common_matrices::rz +); + +macro_rules! multicontrolled_qubit_gate { + ($(#[$meta:meta])* + $qir_name:ident, $gate_matrix:expr) => { + $(#[$meta])* + /// # Safety + /// + /// This function should only be called with arrays and tuples created by the QIR runtime library. + #[no_mangle] + pub unsafe extern "C" fn $qir_name(ctls: *const (usize, Vec), qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + let ctls_size = __quantum__rt__array_get_size_1d(ctls); + let ctls_list: Vec = (0..ctls_size) + .map(|index| { + let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index) + .cast::<*mut c_void>() as usize; + ensure_sufficient_qubits(&mut sim, q); + q + }) + .collect(); + + sim.apply(&$gate_matrix, &[qubit as usize], Some(&ctls_list)); + + *sim_lock = Some(sim); + } + }; +} + +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled H gate with the given qubits. + __quantum__qis__h__ctl, + common_matrices::h() +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled S gate with the given qubits. + __quantum__qis__s__ctl, + common_matrices::s() +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled Adjoint S gate with the given qubits. + __quantum__qis__s__ctladj, + common_matrices::adjoint(&common_matrices::s()) +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled T gate with the given qubits. + __quantum__qis__t__ctl, + common_matrices::t() +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled Adjoint T gate with the given qubits. + __quantum__qis__t__ctladj, + common_matrices::adjoint(&common_matrices::t()) +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled X gate with the given qubits. + __quantum__qis__x__ctl, + common_matrices::x() +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled Y gate with the given qubits. + __quantum__qis__y__ctl, + common_matrices::y() +); +multicontrolled_qubit_gate!( + /// QIR API for performing the multicontrolled Z gate with the given qubits. + __quantum__qis__z__ctl, + common_matrices::z() +); + +#[derive(Copy, Clone)] +#[repr(C)] +struct RotationArgs { + theta: c_double, + qubit: *mut c_void, +} + +macro_rules! multicontrolled_qubit_rotation { + ($(#[$meta:meta])* + $qir_name:ident, $gate_matrix:expr) => { + $(#[$meta])* + /// # Safety + /// + /// This function should only be called with arrays and tuples created by the QIR runtime library. + #[no_mangle] + pub unsafe extern "C" fn $qir_name( + ctls: *const (usize, Vec), + arg_tuple: *mut *const Vec, + ) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + + let args = *arg_tuple.cast::(); + + ensure_sufficient_qubits(&mut sim, args.qubit as usize); + let ctls_size = __quantum__rt__array_get_size_1d(ctls); + let ctls_list: Vec = (0..ctls_size) + .map(|index| { + let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index) + .cast::<*mut c_void>() as usize; + ensure_sufficient_qubits(&mut sim, q); + q + }) + .collect(); + + sim.apply( + &$gate_matrix(args.theta), + &[args.qubit as usize], + Some(&ctls_list), + ); + + *sim_lock = Some(sim); + } + }; +} + +multicontrolled_qubit_rotation!( + /// QIR API for applying a multicontrolled global phase rotation with the given angle and qubit. + __quantum__qis__ri__ctl, + common_matrices::g +); +multicontrolled_qubit_rotation!( + /// QIR API for applying a multicontrolled Pauli-X rotation with the given angle and qubit. + __quantum__qis__rx__ctl, + common_matrices::rx +); +multicontrolled_qubit_rotation!( + /// QIR API for applying a multicontrolled Pauli-Y rotation with the given angle and qubit. + __quantum__qis__ry__ctl, + common_matrices::ry +); +multicontrolled_qubit_rotation!( + /// QIR API for applying a multicontrolled Pauli-Z rotation with the given angle and qubit. + __quantum__qis__rz__ctl, + common_matrices::rz +); + +/// QIR API for applying a rotation about the given Pauli axis with the given angle and qubit. +#[no_mangle] +pub extern "C" fn __quantum__qis__r__body(pauli: Pauli, theta: c_double, qubit: *mut c_void) { + match pauli { + Pauli::I => __quantum__qis__ri__body(theta, qubit), + Pauli::X => __quantum__qis__rx__body(theta, qubit), + Pauli::Y => __quantum__qis__ry__body(theta, qubit), + Pauli::Z => __quantum__qis__rz__body(theta, qubit), + } +} + +/// QIR API for applying an adjoint rotation about the given Pauli axis with the given angle and qubit. +#[no_mangle] +pub extern "C" fn __quantum__qis__r__adj(pauli: Pauli, theta: c_double, qubit: *mut c_void) { + __quantum__qis__r__body(pauli, -theta, qubit); +} + +#[derive(Copy, Clone)] +#[repr(C)] +struct PauliRotationArgs { + pauli: Pauli, + theta: c_double, + qubit: *mut c_void, +} + +/// QIR API for applying a controlled rotation about the given Pauli axis with the given angle and qubit. +/// # Safety +/// +/// This function should only be called with arrays and tuples created by the QIR runtime library. +/// # Panics +/// +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn __quantum__qis__r__ctl( + ctls: *const (usize, Vec), + arg_tuple: *mut *const Vec, +) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + + let args = *arg_tuple.cast::(); + + ensure_sufficient_qubits(&mut sim, args.qubit as usize); + let ctls_size = __quantum__rt__array_get_size_1d(ctls); + let ctls_list: Vec = (0..ctls_size) + .map(|index| { + let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index).cast::<*mut c_void>() + as usize; + ensure_sufficient_qubits(&mut sim, q); + q + }) + .collect(); + + sim.apply( + &(match args.pauli { + Pauli::I => common_matrices::g, + Pauli::X => common_matrices::rx, + Pauli::Y => common_matrices::ry, + Pauli::Z => common_matrices::rz, + })(args.theta), + &[args.qubit as usize], + Some(&ctls_list), + ); + + *sim_lock = Some(sim); +} + +/// QIR API for applying an adjoint controlled rotation about the given Pauli axis with the given angle and qubit. +/// # Safety +/// +/// This function should only be called with arrays and tuples created by the QIR runtime library. +#[no_mangle] +pub unsafe extern "C" fn __quantum__qis__r__ctladj( + ctls: *const (usize, Vec), + arg_tuple: *mut *const Vec, +) { + let args = *arg_tuple.cast::(); + let new_args = PauliRotationArgs { + pauli: args.pauli, + theta: -args.theta, + qubit: args.qubit, + }; + let new_arg_tuple = __quantum__rt__tuple_create(size_of::() as u64); + *new_arg_tuple.cast::() = new_args; + __quantum__qis__r__ctl(ctls, new_arg_tuple); + __quantum__rt__tuple_update_reference_count(new_arg_tuple, -1); +} + +/// QIR API for applying a SWAP gate to the given qubits. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__qis__swap__body(qubit0: *mut c_void, qubit1: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, qubit0 as usize); + ensure_sufficient_qubits(&mut sim, qubit1 as usize); + + sim.apply( + &common_matrices::swap(), + &[qubit0 as usize, qubit1 as usize], + None, + ); + + *sim_lock = Some(sim); +} + +/// QIR API for resetting the given qubit in the computational basis. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + + if sim.measure(qubit as usize) { + sim.apply(&common_matrices::x(), &[qubit as usize], None); + } + + *sim_lock = Some(sim); +} + +/// QIR API for measuring the given qubit in the computation basis and storing the measured value with the given result identifier. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__qis__mz__body(qubit: *mut c_void, result: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut res = RESULTS.lock().unwrap(); + let res_id = result as usize; + ensure_sufficient_qubits(&mut sim, qubit as usize); + + if res.len() < res_id + 1 { + res.resize(res_id + 1, false); + } + + *res.get_mut(res_id).unwrap() = sim.measure(qubit as usize); + + *sim_lock = Some(sim); +} + +/// QIR API that reads the Boolean value corresponding to the given result identifier, where true +/// indicates a |1⟩ state and false indicates a |0⟩ state. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__qis__read_result__body(result: *mut c_void) -> bool { + let mut res = RESULTS.lock().unwrap(); + let res_id = result as usize; + if res.len() < res_id + 1 { + res.resize(res_id + 1, false); + } + + let b = *res.get(res_id).unwrap(); + b +} + +/// QIR API that measures a given qubit in the computational basis, returning a runtime managed result value. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__qis__m__body(qubit: *mut c_void) -> *mut c_void { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + + let res = sim.measure(qubit as usize); + + *sim_lock = Some(sim); + if res { + __quantum__rt__result_get_one() + } else { + __quantum__rt__result_get_zero() + } +} + +/// QIR API that performs joint measurement of the given qubits in the corresponding Pauli bases, returning the parity as a runtime managed result value. +/// # Safety +/// +/// This function should only be called with arrays created by the QIR runtime library. +/// # Panics +/// +/// This function will panic if the provided paulis and qubits arrays are not of the same size. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn __quantum__qis__measure__body( + paulis: *const (usize, Vec), + qubits: *const (usize, Vec), +) -> *mut c_void { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + + let combined_list = map_paulis(&mut sim, paulis, qubits); + + let res = sim.joint_measure( + &combined_list + .iter() + .map(|(_, q)| *q) + .collect::>(), + ); + + unmap_paulis(&mut sim, combined_list); + + *sim_lock = Some(sim); + if res { + __quantum__rt__result_get_one() + } else { + __quantum__rt__result_get_zero() + } +} + +/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result +/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance. +/// # Safety +/// +/// This function should only be called with arrays created by the QIR runtime library. +#[no_mangle] +pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body( + paulis: *const (usize, Vec), + qubits: *const (usize, Vec), + result: *mut c_void, + prob: c_double, + msg: *const CString, + tol: c_double, +) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + + let combined_list = map_paulis(&mut sim, paulis, qubits); + + let mut actual_prob = sim.joint_probability( + &combined_list + .iter() + .map(|(_, q)| *q) + .collect::>(), + ); + + if __quantum__rt__result_equal(result, __quantum__rt__result_get_zero()) { + actual_prob = 1.0 - actual_prob; + } + + if (actual_prob - (prob as f64)).abs() > tol as f64 { + __quantum__rt__fail(msg); + } + + unmap_paulis(&mut sim, combined_list); + + *sim_lock = Some(sim); +} + +#[derive(Copy, Clone)] +#[repr(C)] +struct AssertMeasurementProbabilityArgs { + paulis: *const (usize, Vec), + qubits: *const (usize, Vec), + result: *mut c_void, + prob: c_double, + msg: *const CString, + tol: c_double, +} + +/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result +/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance. +/// Note that control qubits are ignored. +/// # Safety +/// +/// This function should only be called with arrays created by the QIR runtime library. +#[no_mangle] +pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__ctl( + _ctls: *const (usize, Vec), + arg_tuple: *mut *const Vec, +) { + let args = *arg_tuple.cast::(); + __quantum__qis__assertmeasurementprobability__body( + args.paulis, + args.qubits, + args.result, + args.prob, + args.msg, + args.tol, + ); +} + +/// QIR API for recording the given result into the program output. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) { + let mut res = RESULTS.lock().unwrap(); + let res_id = result as usize; + let b = if res.len() == 0 { + // No static measurements have been used, so default to dynamic handling. + __quantum__rt__result_equal(result, __quantum__rt__result_get_one()) + } else { + if res.len() < res_id + 1 { + res.resize(res_id + 1, false); + } + *res.get(res_id).unwrap() + }; + + println!("RESULT\t{}", if b { "1" } else { "0" }); +} + +/// QIR API that allocates the next available qubit in the simulation. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + let qubit_id = sim.allocate(); + + // Increase the max qubit id global so that `ensure_sufficient_qubits` wont trigger more allocations. + // NOTE: static allocation and dynamic allocation shouldn't be used together, so this is safe to do. + (*MAX_QUBIT_ID).fetch_max(qubit_id + 1, Relaxed); + + *sim_lock = Some(sim); + qubit_id as *mut c_void +} + +/// QIR API for allocating the given number of qubits in the simulation, returning them as a runtime managed array. +/// # Panics +/// +/// This function will panic if the underlying platform has a pointer size that cannot be described in +/// a `u32`. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const (usize, Vec) { + let arr = __quantum__rt__array_create_1d(size_of::().try_into().unwrap(), size); + for index in 0..size { + unsafe { + let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>(); + *elem = __quantum__rt__qubit_allocate(); + } + } + arr +} + +/// QIR API for releasing the given runtime managed qubit array. +/// # Safety +/// +/// This function should only be called with arrays created by `__quantum__rt__qubit_allocate_array`. +#[allow(clippy::cast_ptr_alignment)] +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const (usize, Vec)) { + for index in 0..__quantum__rt__array_get_size_1d(arr) { + let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>(); + __quantum__rt__qubit_release(*elem); + } + __quantum__rt__array_update_alias_count(arr, -1); +} + +/// QIR API for releasing the given qubit from the simulation. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + sim.release(qubit as usize); + *sim_lock = Some(sim); +} + +/// API for viewing the current global result and quantum state for the simulator. +/// # Panics +/// +#[no_mangle] +pub extern "C" fn dump_state() { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(SparseStateQuantumSim::new, |s| s); + let res = RESULTS.lock().unwrap(); + + println!("Global Results: {}", *res); + sim.dump(); + + *sim_lock = Some(sim); +} + +#[cfg(test)] +mod tests { + use std::ffi::c_void; + + use super::{ + __quantum__qis__cnot__body, __quantum__qis__h__body, __quantum__qis__m__body, + __quantum__qis__mz__body, __quantum__qis__read_result__body, __quantum__qis__x__body, + __quantum__rt__qubit_allocate, __quantum__rt__qubit_allocate_array, + __quantum__rt__qubit_release, __quantum__rt__qubit_release_array, + __quantum__rt__result_equal, __quantum__rt__result_get_one, dump_state, + }; + use crate::arrays::__quantum__rt__array_get_element_ptr_1d; + + // TODO(swernli): Split and expand simulator unit tests. + #[allow(clippy::cast_ptr_alignment)] + #[test] + fn basic_test() { + let q0 = 5 as *mut c_void; + let r0 = std::ptr::null_mut(); + let r1 = 1 as *mut c_void; + __quantum__qis__mz__body(q0, r0); + assert!(!__quantum__qis__read_result__body(r0)); + __quantum__qis__x__body(q0); + __quantum__qis__mz__body(q0, r1); + assert!(__quantum__qis__read_result__body(r1)); + __quantum__qis__x__body(q0); + __quantum__qis__mz__body(q0, r0); + assert!(!__quantum__qis__read_result__body(r0)); + assert!(!__quantum__qis__read_result__body(3 as *mut c_void)); + dump_state(); + + // Dynamic qubits can be used after static, but not the other way around. Keep test cases + // together to avoid issues with global state. + let q1 = __quantum__rt__qubit_allocate(); + let q2 = __quantum__rt__qubit_allocate(); + __quantum__qis__h__body(q1); + __quantum__qis__cnot__body(q1, q2); + let r1 = __quantum__qis__m__body(q1); + let r2 = __quantum__qis__m__body(q2); + assert!(__quantum__rt__result_equal(r1, r2)); + dump_state(); + __quantum__rt__qubit_release(q2); + __quantum__rt__qubit_release(q1); + let qs = __quantum__rt__qubit_allocate_array(4); + unsafe { + let q_elem = __quantum__rt__array_get_element_ptr_1d(qs, 3).cast::<*mut c_void>(); + __quantum__qis__x__body(*q_elem); + dump_state(); + let r = __quantum__qis__m__body(*q_elem); + assert!(__quantum__rt__result_equal( + r, + __quantum__rt__result_get_one() + )); + __quantum__rt__qubit_release_array(qs); + } + } +} diff --git a/src/Qir/riqir/src/sim/simulator.rs b/src/Qir/riqir/src/sim/simulator.rs new file mode 100644 index 00000000000..d8be4bd88cf --- /dev/null +++ b/src/Qir/riqir/src/sim/simulator.rs @@ -0,0 +1,687 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::common_matrices; + +use ndarray::Array2; +use num_complex::Complex64; +use rand::Rng; +use rayon::current_num_threads; +use rustc_hash::FxHashMap; +use std::convert::TryInto; +use std::mem::MaybeUninit; +use std::ops::ControlFlow; + +/// Limit that is checked to determine whether the operations buffer should be flushed before adding +/// more operations. Note that this is not a hard limit such that the buffer will never exceed this value, +/// just the value used to determine whether the buffer should accept the current incoming operation. +const BUFFER_LIMIT: usize = 4; + +/// Get the chunk size we want to use in parallel for the given number of items based on the threads available in the +/// thread pool. +pub(crate) fn parallel_chunk_size(total_size: usize) -> usize { + // Chunk count needs to be a power of two to work with the state vector math. + let mut chunk_count = 1_usize; + while chunk_count << 1 <= current_num_threads() { + chunk_count <<= 1; + } + if total_size > chunk_count { + total_size / chunk_count + } else { + // Try some well known sizes. + if total_size > 8 { + total_size / 8 + } else if total_size > 4 { + total_size / 4 + } else if total_size > 2 { + total_size / 2 + } else { + // Treat as a single chunk. + total_size + } + } +} + +pub(crate) struct OpBuffer { + pub targets: Vec, + pub ops: Array2, +} + +/// The `QuantumSim` struct contains the necessary state for tracking the simulation. Each instance of a +/// `QuantumSim` represents an independant simulation. +pub(crate) struct QuantumSim { + /// The structure that describes the current quantum state. + pub state: T, + + /// The mapping from qubit identifiers to internal state locations. + pub id_map: FxHashMap, + + /// The ordered buffer containing queued quantum operations that have not yet been applied to the + /// state. + pub op_buffer: OpBuffer, +} + +pub(crate) mod detail { + pub trait QuantumSimImpl { + fn extend_state(&mut self); + fn swap_qubits(&mut self, qubit1: usize, qubit2: usize); + fn cleanup_state(&mut self, res: bool); + fn dump_impl(&self, print_id_map: bool); + fn apply_impl(&mut self); + fn check_joint_probability(&self, locs: &[usize]) -> f64; + fn collapse(&mut self, loc: usize, val: bool); + fn joint_collapse(&mut self, locs: &[usize], val: bool); + fn normalize(&mut self); + } +} +use detail::QuantumSimImpl; + +/// Provides the common set of functionality across all quantum simulation types. +impl QuantumSim +where + Self: detail::QuantumSimImpl, +{ + /// Allocates a fresh qubit, returning its identifier. Note that this will use the lowest available + /// identifier, and may result in qubits being allocated "in the middle" of an existing register + /// if those identifiers are available. + #[must_use] + pub(crate) fn allocate(&mut self) -> usize { + self.extend_state(); + self.allocate_next_id() + } + + /// Utility that finds and reserves the next available qubit id in the map, returning the reserved + /// id. + fn allocate_next_id(&mut self) -> usize { + // Add the new entry into the FxHashMap at the first available sequential ID. + let mut sorted_keys: Vec<&usize> = self.id_map.keys().collect(); + sorted_keys.sort(); + let n_qubits = sorted_keys.len(); + let new_key = sorted_keys + .iter() + .enumerate() + .take_while(|(index, key)| index == **key) + .last() + .map_or(0_usize, |(_, &&key)| key + 1); + self.id_map.insert(new_key, n_qubits); + + // Return the new ID that was used. + new_key + } + + /// Releases the given qubit, collapsing its state in the process. After release that identifier is + /// no longer valid for use in other functions and will cause an error if used. + /// # Panics + /// + /// The function will panic if the given id does not correpsond to an allocated qubit. + pub(crate) fn release(&mut self, id: usize) { + self.flush(); + + // Since it is easier to release a contiguous half of the state, find the qubit + // with the last location and swap that with the qubit to be released. + let n_qubits = self.id_map.len(); + let loc = *self + .id_map + .get(&id) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}.", id)); + let last_loc = n_qubits - 1; + if last_loc != loc { + let last_id = *self + .id_map + .iter() + .find(|(_, &value)| value == last_loc) + .unwrap() + .0; + self.swap_qubits(loc, last_loc); + *(self.id_map.get_mut(&last_id).unwrap()) = loc; + *(self.id_map.get_mut(&id).unwrap()) = last_loc; + }; + + // Measure and collapse the state for this qubit. + let res = self.measure_impl(last_loc); + + // Remove the released ID from the mapping and cleanup the unused part of the state. + self.id_map.remove(&id); + self.cleanup_state(res); + } + + /// Flushes the current operations buffer and updates the resulting state, clearing the buffer + /// in the process. + /// # Panics + /// + /// This function will panic if the given ids that do not correspond to allocated qubits. + fn flush(&mut self) { + if !self.op_buffer.targets.is_empty() { + // Swap each of the target qubits to the right-most entries in the state, in order. + self.op_buffer + .targets + .clone() + .iter() + .rev() + .enumerate() + .for_each(|(target_loc, target)| { + let loc = *self + .id_map + .get(target) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", target)); + let swap_id = *self + .id_map + .iter() + .find(|(_, &value)| value == target_loc) + .unwrap() + .0; + self.swap_qubits(loc, target_loc); + *(self.id_map.get_mut(&swap_id).unwrap()) = loc; + *(self.id_map.get_mut(target).unwrap()) = target_loc; + }); + + self.apply_impl(); + + self.op_buffer = OpBuffer { + targets: vec![], + ops: Array2::default((0, 0)), + }; + } + } + + /// Prints the current state vector to standard output with integer labels for the states, skipping any + /// states with zero amplitude. + /// # Panics + /// + /// This function panics if it is unable sort the state into qubit id order. + pub(crate) fn dump(&mut self) { + self.flush(); + + // Swap all the entries in the state to be ordered by qubit identifier. This makes + // interpreting the state easier for external consumers that don't have access to the id map. + let mut sorted_keys: Vec = self.id_map.keys().copied().collect(); + sorted_keys.sort_unstable(); + sorted_keys.iter().enumerate().for_each(|(index, &key)| { + if index != self.id_map[&key] { + self.swap_qubits(self.id_map[&key], index); + let swapped_key = *self + .id_map + .iter() + .find(|(_, &value)| value == index) + .unwrap() + .0; + *(self.id_map.get_mut(&swapped_key).unwrap()) = self.id_map[&key]; + *(self.id_map.get_mut(&key).unwrap()) = index; + } + }); + + self.dump_impl(false); + } + + /// Applies the given unitary to the given targets, extending the unitary to accomodate controls if any. + /// # Panics + /// + /// This function will panic if given ids in either targets or optional controls that do not correspond to allocated + /// qubits, or if there is a duplicate id in targets or controls. + /// This funciton will panic if the given unitary matrix does not match the number of targets provided. + /// This function will panic if the given unitary is not square. + /// This function will panic if the total number of targets and controls too large for a `u32`. + pub(crate) fn apply( + &mut self, + unitary: &Array2, + targets: &[usize], + controls: Option<&[usize]>, + ) { + let mut targets = targets.to_vec(); + let mut unitary = unitary.clone(); + + assert!( + unitary.ncols() == unitary.nrows(), + "Application given non-square matrix." + ); + + assert!( + targets.len() == unitary.ncols() / 2, + "Application given incorrect number of targets; expected {}, given {}.", + unitary.ncols() / 2, + targets.len() + ); + + if let Some(ctrls) = controls { + // Add controls in order as targets. + ctrls + .iter() + .enumerate() + .for_each(|(index, &element)| targets.insert(index, element)); + + // Extend the provided unitary by inserting it into an identity matrix. + unitary = common_matrices::controlled(&unitary, ctrls.len().try_into().unwrap()); + } + + let mut sorted_targets = targets.clone(); + sorted_targets.sort_unstable(); + if let ControlFlow::Break(Some(duplicate)) = + sorted_targets.iter().try_fold(None, |last, current| { + last.map_or_else( + || ControlFlow::Continue(Some(current)), + |last| { + if last == current { + ControlFlow::Break(Some(current)) + } else { + ControlFlow::Continue(Some(current)) + } + }, + ) + }) + { + panic!("Duplicate qubit id '{}' found in application.", duplicate); + } + + // If the new operation would be too big for the buffer limit or if any target qubits are already + // targets of the buffered operations, flush the buffer first before adding the currently + // requested operation. + if self.op_buffer.targets.len() + targets.len() > BUFFER_LIMIT + || self.op_buffer.targets.iter().any(|q| targets.contains(q)) + { + self.flush(); + } + self.op_buffer.targets.append(&mut targets.clone()); + if self.op_buffer.targets.len() > targets.len() { + // Add the new unitary to the buffered operation by performing the Kronecker product. Note + // this means tthe order of targets must be maintained to match the combined matrix. + self.op_buffer.ops = QuantumSim::kron(&self.op_buffer.ops, &unitary); + } else { + // This is the only operation in an empty buffer, so just copy it. + self.op_buffer.ops = unitary; + } + } + + /// Parallelized Kronecker product implementation, copied from `ndarray::linalg::kron`. + fn kron(alpha: &Array2, beta: &Array2) -> Array2 { + let dim_alpha_rows = alpha.shape()[0]; + let dim_alpha_cols = alpha.shape()[1]; + let dim_beta_rows = beta.shape()[0]; + let dim_beta_cols = beta.shape()[1]; + let mut out: Array2> = Array2::uninit(( + dim_alpha_rows + .checked_mul(dim_beta_rows) + .expect("Dimensions of kronecker product output array overflows usize."), + dim_alpha_cols + .checked_mul(dim_beta_cols) + .expect("Dimensions of kronecker product output array overflows usize."), + )); + ndarray::Zip::from(out.exact_chunks_mut((dim_beta_rows, dim_beta_cols))) + .and(alpha) + .par_for_each(|out, &a| { + ndarray::Zip::from(out).and(beta).for_each(|out, &b| { + *out = MaybeUninit::new(a * b); + }); + }); + unsafe { out.assume_init() } + } + + /// Checks the probability of parity measurement in the computational basis for the given set of + /// qubits. + /// # Panics + /// + /// This function will panic if the given ids do not all correspond to allocated qubits. + /// This function will panic if there are duplicate ids in the given list. + #[must_use] + pub(crate) fn joint_probability(&mut self, ids: &[usize]) -> f64 { + let mut sorted_targets = ids.to_vec(); + sorted_targets.sort_unstable(); + if let ControlFlow::Break(Some(duplicate)) = + sorted_targets.iter().try_fold(None, |last, current| { + last.map_or_else( + || ControlFlow::Continue(Some(current)), + |last| { + if last == current { + ControlFlow::Break(Some(current)) + } else { + ControlFlow::Continue(Some(current)) + } + }, + ) + }) + { + panic!("Duplicate qubit id '{}' found in application.", duplicate); + } + + // Flush the buffered operations to update the state. + self.flush(); + + let locs: Vec = ids + .iter() + .map(|id| { + *self + .id_map + .get(id) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", id)) + }) + .collect(); + + self.check_joint_probability(&locs) + } + + /// Utility that performs the actual measurement and collapse of the state for the given + /// location. + fn measure_impl(&mut self, loc: usize) -> bool { + let mut rng = rand::thread_rng(); + let random_sample: f64 = rng.gen(); + let res = random_sample < self.check_joint_probability(&[loc]); + self.collapse(loc, res); + res + } + + /// Measures the qubit with the given id, collapsing the state based on the measured result. + /// # Panics + /// + /// This funciton will panic if the given identifier does not correspond to an allocated qubit. + #[must_use] + pub(crate) fn measure(&mut self, id: usize) -> bool { + // Flush the buffered operations and update the state. + self.flush(); + + self.measure_impl( + *self + .id_map + .get(&id) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", id)), + ) + } + + /// Performs a joint measurement to get the parity of the given qubits, collapsing the state + /// based on the measured result. + /// # Panics + /// + /// This function will panic if any of the given identifiers do not correspond to an allocated qubit. + /// This function will panic if any of the given identifiers are duplicates. + #[must_use] + pub(crate) fn joint_measure(&mut self, ids: &[usize]) -> bool { + let mut sorted_targets = ids.to_vec(); + sorted_targets.sort_unstable(); + if let ControlFlow::Break(Some(duplicate)) = + sorted_targets.iter().try_fold(None, |last, current| { + last.map_or_else( + || ControlFlow::Continue(Some(current)), + |last| { + if last == current { + ControlFlow::Break(Some(current)) + } else { + ControlFlow::Continue(Some(current)) + } + }, + ) + }) + { + panic!("Duplicate qubit id '{}' found in application.", duplicate); + } + + // Flush the buffered operations and update the state. + self.flush(); + + let locs: Vec = ids + .iter() + .map(|id| { + *self + .id_map + .get(id) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", id)) + }) + .collect(); + + let mut rng = rand::thread_rng(); + let random_sample: f64 = rng.gen(); + let res = random_sample < self.check_joint_probability(&locs); + self.joint_collapse(&locs, res); + res + } +} + +#[cfg(test)] +mod tests { + use super::{ + common_matrices::{adjoint, controlled, h, s, x}, + *, + }; + use crate::sim::sparsestate::SparseStateQuantumSim; + use ndarray::array; + use num_traits::{One, Zero}; + + fn almost_equal(a: f64, b: f64) -> bool { + a.max(b) - b.min(a) <= 1e-10 + } + + // Test that basic allocation and release of qubits doesn't fail. + #[test] + fn test_alloc_release() { + let sim = &mut SparseStateQuantumSim::default(); + for i in 0..16 { + assert_eq!(sim.allocate(), i); + } + sim.release(4); + sim.release(7); + sim.release(12); + assert_eq!(sim.allocate(), 4); + for i in 0..7 { + sim.release(i); + } + for i in 8..12 { + sim.release(i); + } + for i in 13..16 { + sim.release(i); + } + } + + /// Test basic appliation of gate matrices doesn't fail. Note that this does not verify the contents + /// of the state. + #[test] + fn test_apply1() { + let mut sim = SparseStateQuantumSim::default(); + for i in 0..6 { + assert_eq!(sim.allocate(), i); + } + sim.apply(&x(), &[2], None); + sim.apply(&x(), &[4], None); + sim.apply(&x(), &[5], None); + sim.release(5); + sim.apply(&ndarray::linalg::kron(&h(), &x()), &[1, 4], None); + sim.apply(&x(), &[2], None); + sim.apply(&h(), &[1], None); + for i in 0..5 { + sim.release(i); + } + } + + /// Test appliction of gates that result in entanglement. Note that this does not verify the contents + /// of the state. + #[test] + fn test_apply2() { + let mut sim = SparseStateQuantumSim::default(); + let q0 = sim.allocate(); + let q1 = sim.allocate(); + let q2 = sim.allocate(); + sim.apply(&h(), &[q0], None); + sim.apply(&controlled(&x(), 1), &[q0, q1], None); + sim.apply(&controlled(&x(), 1), &[q0, q2], None); + sim.apply(&controlled(&x(), 1), &[q1, q0], None); + sim.apply(&controlled(&x(), 1), &[q1, q2], None); + sim.apply(&h(), &[q1], None); + sim.release(q0); + sim.release(q1); + sim.release(q2); + } + + /// Verifies that application of gates to a qubit results in the correct probabilities. + #[test] + fn test_probability() { + let mut sim = SparseStateQuantumSim::default(); + let q = sim.allocate(); + let extra = sim.allocate(); + assert!(almost_equal(0.0, sim.joint_probability(&[q]))); + sim.apply(&x(), &[q], None); + assert!(almost_equal(1.0, sim.joint_probability(&[q]))); + sim.apply(&x(), &[q], None); + assert!(almost_equal(0.0, sim.joint_probability(&[q]))); + sim.apply(&h(), &[q], None); + assert!(almost_equal(0.5, sim.joint_probability(&[q]))); + sim.apply(&h(), &[q], None); + assert!(almost_equal(0.0, sim.joint_probability(&[q]))); + sim.apply(&x(), &[q], None); + sim.apply(&h(), &[q], None); + sim.apply(&s(), &[q], None); + assert!(almost_equal(0.5, sim.joint_probability(&[q]))); + sim.apply(&adjoint(&s()), &[q], None); + sim.apply(&h(), &[q], None); + sim.apply(&x(), &[q], None); + assert!(almost_equal(0.0, sim.joint_probability(&[q]))); + sim.release(extra); + sim.release(q); + } + + /// Verify that a qubit in superposition has probability corresponding the measured value and + /// can be operationally reset back into the ground state. + #[test] + fn test_measure() { + let mut sim = SparseStateQuantumSim::default(); + let q = sim.allocate(); + let extra = sim.allocate(); + assert!(!sim.measure(q)); + sim.apply(&x(), &[q], None); + assert!(sim.measure(q)); + let mut res = false; + while !res { + sim.apply(&h(), &[q], None); + res = sim.measure(q); + assert!(almost_equal( + sim.joint_probability(&[q]), + if res { 1.0 } else { 0.0 } + )); + if res { + sim.apply(&x(), &[q], None); + } + } + assert!(almost_equal(sim.joint_probability(&[q]), 0.0)); + sim.release(extra); + sim.release(q); + } + + /// Verify joint probability works as expected, namely that it corresponds to the parity of the + /// qubits. + #[test] + fn test_joint_probability() { + let mut sim = SparseStateQuantumSim::default(); + let q0 = sim.allocate(); + let q1 = sim.allocate(); + assert!(almost_equal(0.0, sim.joint_probability(&[q0, q1]))); + sim.apply(&x(), &[q0], None); + assert!(almost_equal(1.0, sim.joint_probability(&[q0, q1]))); + sim.apply(&x(), &[q1], None); + assert!(almost_equal(0.0, sim.joint_probability(&[q0, q1]))); + assert!(almost_equal(1.0, sim.joint_probability(&[q0]))); + assert!(almost_equal(1.0, sim.joint_probability(&[q1]))); + sim.apply(&h(), &[q0], None); + assert!(almost_equal(0.5, sim.joint_probability(&[q0, q1]))); + sim.release(q1); + sim.release(q0); + } + + /// Verify joint measurement works as expected, namely that it corresponds to the parity of the + /// qubits. + #[test] + fn test_joint_measurement() { + let mut sim = SparseStateQuantumSim::default(); + let q0 = sim.allocate(); + let q1 = sim.allocate(); + assert!(!sim.joint_measure(&[q0, q1])); + sim.apply(&x(), &[q0], None); + assert!(sim.joint_measure(&[q0, q1])); + sim.apply(&x(), &[q1], None); + assert!(!sim.joint_measure(&[q0, q1])); + assert!(sim.joint_measure(&[q0])); + assert!(sim.joint_measure(&[q1])); + sim.apply(&h(), &[q0], None); + let res = sim.joint_measure(&[q0, q1]); + assert!(almost_equal( + if res { 1.0 } else { 0.0 }, + sim.joint_probability(&[q0, q1]) + )); + sim.release(q1); + sim.release(q0); + } + + /// Test arbitrary controls, which should extend the applied unitary matrix. + #[test] + fn test_arbitrary_controls() { + let mut sim = SparseStateQuantumSim::default(); + let q0 = sim.allocate(); + let q1 = sim.allocate(); + let q2 = sim.allocate(); + assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); + sim.apply(&h(), &[q0], None); + assert!(almost_equal(0.5, sim.joint_probability(&[q0]))); + sim.apply(&h(), &[q0], None); + assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); + sim.apply(&h(), &[q0], Some(&[q1])); + assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); + sim.apply(&x(), &[q1], None); + sim.apply(&h(), &[q0], Some(&[q1])); + assert!(almost_equal(0.5, sim.joint_probability(&[q0]))); + sim.apply(&h(), &[q0], Some(&[q2, q1])); + assert!(almost_equal(0.5, sim.joint_probability(&[q0]))); + sim.apply(&x(), &[q2], None); + sim.apply(&h(), &[q0], Some(&[q2, q1])); + assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); + sim.apply(&x(), &[q1], None); + sim.apply(&x(), &[q2], None); + sim.release(q2); + sim.release(q1); + sim.release(q0); + } + + /// Verify that targets cannot be duplicated. + #[test] + #[should_panic(expected = "Duplicate qubit id '0' found in application.")] + fn test_duplicate_target() { + let mut sim = SparseStateQuantumSim::new(); + let q = sim.allocate(); + sim.apply(&controlled(&x(), 1), &[q, q], None); + } + + /// Verify that controls cannot be duplicated. + #[test] + #[should_panic(expected = "Duplicate qubit id '1' found in application.")] + fn test_duplicate_control() { + let mut sim = SparseStateQuantumSim::new(); + let q = sim.allocate(); + let c = sim.allocate(); + sim.apply(&x(), &[q], Some(&[c, c])); + } + + /// Verify that targets aren't in controls. + #[test] + #[should_panic(expected = "Duplicate qubit id '0' found in application.")] + fn test_target_in_control() { + let mut sim = SparseStateQuantumSim::new(); + let q = sim.allocate(); + let c = sim.allocate(); + sim.apply(&x(), &[q], Some(&[c, q])); + } + + /// Verify target count logic, which should reject any application where the given targets does + /// not match the size of the given unitary. + #[test] + #[should_panic( + expected = "Application given incorrect number of targets; expected 2, given 1." + )] + fn test_target_count() { + let mut sim = SparseStateQuantumSim::new(); + let q = sim.allocate(); + sim.apply(&controlled(&x(), 1), &[q], None); + } + + /// Verify that non-square matrices are rejected. + #[test] + #[should_panic(expected = "Application given non-square matrix.")] + fn test_nonsquare() { + let mut sim = SparseStateQuantumSim::new(); + let q = sim.allocate(); + sim.apply(&array![[Complex64::zero()], [Complex64::one()]], &[q], None); + } +} diff --git a/src/Qir/riqir/src/sim/sparsestate.rs b/src/Qir/riqir/src/sim/sparsestate.rs new file mode 100644 index 00000000000..2f45539a6c1 --- /dev/null +++ b/src/Qir/riqir/src/sim/sparsestate.rs @@ -0,0 +1,268 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::convert::TryInto; + +use ndarray::Array2; +use num_complex::Complex64; +use num_traits::{One, Zero}; +use rayon::prelude::*; +use rustc_hash::FxHashMap; + +use super::simulator::{detail::QuantumSimImpl, parallel_chunk_size, OpBuffer, QuantumSim}; + +pub type SparseState = FxHashMap; + +/// Sparse quantum state simulation using a dictionary of states with non-zero amplitudes, based on . +/// This simulator is memory efficient for highly entangled states, allowing for the use of up to 128 qubits, but less performant +/// than full state simulation for dense quantum states. +pub(crate) type SparseStateQuantumSim = QuantumSim; + +impl Default for QuantumSim { + fn default() -> Self { + Self::new() + } +} + +impl QuantumSim { + /// Creates a new sparse state quantum simulator object with empty initial state (no qubits allocated, no operations buffered). + #[must_use] + pub fn new() -> Self { + QuantumSim { + state: FxHashMap::default(), + + id_map: FxHashMap::default(), + + op_buffer: OpBuffer { + targets: vec![], + ops: Array2::default((0, 0)), + }, + } + } +} + +impl QuantumSimImpl for QuantumSim { + /// Utility that extends the internal state to make room for a newly allocated qubit. + /// # Panics + /// + /// This function will panic if the total number of allocated qubits would increase beyond 128. + fn extend_state(&mut self) { + if self.id_map.len() >= 128 { + panic!("Attempting to allocate more than 128 qubits. Internal data structures of sparse simulation can handle at most 128 qubits."); + } else if self.id_map.is_empty() { + // Add the intial value for the zero state. + self.state.insert(0, Complex64::one()); + } + } + + /// Utility function that will swap states of two qubits throughout the sparse state map. + fn swap_qubits(&mut self, qubit1: usize, qubit2: usize) { + if qubit1 == qubit2 { + return; + } + let offset1 = 1_u128 << qubit1; + let offset2 = 1_u128 << qubit2; + + // In parallel, swap entries in the sparse state to correspond to swapping of two qubits' + // locations. + let chunk_size = parallel_chunk_size(self.state.len()); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (k, v) in chunk { + if (k & offset1 > 0) == (k & offset2 > 0) { + accum.insert(*k, *v); + } else { + accum.insert((k ^ offset1) ^ offset2, *v); + } + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut chunk| { + for (k, v) in chunk.drain() { + accum.insert(k, v); + } + accum + }); + } + + /// Releases the given qubit, collapsing its state in the process. After release that identifier is + /// no longer valid for use in other functions and will cause an error if used. + /// # Panics + /// + /// The function will panic if the given id does not correpsond to an allocated qubit. + fn cleanup_state(&mut self, res: bool) { + if res { + let offset = 1_u128 << self.id_map.len(); + let chunk_size = parallel_chunk_size(self.state.len()); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (k, v) in chunk { + accum.insert(k ^ offset, *v); + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut chunk| { + for (k, v) in chunk.drain() { + accum.insert(k, v); + } + accum + }); + } + } + + /// Utility function that performs the actual output of state (and optionally map) to screen. Can + /// be called internally from other functions to aid in debugging and does not perform any modification + /// of the internal structures. + fn dump_impl(&self, print_id_map: bool) { + if print_id_map { + println!("MAP: {:?}", self.id_map); + }; + print!("STATE: [ "); + let mut sorted_keys = self.state.keys().copied().collect::>(); + sorted_keys.sort_unstable(); + for key in sorted_keys { + print!( + "|{}\u{27e9}: {}, ", + key, + self.state.get(&key).map_or_else(Complex64::zero, |v| *v) + ); + } + println!("]"); + } + + /// Utility that actually performs the application of the buffered unitary to the targets within the + /// sparse state. + fn apply_impl(&mut self) { + // let state_size = 1_usize << self.id_map.len(); + let chunk_size = parallel_chunk_size(self.state.len()); + let op_size = self.op_buffer.ops.nrows() as u128; + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (index, val) in chunk { + let i = index / op_size; + let l = (index % op_size) as usize; + for j in + (0..op_size).filter(|j| !self.op_buffer.ops.row(*j as usize)[l].is_zero()) + { + let loc = (i * op_size) + (j as u128); + if let Some(entry) = accum.get_mut(&loc) { + *entry += self.op_buffer.ops.row(j.try_into().unwrap())[l] * val; + } else { + accum.insert( + (i * op_size) + (j as u128), + self.op_buffer.ops.row(j.try_into().unwrap())[l] * val, + ); + } + if accum + .get(&loc) + .map_or_else(Complex64::one, |entry| *entry) + .is_zero() + { + accum.remove(&loc); + } + } + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut sparse_chunk| { + for (k, v) in sparse_chunk.drain() { + if let Some(entry) = accum.get_mut(&k) { + *entry += v; + } else if !v.is_zero() { + accum.insert(k, v); + } + if accum + .get(&k) + .map_or_else(Complex64::one, |entry| *entry) + .is_zero() + { + accum.remove(&k); + } + } + accum + }); + } + + /// Utility to get the sum of all probabilies where an odd number of the bits at the given locations + /// are set. This corresponds to the probability of jointly measuring those qubits in the computational + /// basis. + fn check_joint_probability(&self, locs: &[usize]) -> f64 { + let mask = locs.iter().fold(0_u128, |accum, loc| accum | (1 << loc)); + (&self.state) + .into_par_iter() + .fold( + || 0.0_f64, + |accum, (index, val)| { + if (index & mask).count_ones() & 1 > 0 { + accum + val.norm_sqr() + } else { + accum + } + }, + ) + .sum() + } + + /// Utility to perform the normalize of the state. + fn normalize(&mut self) { + let scale = 1.0 + / self + .state + .par_iter() + .fold(|| 0.0_f64, |sum, (_, val)| sum + val.norm_sqr()) + .sum::() + .sqrt(); + + self.state.par_iter_mut().for_each(|(_, val)| *val *= scale); + } + + /// Utility to collapse the probability at the given location based on the boolean value. This means + /// that if the given value is 'true' then all keys in the sparse state where the given location + /// has a zero bit will be reduced to zero and removed. Then the sparse state is normalized. + fn collapse(&mut self, loc: usize, val: bool) { + self.joint_collapse(&[loc], val); + } + + /// Utility to collapse the joint probability of a particular set of locations in the sparse state. + /// The entries that do not correspond to the given boolean value are removed, and then the whole + /// state is normalized. + fn joint_collapse(&mut self, locs: &[usize], val: bool) { + let mask = locs.iter().fold(0_u128, |accum, loc| accum | (1 << loc)); + + let chunk_size = parallel_chunk_size(self.state.len()); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (k, v) in chunk + .iter() + .filter(|(index, _)| ((index & mask).count_ones() & 1 > 0) == val) + { + accum.insert(*k, *v); + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut chunk| { + for (k, v) in chunk.drain() { + accum.insert(k, v); + } + accum + }); + + self.normalize(); + } +} From 14fe034618857d1b05801d157712bfb1bcb417a6 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 26 Aug 2022 15:32:20 -0700 Subject: [PATCH 06/55] Share C++ tests across Rust impl --- build/set-env.ps1 | 8 ++ src/Qir/Common/cmake/unit_test_include.cmake | 7 +- .../FullstateSimulatorTests.cpp | 15 ++- .../qsharp/qir-test-simulator.qs | 34 +++---- src/Qir/Tests/QIR-dynamic/qir-driver.cpp | 10 +- src/Qir/Tests/QIR-static/qir-driver.cpp | 40 +++++++- .../QIR-static/qir-test-conditionals.cpp | 12 +++ src/Qir/Tests/QIR-static/qir-test-math.cpp | 4 + src/Qir/Tests/QIR-static/qir-test-other.cpp | 2 + src/Qir/riqir-test/CMakeLists.txt | 32 +++++++ .../FullstateSimulator/CMakeLists.txt | 20 ++++ src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt | 30 ++++++ src/Qir/riqir-test/QIR-static/CMakeLists.txt | 35 +++++++ src/Qir/riqir-test/build-qir-tests.ps1 | 24 +++++ src/Qir/riqir-test/test-qir-tests.ps1 | 38 ++++++++ src/Qir/riqir/build-qir-runtime.ps1 | 8 +- src/Qir/riqir/src/sim/mod.rs | 96 ++++++++++++++----- 17 files changed, 357 insertions(+), 58 deletions(-) create mode 100644 src/Qir/riqir-test/CMakeLists.txt create mode 100644 src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt create mode 100644 src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt create mode 100644 src/Qir/riqir-test/QIR-static/CMakeLists.txt create mode 100644 src/Qir/riqir-test/build-qir-tests.ps1 create mode 100644 src/Qir/riqir-test/test-qir-tests.ps1 diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 76cd12dd28b..396b8cc2be7 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -39,10 +39,18 @@ If (-not (Test-Path -Path $Env:WHEEL_OUTDIR)) { [IO.Directory]::CreateDirectory( If ($Env:DOCS_OUTDIR -eq $null) { $Env:DOCS_OUTDIR = (Join-Path $Env:DROPS_DIR "docs") } If (-not (Test-Path -Path $Env:DOCS_OUTDIR)) { [IO.Directory]::CreateDirectory($Env:DOCS_OUTDIR) } +$env:BUILD_PLATFORM = "win-x64" +if ($IsLinux) { + $env:BUILD_PLATFORM = "linux-x64" +} elseif ($IsMacOS) { + $env:BUILD_PLATFORM = "osx-x64" +} + Get-ChildItem -Path Env:/* -Include @( "BUILD_BUILDNUMBER", "BUILD_CONFIGURATION", "BUILD_VERBOSITY", + "BUILD_PLATFORM", "ASSEMBLY_VERSION", "PYTHON_VERSION", "NUGET_VERSION", diff --git a/src/Qir/Common/cmake/unit_test_include.cmake b/src/Qir/Common/cmake/unit_test_include.cmake index 93701f563b0..8781e9c902e 100644 --- a/src/Qir/Common/cmake/unit_test_include.cmake +++ b/src/Qir/Common/cmake/unit_test_include.cmake @@ -14,9 +14,10 @@ macro(add_unit_test target) set(TEST_DEPS2 "${CMAKE_BINARY_DIR}/bin") set(TEST_DEPS3 "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") + set(TEST_DEPS3 "${PROJECT_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") set_property(TEST ${target} PROPERTY ENVIRONMENT - "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${LD_LIBRARY_PATH}" - "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${TEST_DEPS3}\;${PATH}" - "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${DYLD_LIBRARY_PATH}" + "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${TEST_DEPS4}:${LD_LIBRARY_PATH}" + "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${TEST_DEPS3}\;${TEST_DEPS4}\;${PATH}" + "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${TEST_DEPS4}:${DYLD_LIBRARY_PATH}" ) endmacro(add_unit_test) diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index 563a1601f85..8651394d4c3 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -17,6 +17,8 @@ using namespace Microsoft::Quantum; using namespace std; +#ifndef RIQIR_TESTING + // The tests rely on the implementation detail of CFullstateSimulator that the qubits are nothing more but contiguously // incremented ids. static unsigned GetQubitId(QubitIdType q) @@ -420,20 +422,29 @@ TEST_CASE("QIR: Simulator rejects unmeasured, non-zero release", "[fullstate_sim QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); REQUIRE_THROWS(Microsoft__Quantum__Testing__QIR__InvalidRelease__Interop()); } +#endif extern "C" int Microsoft__Quantum__Testing__QIR__MeasureRelease__Interop(); // NOLINT TEST_CASE("QIR: Simulator accepts measured release", "[fullstate_simulator]") { +#ifndef RIQIR_TESTING std::unique_ptr sim = CreateFullstateSimulator(); QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); +#endif + REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__MeasureRelease__Interop()); } -extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(); // NOLINT +extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(bool); // NOLINT TEST_CASE("QIR: invoke all standard Q# gates against the fullstate simulator", "[fullstate_simulator]") { +#ifndef RIQIR_TESTING std::unique_ptr sim = CreateFullstateSimulator(); QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); + bool includeExp = true; +#else + bool includeExp = false; +#endif - REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop()); + REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(includeExp)); } diff --git a/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs index 95060828d74..081ec83dbe7 100644 --- a/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs +++ b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs @@ -33,7 +33,7 @@ namespace Microsoft.Quantum.Testing.QIR } @EntryPoint() - operation Test_Simulator_QIS() : Int + operation Test_Simulator_QIS(includeExp : Bool) : Int { mutable res = 0; set res = InvokeAllVariants(X); @@ -57,23 +57,25 @@ namespace Microsoft.Quantum.Testing.QIR set res = InvokeAllVariants(R(PauliX, 0.42, _)); if (res != 0) { return 60 + res; } - use (targets, ctls) = (Qubit[2], Qubit[2]) - { - let theta = 0.42; - Exp([PauliX, PauliY], theta, targets); - Adjoint Exp([PauliX, PauliY], theta, targets); - if (M(targets[0]) != Zero) { set res = 1; } + if includeExp { + use (targets, ctls) = (Qubit[2], Qubit[2]) + { + let theta = 0.42; + Exp([PauliX, PauliY], theta, targets); + Adjoint Exp([PauliX, PauliY], theta, targets); + if (M(targets[0]) != Zero) { set res = 1; } - H(ctls[0]); - H(ctls[1]); - Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); - Adjoint Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); - H(ctls[0]); - H(ctls[1]); - if (M(targets[0]) != Zero) { set res = 2; } - ResetAll(targets + ctls); + H(ctls[0]); + H(ctls[1]); + Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); + Adjoint Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); + H(ctls[0]); + H(ctls[1]); + if (M(targets[0]) != Zero) { set res = 2; } + ResetAll(targets + ctls); + } + if (res != 0) { return 70 + res; } } - if (res != 0) { return 70 + res; } use qs = Qubit[3] { diff --git a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp index 97c065c67e4..210bee0dcf2 100644 --- a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp +++ b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp @@ -42,8 +42,10 @@ using namespace Microsoft::Quantum; TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") { +#ifndef RIQIR_TESTING std::unique_ptr sim = CreateFullstateSimulator(); QirExecutionContext::Scoped contextReleaser{sim.get()}; +#endif const int64_t ret1 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); const int64_t ret2 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); @@ -57,7 +59,7 @@ TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") CHECK(ret2 != ret3); } - +#ifndef RIQIR_TESTING static bool FileExists(const char* filePath) { return std::ifstream(filePath).operator bool(); @@ -190,12 +192,14 @@ static void AssertMeasMessageTest(void (*funcPtr)(const char*)) // TODO: Extract into a separate .cpp compiled with leak detection off. REQUIRE(outStrStream.str() == (std::string(testStr) + "\n")); } - +#endif TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") { +#ifndef RIQIR_TESTING std::unique_ptr sim = CreateFullstateSimulator(); QirExecutionContext::Scoped contextReleaser{sim.get()}; +#endif REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasAlloc1OKTest__Interop()); @@ -208,7 +212,9 @@ TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasMixedBasesTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertGHZMeasurementsTest__Interop()); +#ifndef RIQIR_TESTING AssertMeasMessageTest(Microsoft__Quantum__Testing__QIR__AssertMeasMessageTest__Interop); AssertMeasMessageTest(Microsoft__Quantum__Testing__QIR__AssertMeasProbMessageTest__Interop); +#endif } // TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") diff --git a/src/Qir/Tests/QIR-static/qir-driver.cpp b/src/Qir/Tests/QIR-static/qir-driver.cpp index ac8d24ca0e9..16dc58ccdf0 100644 --- a/src/Qir/Tests/QIR-static/qir-driver.cpp +++ b/src/Qir/Tests/QIR-static/qir-driver.cpp @@ -12,6 +12,7 @@ #include "CoreTypes.hpp" #include "QirContext.hpp" #include "QirTypes.hpp" +#include "QirRuntime.hpp" #include "QirRuntimeApi_I.hpp" #include "SimFactory.hpp" #include "SimulatorStub.hpp" @@ -56,8 +57,9 @@ extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop( // NO Array* array, int64_t index, int64_t val); TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") { +#ifndef RIQIR_TESTING QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); - +#endif constexpr int64_t n = 5; int64_t values[n] = {0, 1, 2, 3, 4}; auto array = Array{n, values}; @@ -149,17 +151,21 @@ struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub }; TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][qir.result]") { +#ifndef RIQIR_TESTING unique_ptr sim = make_unique(); QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); +#endif REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__Interop()); +#ifndef RIQIR_TESTING // check that all qubits have been released for (size_t id = 0; id < sim->qubits.size(); id++) { INFO(std::string("unreleased qubit: ") + std::to_string(id)); CHECK(sim->qubits[id] == RELEASED); } +#endif // check that all results, allocated by measurements have been released // TODO: enable after https://github.com/microsoft/qsharp-compiler/issues/780 is fixed @@ -170,6 +176,8 @@ TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][ // } } +#ifndef RIQIR_TESTING + #ifdef _WIN32 // A non-sensical function that creates a 3D array with given dimensions, then projects on the index = 1 of the // second dimension and returns a function of the sizes of the dimensions of the projection and a the provided value, @@ -178,7 +186,9 @@ TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][ extern "C" int64_t TestMultidimArrays(char value, int64_t dim0, int64_t dim1, int64_t dim2); TEST_CASE("QIR: multidimensional arrays", "[qir][qir.arrMultid]") { +#ifndef RIQIR_TESTING QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); +#endif REQUIRE(42 + (2 + 8) / 2 == TestMultidimArrays(42, 2, 4, 8)); REQUIRE(17 + (3 + 7) / 2 == TestMultidimArrays(17, 3, 5, 7)); @@ -191,7 +201,9 @@ TEST_CASE("QIR: multidimensional arrays", "[qir][qir.arrMultid]") extern "C" void TestFailWithRangeString(int64_t start, int64_t step, int64_t end); TEST_CASE("QIR: Report range in a failure message", "[qir][qir.range]") { +#ifndef RIQIR_TESTING QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); +#endif bool failed = false; try @@ -208,11 +220,15 @@ TEST_CASE("QIR: Report range in a failure message", "[qir][qir.range]") REQUIRE(failed); } +#endif // RIQIR_TESTING + // TestPartials subtracts the second argument from the first and returns the result. extern "C" int64_t Microsoft__Quantum__Testing__QIR__TestPartials__Interop(int64_t, int64_t); // NOLINT TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") { +#ifndef RIQIR_TESTING QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); +#endif const int64_t res = Microsoft__Quantum__Testing__QIR__TestPartials__Interop(42, 17); REQUIRE(res == 42 - 17); @@ -220,6 +236,7 @@ TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") // The Microsoft__Quantum__Testing__QIR__TestFunctors__Interop tests needs proper semantics of X and M, and nothing // else. The validation is done inside the test and it would throw in case of failure. +#ifndef RIQIR_TESTING struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub { std::vector qubits; @@ -311,19 +328,38 @@ extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q) // NOLINT g_ctrqapi->ControlledX((long)(controls->count), reinterpret_cast(controls->buffer), reinterpret_cast(q)); } +#else +extern "C" void __quantum__qis__x__body(QUBIT* q); // NOLINT +extern "C" void __quantum__qis__x__ctl(QirArray* controls, QUBIT* q); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__Interop(); // NOLINT +extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__Interop(); // NOLINT +extern "C" void __quantum__qis__k__body(QUBIT* q) // NOLINT +{ + __quantum__qis__x__body(q); +} +extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q) // NOLINT +{ + __quantum__qis__x__ctl(controls, q); +} +#endif TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") { +#ifndef RIQIR_TESTING unique_ptr qapi = make_unique(); QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); g_ctrqapi = qapi.get(); - +#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__Interop()); +#ifndef RIQIR_TESTING const int cKCalls = g_cKCalls; const int cKCallsControlled = g_cKCallsControlled; +#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__Interop()); +#ifndef RIQIR_TESTING CHECK(g_cKCalls - cKCalls == 3); CHECK(g_cKCallsControlled - cKCallsControlled == 5); g_ctrqapi = nullptr; +#endif } diff --git a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp index ecd5b47a2ce..361db7dbb81 100644 --- a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp @@ -120,44 +120,56 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIf__Interop(); // NOLINT TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") { +#ifndef RIQIR_TESTING unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); +#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__Interop()); +#ifndef RIQIR_TESTING INFO(qapi->GetHistory()); CHECK(qapi->xCallbacks.size() == 8); CHECK(qapi->cxCallbacks.size() == 0); CHECK(qapi->otherCallbacks.size() == 0); +#endif } extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__Interop(); // NOLINT TEST_CASE("QIR: ApplyIf with functors", "[qir][qir.conditionals]") { +#ifndef RIQIR_TESTING unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); +#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__Interop()); +#ifndef RIQIR_TESTING INFO(qapi->GetHistory()); CHECK(qapi->xCallbacks.size() == 5); CHECK(qapi->cxCallbacks.size() == 7); CHECK(qapi->otherCallbacks.size() == 0); +#endif } extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__Interop(); // NOLINT TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") { +#ifndef RIQIR_TESTING unique_ptr qapi = make_unique(vector{Result_Zero, Result_One}); QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); +#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__Interop()); +#ifndef RIQIR_TESTING INFO(qapi->GetHistory()); CHECK(qapi->xCallbacks.size() == 4); CHECK(qapi->cxCallbacks.size() == 2); CHECK(qapi->otherCallbacks.size() == 0); +#endif } diff --git a/src/Qir/Tests/QIR-static/qir-test-math.cpp b/src/Qir/Tests/QIR-static/qir-test-math.cpp index 87765cb22e4..9f30099fe99 100644 --- a/src/Qir/Tests/QIR-static/qir-test-math.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-math.cpp @@ -106,6 +106,7 @@ TEST_CASE("QIR: Math.Exponent.builtin", "[qir.math][qir.Math.Exponent.builtin]") TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") { +#ifndef RIQIR_TESTING // Test that the Q# random number generator is a wrapper around the C++ generator: size_t times = 1000; while (--times) @@ -137,6 +138,7 @@ TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") { REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); } +#endif // Test equal minimum and maximum: for (int64_t num : {-5, 0, 3}) @@ -234,6 +236,7 @@ TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") } // TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") +#ifndef RIQIR_TESTING TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") { // Test that the Q# random number generator is a wrapper around the C++ generator: @@ -269,3 +272,4 @@ TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); } } // TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") +#endif diff --git a/src/Qir/Tests/QIR-static/qir-test-other.cpp b/src/Qir/Tests/QIR-static/qir-test-other.cpp index bb1417234ff..406accf2435 100644 --- a/src/Qir/Tests/QIR-static/qir-test-other.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-other.cpp @@ -7,6 +7,7 @@ extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop(); // NOLINT extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntFailTest__Interop(); // NOLINT +#ifndef RIQIR_TESTING TEST_CASE("QIR: Other.PauliArrayAsIntFail", "[qir.Other][qir.Other.PauliArrayAsIntFail]") { REQUIRE_THROWS(Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntFailTest__Interop()); @@ -14,6 +15,7 @@ TEST_CASE("QIR: Other.PauliArrayAsIntFail", "[qir.Other][qir.Other.PauliArrayAsI // to the moment of exception throw. // TODO: Extract into a separate file compiled with leaks check off. } +#endif TEST_CASE("QIR: Other.PauliArrayAsInt", "[qir.Other][qir.Other.PauliArrayAsInt]") { diff --git a/src/Qir/riqir-test/CMakeLists.txt b/src/Qir/riqir-test/CMakeLists.txt new file mode 100644 index 00000000000..77eebe4ebe2 --- /dev/null +++ b/src/Qir/riqir-test/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) + +message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") + +# set the project name and version +project(riqir-tests) + +# specify the C++ standard, compiler and other tools +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") + +# feel free to customize these flags for your local builds (don't check in) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-inline") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") + +set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") +set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") +set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") + +set(runtime_lib_path "${PROJECT_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") + +include(qir_cmake_include) +include(unit_test_include) + +add_subdirectory(FullstateSimulator) +add_subdirectory(QIR-dynamic) +add_subdirectory(QIR-static) diff --git a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt new file mode 100644 index 00000000000..31a75a9ffe8 --- /dev/null +++ b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt @@ -0,0 +1,20 @@ +add_executable(fullstate-simulator-tests +../../Tests/FullstateSimulator/FullstateSimulatorTests.cpp) + +target_source_from_qir(fullstate-simulator-tests ../../Tests/FullstateSimulator/qsharp/obj/qsharp/qir-test-simulator.bc) + +target_link_libraries(fullstate-simulator-tests PUBLIC + "-L${runtime_lib_path}" + -lqir_range_support + -lqir_runtime +) + +target_include_directories(fullstate-simulator-tests PUBLIC + ${test_includes} + ${public_includes} +) +target_compile_definitions(fullstate-simulator-tests PRIVATE RIQIR_TESTING) + +install(TARGETS fullstate-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +add_unit_test(fullstate-simulator-tests) + diff --git a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt new file mode 100644 index 00000000000..8ba80ff4737 --- /dev/null +++ b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt @@ -0,0 +1,30 @@ +set(TEST_FILES +../../Tests/QIR-dynamic/qsharp/obj/qsharp/qir-test-random.bc +) + +#============================================================================== +# This executable target links test code against the dynamic libraries rather than the explicit +# static QIR/RT libs (qir will statically link in the bridge via transitivity of target_link_libraries). +# +add_executable(qir-dynamic-tests +../../Tests/QIR-dynamic/qir-driver.cpp +) + +foreach(file ${TEST_FILES}) + target_source_from_qir(qir-dynamic-tests ${file}) +endforeach() + +target_link_libraries(qir-dynamic-tests PUBLIC + "-L${runtime_lib_path}" + -lqir_range_support + -lqir_runtime +) + +target_include_directories(qir-dynamic-tests PUBLIC + ${test_includes} + ${public_includes} +) +target_compile_definitions(qir-dynamic-tests PRIVATE RIQIR_TESTING) + +install(TARGETS qir-dynamic-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +add_unit_test(qir-dynamic-tests) diff --git a/src/Qir/riqir-test/QIR-static/CMakeLists.txt b/src/Qir/riqir-test/QIR-static/CMakeLists.txt new file mode 100644 index 00000000000..62986ed8cc2 --- /dev/null +++ b/src/Qir/riqir-test/QIR-static/CMakeLists.txt @@ -0,0 +1,35 @@ +set(TEST_FILES + ../../Tests/QIR-static/qsharp/qir/qir-gen.ll +) + +#============================================================================== +# The executable target for QIR tests triggers the custom actions to compile ll files +# +add_executable(qir-static-tests + ../../Tests/QIR-static/qir-driver.cpp + ../../Tests/QIR-static/qir-test-conditionals.cpp + ../../Tests/QIR-static/qir-test-math.cpp + ../../Tests/QIR-static/qir-test-strings.cpp + ../../Tests/QIR-static/qir-test-other.cpp +) + +foreach(file ${TEST_FILES}) + target_source_from_qir(qir-static-tests ${file}) +endforeach() + +target_link_libraries(qir-static-tests PUBLIC + "-L${runtime_lib_path}" + -lqir_range_support + -lqir_runtime +) + +target_include_directories(qir-static-tests PUBLIC + ${test_includes} + ${common_includes} + ${public_includes} +) +target_compile_definitions(qir-static-tests PRIVATE RIQIR_TESTING) + +install(TARGETS qir-static-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") +add_unit_test(qir-static-tests) + diff --git a/src/Qir/riqir-test/build-qir-tests.ps1 b/src/Qir/riqir-test/build-qir-tests.ps1 new file mode 100644 index 00000000000..ad3beddc51d --- /dev/null +++ b/src/Qir/riqir-test/build-qir-tests.ps1 @@ -0,0 +1,24 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +[CmdletBinding()] +param ( + [Parameter()] + [Switch] + $SkipQSharpBuild +) + +Write-Host "##[info]Compile Q# Test Projects into QIR" + +. (Join-Path $PSScriptRoot .. qir-utils.ps1) + +& (Join-Path $PSScriptRoot ".." check-sources-formatted.ps1) -Path $PSScriptRoot + +Build-QirProject (Join-Path $PSScriptRoot .. Tests QIR-static qsharp) -SkipQSharpBuild:$SkipQSharpBuild +Build-QirProject (Join-Path $PSScriptRoot .. Tests QIR-dynamic qsharp) -SkipQSharpBuild:$SkipQSharpBuild +Build-QirProject (Join-Path $PSScriptRoot .. Tests QIR-tracer qsharp) -SkipQSharpBuild:$SkipQSharpBuild +Build-QirProject (Join-Path $PSScriptRoot .. Tests FullstateSimulator qsharp) -SkipQSharpBuild:$SkipQSharpBuild + +if (-not (Build-CMakeProject $PSScriptRoot "RIQIR Tests")) { + throw "At least one project failed to compile. Check the logs." +} \ No newline at end of file diff --git a/src/Qir/riqir-test/test-qir-tests.ps1 b/src/Qir/riqir-test/test-qir-tests.ps1 new file mode 100644 index 00000000000..2b7abdf3c19 --- /dev/null +++ b/src/Qir/riqir-test/test-qir-tests.ps1 @@ -0,0 +1,38 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +. (Join-Path $PSScriptRoot .. qir-utils.ps1) + +$oldLSAN_OPTIONS = $env:LSAN_OPTIONS +$oldASAN_OPTIONS = $env:ASAN_OPTIONS # Colon-separated list of options (https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags). + +$env:LSAN_OPTIONS = "" +$env:ASAN_OPTIONS = "" + +if ($Env:BUILD_CONFIGURATION -eq "Debug") +{ + # Sanitizers (https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation): + + if (-not ($IsWindows)) + { + $env:LSAN_OPTIONS += "suppressions=../../../../LSan.ignore" # https://clang.llvm.org/docs/AddressSanitizer.html#suppressing-memory-leaks + # TODO: macOS: `ASAN_OPTIONS=detect_leaks=1` (https://clang.llvm.org/docs/AddressSanitizer.html#memory-leak-detection). + $env:ASAN_OPTIONS = "check_initialization_order=true:detect_stack_use_after_return=true:" ` + + "alloc_dealloc_mismatch=true:new_delete_type_mismatch=true:strict_init_order=true:strict_string_checks=true" + # + ":detect_invalid_pointer_pairs=2" TODO(rokuzmin, #883): ==8218==ERROR: AddressSanitizer: invalid-pointer-pair: 0x602000000af4 0x602000000af0 + } +} + +$all_ok = $true + +if (-not (Test-CTest (Join-Path $PSScriptRoot bin $Env:BUILD_CONFIGURATION) "RIQIR Tests")) { + $all_ok = $false +} + +$env:ASAN_OPTIONS = $oldASAN_OPTIONS +$env:LSAN_OPTIONS = $oldLSAN_OPTIONS + +if (-not $all_ok) +{ + throw "At least one project failed testing. Check the logs." +} diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 index f457606e0fb..7b27e3f2a0c 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -38,13 +38,7 @@ try { # Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so # they can be included in pipeline artifacts. - $osDir = "win-x64" - if ($IsLinux) { - $osDir = "linux-x64" - } elseif ($IsMacOS) { - $osDir = "osx-x64" - } - $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $osDir native) + $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) if (-not (Test-Path $Env:QIR_DROPS)) { New-Item -Path $Env:QIR_DROPS -ItemType "directory" } diff --git a/src/Qir/riqir/src/sim/mod.rs b/src/Qir/riqir/src/sim/mod.rs index a5d21bd5ece..d013b30d91e 100644 --- a/src/Qir/riqir/src/sim/mod.rs +++ b/src/Qir/riqir/src/sim/mod.rs @@ -449,8 +449,6 @@ struct PauliRotationArgs { /// # Safety /// /// This function should only be called with arrays and tuples created by the QIR runtime library. -/// # Panics -/// #[allow(clippy::cast_ptr_alignment)] #[no_mangle] pub unsafe extern "C" fn __quantum__qis__r__ctl( @@ -511,8 +509,6 @@ pub unsafe extern "C" fn __quantum__qis__r__ctladj( } /// QIR API for applying a SWAP gate to the given qubits. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__qis__swap__body(qubit0: *mut c_void, qubit1: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); @@ -531,9 +527,45 @@ pub extern "C" fn __quantum__qis__swap__body(qubit0: *mut c_void, qubit1: *mut c *sim_lock = Some(sim); } +/// QIR API for performing the exponential of a multi-qubit Pauli operator with the given angle. +#[no_mangle] +pub extern "C" fn __quantum__qis__exp__body( + _paulis: *const (usize, Vec), + _theta: c_double, + _qubits: *const (usize, Vec), +) { + unimplemented!("Exp is not implemented.") +} + +/// QIR API for performing the adjoint exponential of a multi-qubit Pauli operator with the given angle. +#[no_mangle] +pub extern "C" fn __quantum__qis__exp__adj( + paulis: *const (usize, Vec), + theta: c_double, + qubits: *const (usize, Vec), +) { + __quantum__qis__exp__body(paulis, -theta, qubits); +} + +/// QIR API for performing the multicontrolled exponential of a multi-qubit Pauli operator with the given angle. +#[no_mangle] +pub extern "C" fn __quantum__qis__exp__ctl( + _ctls: *const (usize, Vec), + _arg_tuple: *mut *const Vec, +) { + unimplemented!("Controlled Exp is not implemented.") +} + +/// QIR API for performing the adjoint multicontrolled exponential of a multi-qubit Pauli operator with the given angle. +#[no_mangle] +pub extern "C" fn __quantum__qis__exp__ctladj( + _ctls: *const (usize, Vec), + _arg_tuple: *mut *const Vec, +) { + unimplemented!("Controlled Exp is not implemented.") +} + /// QIR API for resetting the given qubit in the computational basis. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); @@ -550,15 +582,13 @@ pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) { } /// QIR API for measuring the given qubit in the computation basis and storing the measured value with the given result identifier. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__qis__mz__body(qubit: *mut c_void, result: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() .map_or_else(SparseStateQuantumSim::new, |s| s); - let mut res = RESULTS.lock().unwrap(); + let mut res = RESULTS.lock().expect("Unable to lock global result state."); let res_id = result as usize; ensure_sufficient_qubits(&mut sim, qubit as usize); @@ -566,24 +596,25 @@ pub extern "C" fn __quantum__qis__mz__body(qubit: *mut c_void, result: *mut c_vo res.resize(res_id + 1, false); } - *res.get_mut(res_id).unwrap() = sim.measure(qubit as usize); + *res.get_mut(res_id) + .expect("Result with given id missing after expansion.") = sim.measure(qubit as usize); *sim_lock = Some(sim); } /// QIR API that reads the Boolean value corresponding to the given result identifier, where true /// indicates a |1⟩ state and false indicates a |0⟩ state. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__qis__read_result__body(result: *mut c_void) -> bool { - let mut res = RESULTS.lock().unwrap(); + let mut res = RESULTS.lock().expect("Unable to lock global result state."); let res_id = result as usize; if res.len() < res_id + 1 { res.resize(res_id + 1, false); } - let b = *res.get(res_id).unwrap(); + let b = *res + .get(res_id) + .expect("Result with given id missing after expansion."); b } @@ -720,11 +751,9 @@ pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__ctl( } /// QIR API for recording the given result into the program output. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) { - let mut res = RESULTS.lock().unwrap(); + let mut res = RESULTS.lock().expect("Unable to lock global result state."); let res_id = result as usize; let b = if res.len() == 0 { // No static measurements have been used, so default to dynamic handling. @@ -733,15 +762,14 @@ pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) { if res.len() < res_id + 1 { res.resize(res_id + 1, false); } - *res.get(res_id).unwrap() + *res.get(res_id) + .expect("Result with given id missing after expansion.") }; println!("RESULT\t{}", if b { "1" } else { "0" }); } /// QIR API that allocates the next available qubit in the simulation. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); @@ -791,8 +819,6 @@ pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const (usize, } /// QIR API for releasing the given qubit from the simulation. -/// # Panics -/// #[no_mangle] pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); @@ -804,22 +830,40 @@ pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { } /// API for viewing the current global result and quantum state for the simulator. -/// # Panics -/// #[no_mangle] pub extern "C" fn dump_state() { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() .map_or_else(SparseStateQuantumSim::new, |s| s); - let res = RESULTS.lock().unwrap(); + let res = RESULTS.lock().expect("Unable to lock global result state."); - println!("Global Results: {}", *res); + if !(*res).is_empty() { + println!("Global Results: {}", *res); + } sim.dump(); *sim_lock = Some(sim); } +/// QIR API for dumping full internal simulator state. +#[no_mangle] +pub extern "C" fn __quantum__qis__dumpmachine__body(location: *mut c_void) { + if !location.is_null() { + unimplemented!("Dump to location is not implemented.") + } + dump_state(); +} + +/// QIR API for dumping the internal simulator state for the given qubits. +#[no_mangle] +pub extern "C" fn __quantum__qis__dumpregister__body( + _location: *mut c_void, + _qubits: *const (usize, Vec), +) { + unimplemented!("Dump of qubit register is not implemented.") +} + #[cfg(test)] mod tests { use std::ffi::c_void; From 94a1bd97e99b76dd0efdea6a80ca95382b518742 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 26 Aug 2022 16:15:32 -0700 Subject: [PATCH 07/55] Add to build scripts --- build/build.ps1 | 5 +++++ build/test.ps1 | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/build/build.ps1 b/build/build.ps1 index ef4417484b0..8f01dce4473 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -93,6 +93,11 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if ($LastExitCode -ne 0) { $script:all_ok = $False } + $qirTests = (Join-Path $PSScriptRoot "../src/Qir/riqir-test") + & "$qirTests/build-qir-tests.ps1" -SkipQSharpBuild + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } $qirSamples = (Join-Path $PSScriptRoot "../src/Qir/Samples") & "$qirSamples/build-qir-samples.ps1" -SkipQSharpBuild if ($LastExitCode -ne 0) { diff --git a/build/test.ps1 b/build/test.ps1 index 89094b63d8f..2f838a7b895 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -61,6 +61,12 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { $script:all_ok = $False } + $qirTests = (Join-Path $PSScriptRoot "../src/Qir/riqir-test") + & "$qirTests/test-qir-tests.ps1" + if ($LastExitCode -ne 0) { + $script:all_ok = $False + } + $qirSamples = (Join-Path $PSScriptRoot "../src/Qir/Samples") & "$qirSamples/test-qir-samples.ps1" if ($LastExitCode -ne 0) { From eb94c176acbf51cc55e07fd0a169b5c02f3c2f0b Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 26 Aug 2022 16:29:19 -0700 Subject: [PATCH 08/55] Fix build ordering --- build/build.ps1 | 4 ++-- src/Qir/Runtime/lib/range_support/CMakeLists.txt | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index 8f01dce4473..b92d59aa480 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -7,12 +7,12 @@ $ErrorActionPreference = 'Stop' $all_ok = $True if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") + $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") & "$qirRuntime/build-qir-runtime.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") + $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") & "$qirRuntime/build-qir-runtime.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False diff --git a/src/Qir/Runtime/lib/range_support/CMakeLists.txt b/src/Qir/Runtime/lib/range_support/CMakeLists.txt index 291f6e9c590..c8d7d1297f6 100644 --- a/src/Qir/Runtime/lib/range_support/CMakeLists.txt +++ b/src/Qir/Runtime/lib/range_support/CMakeLists.txt @@ -5,14 +5,7 @@ add_library(qir_range_support SHARED bridge-rt.cpp) target_source_from_qir(qir_range_support bridge-rt.ll) -if(WIN32) - set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/win-x64/native") -elseif(APPLE) - set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/osx-x64/native") -else() - set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/linux-x64/native") -endif() - +set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") target_link_libraries(qir_range_support ${CMAKE_DL_LIBS} From 8e2a89b6c69f1a47be377a95f9470638bfa2a392 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 26 Aug 2022 22:13:32 -0700 Subject: [PATCH 09/55] Fix CI cleanup --- src/Qir/riqir/build-qir-runtime.ps1 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 index 7b27e3f2a0c..5e1556a985c 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -30,12 +30,6 @@ try { cargo build @releaseFlag if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR Runtime." } - # When building in CI, free disk space by cleaning up. - # Note that this takes longer, but saves ~1 GB of space. - if ($IsCI) { - cargo clean; - } - # Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so # they can be included in pipeline artifacts. $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) @@ -56,6 +50,11 @@ try { Rename-Item $rustlib qir_runtime.lib } + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; + } } finally { Pop-Location From f1f2f669961be9e5cc13d972141e44199cc9d3b1 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 26 Aug 2022 22:31:41 -0700 Subject: [PATCH 10/55] More build fixes --- src/Qir/Runtime/build-qir-runtime.ps1 | 8 +------- src/Qir/riqir/build-qir-runtime.ps1 | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/Qir/Runtime/build-qir-runtime.ps1 b/src/Qir/Runtime/build-qir-runtime.ps1 index 947dacf5b55..02041c353b1 100644 --- a/src/Qir/Runtime/build-qir-runtime.ps1 +++ b/src/Qir/Runtime/build-qir-runtime.ps1 @@ -14,13 +14,7 @@ if (-not (Build-CMakeProject $PSScriptRoot "QIR Runtime")) { # Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so # they can be included in pipeline artifacts. -$osDir = "win-x64" -if ($IsLinux) { - $osDir = "linux-x64" -} elseif ($IsMacOS) { - $osDir = "osx-x64" -} -$qirDropsBin = (Join-Path $Env:QIR_DROPS bin $osDir native) +$qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) $qirDropsInclude = (Join-Path $Env:QIR_DROPS include) if (-not (Test-Path $Env:QIR_DROPS)) { New-Item -Path $Env:QIR_DROPS -ItemType "directory" diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 index 5e1556a985c..dc9a8147db9 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -40,7 +40,7 @@ try { New-Item -Path $qirDropsBin -ItemType "directory" } $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) - Copy-Item $qirBinaries $qirDropsBin -Include "qir_runtime*" -Exclude "*.rlib","*.d","*.exp" + Copy-Item $qirBinaries $qirDropsBin -Include "*qir_runtime*" -Exclude "*.rlib","*.d","*.exp" # For Windows platforms, make sure to update the extension of the lib file used for linking # from *.dll.lib to just .lib From 55e30fdbf1aa9f1d7bdb84d5481328acd81f65ec Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 27 Aug 2022 18:02:40 -0700 Subject: [PATCH 11/55] Fix build bug --- src/Qir/Common/cmake/unit_test_include.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/Common/cmake/unit_test_include.cmake b/src/Qir/Common/cmake/unit_test_include.cmake index 8781e9c902e..ed8256eb1d2 100644 --- a/src/Qir/Common/cmake/unit_test_include.cmake +++ b/src/Qir/Common/cmake/unit_test_include.cmake @@ -14,7 +14,7 @@ macro(add_unit_test target) set(TEST_DEPS2 "${CMAKE_BINARY_DIR}/bin") set(TEST_DEPS3 "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") - set(TEST_DEPS3 "${PROJECT_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") + set(TEST_DEPS4 "${PROJECT_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") set_property(TEST ${target} PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${TEST_DEPS3}:${TEST_DEPS4}:${LD_LIBRARY_PATH}" "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${TEST_DEPS3}\;${TEST_DEPS4}\;${PATH}" From 65c3e8cfc801bacff17eb4f9b37489bb341336cd Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 29 Aug 2022 09:51:07 -0700 Subject: [PATCH 12/55] Minor error message fix --- src/Qir/riqir/src/sim/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/riqir/src/sim/mod.rs b/src/Qir/riqir/src/sim/mod.rs index d013b30d91e..c22e28d7f43 100644 --- a/src/Qir/riqir/src/sim/mod.rs +++ b/src/Qir/riqir/src/sim/mod.rs @@ -53,7 +53,7 @@ unsafe fn map_paulis( let qubits_size = __quantum__rt__array_get_size_1d(qubits); if paulis_size != qubits_size { __quantum__rt__fail(__quantum__rt__string_create( - CString::new("Pauli array and Qubit array for Measurement must be the same size.") + CString::new("Pauli array and Qubit array must be the same size.") .unwrap() .as_bytes_with_nul() .as_ptr() as *mut i8, From af6cd9486b994bcfe57d7a33667a89b86defcc00 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 29 Aug 2022 14:13:16 -0700 Subject: [PATCH 13/55] Move result related api into sim --- src/Qir/riqir/src/lib.rs | 2 -- src/Qir/riqir/src/{ => sim}/conditionals.rs | 3 ++- src/Qir/riqir/src/sim/mod.rs | 10 +++++++--- src/Qir/riqir/src/{ => sim}/result_bool.rs | 0 4 files changed, 9 insertions(+), 6 deletions(-) rename src/Qir/riqir/src/{ => sim}/conditionals.rs (96%) rename src/Qir/riqir/src/{ => sim}/result_bool.rs (100%) diff --git a/src/Qir/riqir/src/lib.rs b/src/Qir/riqir/src/lib.rs index 5b70e153d91..7e22cc5aa42 100644 --- a/src/Qir/riqir/src/lib.rs +++ b/src/Qir/riqir/src/lib.rs @@ -7,11 +7,9 @@ pub mod arrays; pub mod bigints; pub mod callables; -pub mod conditionals; pub mod math; pub mod output_recording; pub mod range_support; -pub mod result_bool; pub mod strings; pub mod tuples; diff --git a/src/Qir/riqir/src/conditionals.rs b/src/Qir/riqir/src/sim/conditionals.rs similarity index 96% rename from src/Qir/riqir/src/conditionals.rs rename to src/Qir/riqir/src/sim/conditionals.rs index cceefa32872..730b0a8d06e 100644 --- a/src/Qir/riqir/src/conditionals.rs +++ b/src/Qir/riqir/src/sim/conditionals.rs @@ -2,11 +2,12 @@ // Licensed under the MIT License. #![deny(clippy::all, clippy::pedantic)] +use super::result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one}; + use crate::{ __quantum__rt__fail, arrays::{__quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, callables::{Callable, __quantum__rt__callable_invoke}, - result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one}, strings::convert, }; use std::{ffi::c_void, ptr::null_mut}; diff --git a/src/Qir/riqir/src/sim/mod.rs b/src/Qir/riqir/src/sim/mod.rs index c22e28d7f43..f04830bfea1 100644 --- a/src/Qir/riqir/src/sim/mod.rs +++ b/src/Qir/riqir/src/sim/mod.rs @@ -3,6 +3,9 @@ //! Module defining QIR compliant APIs for quantum simulation. +pub mod conditionals; +pub mod result_bool; + mod common_matrices; mod simulator; mod sparsestate; @@ -17,15 +20,16 @@ use std::os::raw::c_double; use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use std::sync::Mutex; +use result_bool::{ + __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero, +}; + use super::{ Pauli, __quantum__rt__fail, arrays::{ __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d, __quantum__rt__array_update_alias_count, }, - result_bool::{ - __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero, - }, strings::__quantum__rt__string_create, tuples::{__quantum__rt__tuple_create, __quantum__rt__tuple_update_reference_count}, }; diff --git a/src/Qir/riqir/src/result_bool.rs b/src/Qir/riqir/src/sim/result_bool.rs similarity index 100% rename from src/Qir/riqir/src/result_bool.rs rename to src/Qir/riqir/src/sim/result_bool.rs From 61e43f0dc6073b5be24eb28a028a08e7d9c8030b Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 29 Aug 2022 22:01:41 -0700 Subject: [PATCH 14/55] refactor qir_runtime, split out qir_sim --- Cargo.toml | 3 +- .../Runtime/lib/range_support/CMakeLists.txt | 2 +- .../FullstateSimulator/CMakeLists.txt | 2 +- src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt | 2 +- src/Qir/riqir-test/QIR-static/CMakeLists.txt | 2 +- src/Qir/riqir/Cargo.toml | 25 ----- src/Qir/riqir/build-qir-runtime.ps1 | 100 +++++++++--------- src/Qir/riqir/runtime/Cargo.toml | 12 +++ src/Qir/riqir/{ => runtime}/src/arrays.rs | 0 src/Qir/riqir/{ => runtime}/src/bigints.rs | 0 src/Qir/riqir/{ => runtime}/src/callables.rs | 0 src/Qir/riqir/{ => runtime}/src/lib.rs | 3 - src/Qir/riqir/{ => runtime}/src/math.rs | 0 .../{ => runtime}/src/output_recording.rs | 0 .../riqir/{ => runtime}/src/range_support.rs | 0 src/Qir/riqir/{ => runtime}/src/strings.rs | 0 src/Qir/riqir/{ => runtime}/src/tuples.rs | 0 src/Qir/riqir/sim/Cargo.toml | 19 ++++ .../{src/sim => sim/src}/common_matrices.rs | 0 .../{src/sim => sim/src}/conditionals.rs | 19 ++-- .../riqir/{src/sim/mod.rs => sim/src/lib.rs} | 13 +-- .../riqir/{src/sim => sim/src}/result_bool.rs | 2 +- .../riqir/{src/sim => sim/src}/simulator.rs | 2 +- .../riqir/{src/sim => sim/src}/sparsestate.rs | 0 src/Qir/riqir/test-qir-runtime.ps1 | 38 +++---- 25 files changed, 127 insertions(+), 117 deletions(-) delete mode 100644 src/Qir/riqir/Cargo.toml create mode 100644 src/Qir/riqir/runtime/Cargo.toml rename src/Qir/riqir/{ => runtime}/src/arrays.rs (100%) rename src/Qir/riqir/{ => runtime}/src/bigints.rs (100%) rename src/Qir/riqir/{ => runtime}/src/callables.rs (100%) rename src/Qir/riqir/{ => runtime}/src/lib.rs (98%) rename src/Qir/riqir/{ => runtime}/src/math.rs (100%) rename src/Qir/riqir/{ => runtime}/src/output_recording.rs (100%) rename src/Qir/riqir/{ => runtime}/src/range_support.rs (100%) rename src/Qir/riqir/{ => runtime}/src/strings.rs (100%) rename src/Qir/riqir/{ => runtime}/src/tuples.rs (100%) create mode 100644 src/Qir/riqir/sim/Cargo.toml rename src/Qir/riqir/{src/sim => sim/src}/common_matrices.rs (100%) rename src/Qir/riqir/{src/sim => sim/src}/conditionals.rs (81%) rename src/Qir/riqir/{src/sim/mod.rs => sim/src/lib.rs} (98%) rename src/Qir/riqir/{src/sim => sim/src}/result_bool.rs (95%) rename src/Qir/riqir/{src/sim => sim/src}/simulator.rs (99%) rename src/Qir/riqir/{src/sim => sim/src}/sparsestate.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 38b3e3a1f5b..3f168d57383 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ members = [ "src/Simulation/qdk_sim_rs", - "src/Qir/riqir", + "src/Qir/riqir/runtime", + "src/Qir/riqir/sim", ] [profile.release] diff --git a/src/Qir/Runtime/lib/range_support/CMakeLists.txt b/src/Qir/Runtime/lib/range_support/CMakeLists.txt index c8d7d1297f6..3e28a0727ca 100644 --- a/src/Qir/Runtime/lib/range_support/CMakeLists.txt +++ b/src/Qir/Runtime/lib/range_support/CMakeLists.txt @@ -10,7 +10,7 @@ set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") target_link_libraries(qir_range_support ${CMAKE_DL_LIBS} "-L${QIR_LIB_PATH}" - -lqir_runtime + -lqir_sim ${SPECTRE_LIBS} ) diff --git a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt index 31a75a9ffe8..a036bb2e1fc 100644 --- a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt +++ b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt @@ -6,7 +6,7 @@ target_source_from_qir(fullstate-simulator-tests ../../Tests/FullstateSimulator/ target_link_libraries(fullstate-simulator-tests PUBLIC "-L${runtime_lib_path}" -lqir_range_support - -lqir_runtime + -lqir_sim ) target_include_directories(fullstate-simulator-tests PUBLIC diff --git a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt index 8ba80ff4737..b43ce0c27ab 100644 --- a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt +++ b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt @@ -17,7 +17,7 @@ endforeach() target_link_libraries(qir-dynamic-tests PUBLIC "-L${runtime_lib_path}" -lqir_range_support - -lqir_runtime + -lqir_sim ) target_include_directories(qir-dynamic-tests PUBLIC diff --git a/src/Qir/riqir-test/QIR-static/CMakeLists.txt b/src/Qir/riqir-test/QIR-static/CMakeLists.txt index 62986ed8cc2..c7ce4b710c0 100644 --- a/src/Qir/riqir-test/QIR-static/CMakeLists.txt +++ b/src/Qir/riqir-test/QIR-static/CMakeLists.txt @@ -20,7 +20,7 @@ endforeach() target_link_libraries(qir-static-tests PUBLIC "-L${runtime_lib_path}" -lqir_range_support - -lqir_runtime + -lqir_sim ) target_include_directories(qir-static-tests PUBLIC diff --git a/src/Qir/riqir/Cargo.toml b/src/Qir/riqir/Cargo.toml deleted file mode 100644 index edcc7b31566..00000000000 --- a/src/Qir/riqir/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "qir-runtime" -version = "0.1.0" -authors = ["Microsoft"] -edition = "2021" - -[dependencies] -num-bigint = { version = "0.4.3", default-features = false } -rand = "0.8.5" - -rustc-hash = {version = "1.1.0", optional = true } -num-complex = {version = "0.4", optional = true } -num-traits = {version = "0.2.14", optional = true } -rayon = {version = "1.5.1", optional = true } -ndarray = { version = "0.15.4", features = [ "rayon", "matrixmultiply-threading" ], optional = true } -mimalloc = { version = "0.1.27", default-features = false, optional = true } -lazy_static = {version = "1.4.0", optional = true } -bitvec = {version = "1.0.0", optional = true } - -[lib] -crate-type = ["cdylib","rlib"] - -[features] -default = ["sim"] -sim = ["rustc-hash", "num-complex", "num-traits", "rayon", "ndarray", "lazy_static", "bitvec"] \ No newline at end of file diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 index dc9a8147db9..67882f2ac25 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -7,55 +7,57 @@ $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; -Push-Location $PSScriptRoot -try { - # Start with the quick check first and make sure that Rust sources - # meet formatting and style guide rules. - cargo fmt -- --check - if ($LASTEXITCODE -ne 0) { throw "Failed cargo fmt check on QIR Runtime." } - - # Check linting rules defined by clippy, a linting tool provided with the - # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy - # and https://rust-lang.github.io/rust-clippy/master/index.html - # for more information. - # If there's a false positive, that check should be explicitly disabled - # at the point where the false positive occurs with an explanation as to - # why it's OK. - cargo clippy -- -D warnings - if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR Runtime." } - - $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - - # Actually run the build. - cargo build @releaseFlag - if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR Runtime." } - - # Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so - # they can be included in pipeline artifacts. - $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) - if (-not (Test-Path $Env:QIR_DROPS)) { - New-Item -Path $Env:QIR_DROPS -ItemType "directory" +foreach ($folder in "runtime","sim") { + Push-Location (Join-Path $PSScriptRoot $folder) + try { + # Start with the quick check first and make sure that Rust sources + # meet formatting and style guide rules. + cargo fmt -- --check + if ($LASTEXITCODE -ne 0) { throw "Failed cargo fmt check on QIR $folder." } + + # Check linting rules defined by clippy, a linting tool provided with the + # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy + # and https://rust-lang.github.io/rust-clippy/master/index.html + # for more information. + # If there's a false positive, that check should be explicitly disabled + # at the point where the false positive occurs with an explanation as to + # why it's OK. + cargo clippy -- -D warnings + if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR $folder." } + + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # Actually run the build. + cargo build @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR $folder." } + + # Copy the results of compilation and the corresponding headers to the QIR drops folder so + # they can be included in pipeline artifacts. + $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) + if (-not (Test-Path $Env:QIR_DROPS)) { + New-Item -Path $Env:QIR_DROPS -ItemType "directory" + } + if (-not (Test-Path $qirDropsBin)) { + New-Item -Path $qirDropsBin -ItemType "directory" + } + $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) + Copy-Item $qirBinaries $qirDropsBin -Include "*qir_$folder*" -Exclude "*.rlib","*.d","*.exp" + + # For Windows platforms, make sure to update the extension of the lib file used for linking + # from *.dll.lib to just .lib + $rustlib = (Join-Path $qirDropsBin "qir_$folder.dll.lib") + if (Test-Path $rustlib) { + Remove-Item (Join-Path $qirDropsBin "qir_$folder.lib") -ErrorAction SilentlyContinue + Rename-Item $rustlib "qir_$folder.lib" + } + + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; + } } - if (-not (Test-Path $qirDropsBin)) { - New-Item -Path $qirDropsBin -ItemType "directory" + finally { + Pop-Location } - $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) - Copy-Item $qirBinaries $qirDropsBin -Include "*qir_runtime*" -Exclude "*.rlib","*.d","*.exp" - - # For Windows platforms, make sure to update the extension of the lib file used for linking - # from *.dll.lib to just .lib - $rustlib = (Join-Path $qirDropsBin qir_runtime.dll.lib) - if (Test-Path $rustlib) { - Remove-Item (Join-Path $qirDropsBin qir_runtime.lib) -ErrorAction SilentlyContinue - Rename-Item $rustlib qir_runtime.lib - } - - # When building in CI, free disk space by cleaning up. - # Note that this takes longer, but saves ~1 GB of space. - if ($IsCI) { - cargo clean; - } -} -finally { - Pop-Location } \ No newline at end of file diff --git a/src/Qir/riqir/runtime/Cargo.toml b/src/Qir/riqir/runtime/Cargo.toml new file mode 100644 index 00000000000..df3da8a8e30 --- /dev/null +++ b/src/Qir/riqir/runtime/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "qir-runtime" +version = "0.1.0" +authors = ["Microsoft"] +edition = "2021" + +[dependencies] +num-bigint = { version = "0.4.3", default-features = false } +rand = "0.8.5" + +[lib] +crate-type = ["cdylib","rlib"] diff --git a/src/Qir/riqir/src/arrays.rs b/src/Qir/riqir/runtime/src/arrays.rs similarity index 100% rename from src/Qir/riqir/src/arrays.rs rename to src/Qir/riqir/runtime/src/arrays.rs diff --git a/src/Qir/riqir/src/bigints.rs b/src/Qir/riqir/runtime/src/bigints.rs similarity index 100% rename from src/Qir/riqir/src/bigints.rs rename to src/Qir/riqir/runtime/src/bigints.rs diff --git a/src/Qir/riqir/src/callables.rs b/src/Qir/riqir/runtime/src/callables.rs similarity index 100% rename from src/Qir/riqir/src/callables.rs rename to src/Qir/riqir/runtime/src/callables.rs diff --git a/src/Qir/riqir/src/lib.rs b/src/Qir/riqir/runtime/src/lib.rs similarity index 98% rename from src/Qir/riqir/src/lib.rs rename to src/Qir/riqir/runtime/src/lib.rs index 7e22cc5aa42..3ea9371ba8b 100644 --- a/src/Qir/riqir/src/lib.rs +++ b/src/Qir/riqir/runtime/src/lib.rs @@ -13,9 +13,6 @@ pub mod range_support; pub mod strings; pub mod tuples; -#[cfg(feature = "sim")] -pub mod sim; - use std::{ ffi::CString, rc::{Rc, Weak}, diff --git a/src/Qir/riqir/src/math.rs b/src/Qir/riqir/runtime/src/math.rs similarity index 100% rename from src/Qir/riqir/src/math.rs rename to src/Qir/riqir/runtime/src/math.rs diff --git a/src/Qir/riqir/src/output_recording.rs b/src/Qir/riqir/runtime/src/output_recording.rs similarity index 100% rename from src/Qir/riqir/src/output_recording.rs rename to src/Qir/riqir/runtime/src/output_recording.rs diff --git a/src/Qir/riqir/src/range_support.rs b/src/Qir/riqir/runtime/src/range_support.rs similarity index 100% rename from src/Qir/riqir/src/range_support.rs rename to src/Qir/riqir/runtime/src/range_support.rs diff --git a/src/Qir/riqir/src/strings.rs b/src/Qir/riqir/runtime/src/strings.rs similarity index 100% rename from src/Qir/riqir/src/strings.rs rename to src/Qir/riqir/runtime/src/strings.rs diff --git a/src/Qir/riqir/src/tuples.rs b/src/Qir/riqir/runtime/src/tuples.rs similarity index 100% rename from src/Qir/riqir/src/tuples.rs rename to src/Qir/riqir/runtime/src/tuples.rs diff --git a/src/Qir/riqir/sim/Cargo.toml b/src/Qir/riqir/sim/Cargo.toml new file mode 100644 index 00000000000..9c8c4209abd --- /dev/null +++ b/src/Qir/riqir/sim/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "qir-sim" +version = "0.1.0" +authors = ["Microsoft"] +edition = "2021" + +[dependencies] +qir-runtime = { path = "../runtime" } +rand = "0.8.5" +rustc-hash = "1.1.0" +num-complex = "0.4" +num-traits = "0.2.14" +rayon = "1.5.1" +ndarray = { version = "0.15.4", features = [ "rayon", "matrixmultiply-threading" ] } +lazy_static = "1.4.0" +bitvec = "1.0.0" + +[lib] +crate-type = ["cdylib","rlib"] diff --git a/src/Qir/riqir/src/sim/common_matrices.rs b/src/Qir/riqir/sim/src/common_matrices.rs similarity index 100% rename from src/Qir/riqir/src/sim/common_matrices.rs rename to src/Qir/riqir/sim/src/common_matrices.rs diff --git a/src/Qir/riqir/src/sim/conditionals.rs b/src/Qir/riqir/sim/src/conditionals.rs similarity index 81% rename from src/Qir/riqir/src/sim/conditionals.rs rename to src/Qir/riqir/sim/src/conditionals.rs index 730b0a8d06e..10d699972ae 100644 --- a/src/Qir/riqir/src/sim/conditionals.rs +++ b/src/Qir/riqir/sim/src/conditionals.rs @@ -4,13 +4,16 @@ use super::result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one}; -use crate::{ +use qir_runtime::{ __quantum__rt__fail, arrays::{__quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, callables::{Callable, __quantum__rt__callable_invoke}, - strings::convert, + strings::__quantum__rt__string_create, +}; +use std::{ + ffi::{c_void, CString}, + ptr::null_mut, }; -use std::{ffi::c_void, ptr::null_mut}; /// # Safety /// @@ -45,9 +48,13 @@ pub unsafe extern "C" fn __quantum__qis__applyconditionallyintrinsic__body( ) { let result_len = __quantum__rt__array_get_size_1d(results); if result_len != __quantum__rt__array_get_size_1d(expected) { - __quantum__rt__fail(convert( - &"Invalid Argument: expected and actual result arrays must have the same size." - .to_string(), + __quantum__rt__fail(__quantum__rt__string_create( + CString::new( + "Invalid Argument: expected and actual result arrays must have the same size.", + ) + .expect("Unable to allocate string for error message.") + .as_bytes_with_nul() + .as_ptr() as *mut i8, )); } diff --git a/src/Qir/riqir/src/sim/mod.rs b/src/Qir/riqir/sim/src/lib.rs similarity index 98% rename from src/Qir/riqir/src/sim/mod.rs rename to src/Qir/riqir/sim/src/lib.rs index f04830bfea1..04a67a17e5c 100644 --- a/src/Qir/riqir/src/sim/mod.rs +++ b/src/Qir/riqir/sim/src/lib.rs @@ -24,14 +24,9 @@ use result_bool::{ __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero, }; -use super::{ - Pauli, __quantum__rt__fail, - arrays::{ - __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d, - __quantum__rt__array_get_size_1d, __quantum__rt__array_update_alias_count, - }, - strings::__quantum__rt__string_create, - tuples::{__quantum__rt__tuple_create, __quantum__rt__tuple_update_reference_count}, +pub use qir_runtime::{ + arrays::*, bigints::*, callables::*, math::*, output_recording::*, range_support::*, + strings::*, tuples::*, *, }; lazy_static! { @@ -879,7 +874,7 @@ mod tests { __quantum__rt__qubit_release, __quantum__rt__qubit_release_array, __quantum__rt__result_equal, __quantum__rt__result_get_one, dump_state, }; - use crate::arrays::__quantum__rt__array_get_element_ptr_1d; + use qir_runtime::arrays::__quantum__rt__array_get_element_ptr_1d; // TODO(swernli): Split and expand simulator unit tests. #[allow(clippy::cast_ptr_alignment)] diff --git a/src/Qir/riqir/src/sim/result_bool.rs b/src/Qir/riqir/sim/src/result_bool.rs similarity index 95% rename from src/Qir/riqir/src/sim/result_bool.rs rename to src/Qir/riqir/sim/src/result_bool.rs index cbaf1d4faeb..08a200edac3 100644 --- a/src/Qir/riqir/src/sim/result_bool.rs +++ b/src/Qir/riqir/sim/src/result_bool.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. #![deny(clippy::all, clippy::pedantic)] -use crate::strings::__quantum__rt__string_create; +use qir_runtime::strings::__quantum__rt__string_create; use std::ffi::{c_void, CString}; #[no_mangle] diff --git a/src/Qir/riqir/src/sim/simulator.rs b/src/Qir/riqir/sim/src/simulator.rs similarity index 99% rename from src/Qir/riqir/src/sim/simulator.rs rename to src/Qir/riqir/sim/src/simulator.rs index d8be4bd88cf..68641b2b839 100644 --- a/src/Qir/riqir/src/sim/simulator.rs +++ b/src/Qir/riqir/sim/src/simulator.rs @@ -439,7 +439,7 @@ mod tests { common_matrices::{adjoint, controlled, h, s, x}, *, }; - use crate::sim::sparsestate::SparseStateQuantumSim; + use crate::sparsestate::SparseStateQuantumSim; use ndarray::array; use num_traits::{One, Zero}; diff --git a/src/Qir/riqir/src/sim/sparsestate.rs b/src/Qir/riqir/sim/src/sparsestate.rs similarity index 100% rename from src/Qir/riqir/src/sim/sparsestate.rs rename to src/Qir/riqir/sim/src/sparsestate.rs diff --git a/src/Qir/riqir/test-qir-runtime.ps1 b/src/Qir/riqir/test-qir-runtime.ps1 index c56d9eced0e..d2ddbac5711 100644 --- a/src/Qir/riqir/test-qir-runtime.ps1 +++ b/src/Qir/riqir/test-qir-runtime.ps1 @@ -1,26 +1,28 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -Push-Location $PSScriptRoot -try { - $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); +foreach ($folder in "runtime","sim") { + Push-Location (Join-Path $PSScriptRoot $folder) + try { + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) - # for interoperating Rust and C. - # NB: CFG is only supported on Windows, but the Rust flag is supported on - # all platforms; it's ignored on platforms without CFG functionality. - $Env:RUSTFLAGS = "-C control-flow-guard"; + # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) + # for interoperating Rust and C. + # NB: CFG is only supported on Windows, but the Rust flag is supported on + # all platforms; it's ignored on platforms without CFG functionality. + $Env:RUSTFLAGS = "-C control-flow-guard"; - # Actually run the test. - cargo test @releaseFlag - if ($LASTEXITCODE -ne 0) { throw "Failed cargo test on QIR Runtime." } + # Actually run the test. + cargo test @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo test on QIR $folder." } - # When building in CI, free disk space by cleaning up. - # Note that this takes longer, but saves ~1 GB of space. - if ($IsCI) { - cargo clean; + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; + } + } + finally { + Pop-Location } -} -finally { - Pop-Location } \ No newline at end of file From 4a0cfad6c97a1054cf61be1170aca7591006c9f4 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 29 Aug 2022 23:10:18 -0700 Subject: [PATCH 15/55] Use BigUint for sparse sim indexing --- src/Qir/riqir/sim/Cargo.toml | 1 + src/Qir/riqir/sim/src/simulator.rs | 16 +++++++ src/Qir/riqir/sim/src/sparsestate.rs | 67 +++++++++++++++------------- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/Qir/riqir/sim/Cargo.toml b/src/Qir/riqir/sim/Cargo.toml index 9c8c4209abd..5d9728306b8 100644 --- a/src/Qir/riqir/sim/Cargo.toml +++ b/src/Qir/riqir/sim/Cargo.toml @@ -10,6 +10,7 @@ rand = "0.8.5" rustc-hash = "1.1.0" num-complex = "0.4" num-traits = "0.2.14" +num-bigint = { version = "0.4.3", default-features = false } rayon = "1.5.1" ndarray = { version = "0.15.4", features = [ "rayon", "matrixmultiply-threading" ] } lazy_static = "1.4.0" diff --git a/src/Qir/riqir/sim/src/simulator.rs b/src/Qir/riqir/sim/src/simulator.rs index 68641b2b839..fb8e3c1a34c 100644 --- a/src/Qir/riqir/sim/src/simulator.rs +++ b/src/Qir/riqir/sim/src/simulator.rs @@ -684,4 +684,20 @@ mod tests { let q = sim.allocate(); sim.apply(&array![[Complex64::zero()], [Complex64::one()]], &[q], None); } + + /// Large, entangled state handling. + #[test] + fn test_large_state() { + let mut sim = SparseStateQuantumSim::new(); + let ctl = sim.allocate(); + sim.apply(&h(), &[ctl], None); + for _ in 0..4999 { + let q = sim.allocate(); + sim.apply(&x(), &[q], Some(&[ctl])); + } + let _ = sim.measure(ctl); + for i in 0..5000 { + sim.release(i); + } + } } diff --git a/src/Qir/riqir/sim/src/sparsestate.rs b/src/Qir/riqir/sim/src/sparsestate.rs index 2f45539a6c1..a792e9ef347 100644 --- a/src/Qir/riqir/sim/src/sparsestate.rs +++ b/src/Qir/riqir/sim/src/sparsestate.rs @@ -1,17 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::convert::TryInto; - use ndarray::Array2; +use num_bigint::BigUint; use num_complex::Complex64; -use num_traits::{One, Zero}; +use num_traits::{One, ToPrimitive, Zero}; use rayon::prelude::*; use rustc_hash::FxHashMap; use super::simulator::{detail::QuantumSimImpl, parallel_chunk_size, OpBuffer, QuantumSim}; -pub type SparseState = FxHashMap; +pub type SparseState = FxHashMap; /// Sparse quantum state simulation using a dictionary of states with non-zero amplitudes, based on . /// This simulator is memory efficient for highly entangled states, allowing for the use of up to 128 qubits, but less performant @@ -47,11 +46,9 @@ impl QuantumSimImpl for QuantumSim { /// /// This function will panic if the total number of allocated qubits would increase beyond 128. fn extend_state(&mut self) { - if self.id_map.len() >= 128 { - panic!("Attempting to allocate more than 128 qubits. Internal data structures of sparse simulation can handle at most 128 qubits."); - } else if self.id_map.is_empty() { + if self.id_map.is_empty() { // Add the intial value for the zero state. - self.state.insert(0, Complex64::one()); + self.state.insert(BigUint::zero(), Complex64::one()); } } @@ -60,8 +57,8 @@ impl QuantumSimImpl for QuantumSim { if qubit1 == qubit2 { return; } - let offset1 = 1_u128 << qubit1; - let offset2 = 1_u128 << qubit2; + + let (q1, q2) = (qubit1 as u64, qubit2 as u64); // In parallel, swap entries in the sparse state to correspond to swapping of two qubits' // locations. @@ -73,10 +70,13 @@ impl QuantumSimImpl for QuantumSim { .par_chunks(chunk_size) .fold(FxHashMap::default, |mut accum, chunk| { for (k, v) in chunk { - if (k & offset1 > 0) == (k & offset2 > 0) { - accum.insert(*k, *v); + if k.bit(q1) == k.bit(q2) { + accum.insert(k.clone(), *v); } else { - accum.insert((k ^ offset1) ^ offset2, *v); + let mut new_k = k.clone(); + new_k.set_bit(q1, !k.bit(q1)); + new_k.set_bit(q2, !k.bit(q2)); + accum.insert(new_k, *v); } } accum @@ -96,7 +96,7 @@ impl QuantumSimImpl for QuantumSim { /// The function will panic if the given id does not correpsond to an allocated qubit. fn cleanup_state(&mut self, res: bool) { if res { - let offset = 1_u128 << self.id_map.len(); + let qubit = self.id_map.len() as u64; let chunk_size = parallel_chunk_size(self.state.len()); self.state = self .state @@ -105,7 +105,9 @@ impl QuantumSimImpl for QuantumSim { .par_chunks(chunk_size) .fold(FxHashMap::default, |mut accum, chunk| { for (k, v) in chunk { - accum.insert(k ^ offset, *v); + let mut new_k = k.clone(); + new_k.set_bit(qubit, !k.bit(qubit)); + accum.insert(new_k, *v); } accum }) @@ -126,13 +128,13 @@ impl QuantumSimImpl for QuantumSim { println!("MAP: {:?}", self.id_map); }; print!("STATE: [ "); - let mut sorted_keys = self.state.keys().copied().collect::>(); + let mut sorted_keys = self.state.keys().collect::>(); sorted_keys.sort_unstable(); for key in sorted_keys { print!( "|{}\u{27e9}: {}, ", key, - self.state.get(&key).map_or_else(Complex64::zero, |v| *v) + self.state.get(key).map_or_else(Complex64::zero, |v| *v) ); } println!("]"); @@ -143,7 +145,7 @@ impl QuantumSimImpl for QuantumSim { fn apply_impl(&mut self) { // let state_size = 1_usize << self.id_map.len(); let chunk_size = parallel_chunk_size(self.state.len()); - let op_size = self.op_buffer.ops.nrows() as u128; + let op_size = self.op_buffer.ops.nrows(); self.state = self .state .drain() @@ -152,18 +154,17 @@ impl QuantumSimImpl for QuantumSim { .fold(FxHashMap::default, |mut accum, chunk| { for (index, val) in chunk { let i = index / op_size; - let l = (index % op_size) as usize; + let l = (index % op_size) + .to_usize() + .expect("Cannot operate on more than 64 qubits at a time."); for j in (0..op_size).filter(|j| !self.op_buffer.ops.row(*j as usize)[l].is_zero()) { - let loc = (i * op_size) + (j as u128); + let loc = (&i * op_size) + (j as u128); if let Some(entry) = accum.get_mut(&loc) { - *entry += self.op_buffer.ops.row(j.try_into().unwrap())[l] * val; + *entry += self.op_buffer.ops.row(j)[l] * val; } else { - accum.insert( - (i * op_size) + (j as u128), - self.op_buffer.ops.row(j.try_into().unwrap())[l] * val, - ); + accum.insert((&i * op_size) + j, self.op_buffer.ops.row(j)[l] * val); } if accum .get(&loc) @@ -181,7 +182,7 @@ impl QuantumSimImpl for QuantumSim { if let Some(entry) = accum.get_mut(&k) { *entry += v; } else if !v.is_zero() { - accum.insert(k, v); + accum.insert(k.clone(), v); } if accum .get(&k) @@ -199,13 +200,15 @@ impl QuantumSimImpl for QuantumSim { /// are set. This corresponds to the probability of jointly measuring those qubits in the computational /// basis. fn check_joint_probability(&self, locs: &[usize]) -> f64 { - let mask = locs.iter().fold(0_u128, |accum, loc| accum | (1 << loc)); + let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { + accum | (BigUint::one() << loc) + }); (&self.state) .into_par_iter() .fold( || 0.0_f64, |accum, (index, val)| { - if (index & mask).count_ones() & 1 > 0 { + if (index & &mask).count_ones() & 1 > 0 { accum + val.norm_sqr() } else { accum @@ -239,7 +242,9 @@ impl QuantumSimImpl for QuantumSim { /// The entries that do not correspond to the given boolean value are removed, and then the whole /// state is normalized. fn joint_collapse(&mut self, locs: &[usize], val: bool) { - let mask = locs.iter().fold(0_u128, |accum, loc| accum | (1 << loc)); + let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { + accum | (BigUint::one() << loc) + }); let chunk_size = parallel_chunk_size(self.state.len()); self.state = self @@ -250,9 +255,9 @@ impl QuantumSimImpl for QuantumSim { .fold(FxHashMap::default, |mut accum, chunk| { for (k, v) in chunk .iter() - .filter(|(index, _)| ((index & mask).count_ones() & 1 > 0) == val) + .filter(|(index, _)| ((index & &mask).count_ones() & 1 > 0) == val) { - accum.insert(*k, *v); + accum.insert(k.clone(), *v); } accum }) From 06de82be252d6c6fa5d0c26649277835b8d4a62b Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 31 Aug 2022 22:57:05 -0700 Subject: [PATCH 16/55] Type updates, sparse nearly_zero fix --- src/Qir/riqir/build-qir-runtime.ps1 | 2 +- src/Qir/riqir/runtime/src/arrays.rs | 62 ++++++++++++---------- src/Qir/riqir/runtime/src/callables.rs | 24 ++++----- src/Qir/riqir/runtime/src/lib.rs | 2 + src/Qir/riqir/runtime/src/range_support.rs | 19 ++++--- src/Qir/riqir/runtime/src/strings.rs | 21 ++------ src/Qir/riqir/runtime/src/tuples.rs | 8 +-- src/Qir/riqir/sim/src/conditionals.rs | 6 +-- src/Qir/riqir/sim/src/lib.rs | 46 ++++++++-------- src/Qir/riqir/sim/src/nearly_zero.rs | 24 +++++++++ src/Qir/riqir/sim/src/simulator.rs | 22 ++++---- src/Qir/riqir/sim/src/sparsestate.rs | 12 ++--- 12 files changed, 133 insertions(+), 115 deletions(-) create mode 100644 src/Qir/riqir/sim/src/nearly_zero.rs diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 index 67882f2ac25..895c518598a 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -22,7 +22,7 @@ foreach ($folder in "runtime","sim") { # If there's a false positive, that check should be explicitly disabled # at the point where the false positive occurs with an explanation as to # why it's OK. - cargo clippy -- -D warnings + cargo clippy --all-targets -- -D warnings if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR $folder." } $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); diff --git a/src/Qir/riqir/runtime/src/arrays.rs b/src/Qir/riqir/runtime/src/arrays.rs index 373b135d7dd..f6929eeec96 100644 --- a/src/Qir/riqir/runtime/src/arrays.rs +++ b/src/Qir/riqir/runtime/src/arrays.rs @@ -5,19 +5,22 @@ use crate::update_counts; use std::{rc::Rc, usize}; +#[derive(Debug, Clone)] +pub struct QirArray { + pub(crate) elem_size: usize, + pub(crate) data: Vec, +} + /// # Panics /// /// This function panics if the passed in sizes do not fit into the usize type for the /// current platform. #[no_mangle] -pub extern "C" fn __quantum__rt__array_create_1d( - elem_size: u32, - size: u64, -) -> *const (usize, Vec) { - let elem_size_size: usize = elem_size.try_into().unwrap(); - let size_size: usize = size.try_into().unwrap(); - let array = vec![0_u8; elem_size_size * size_size]; - Rc::into_raw(Rc::new((elem_size_size, array))) +pub extern "C" fn __quantum__rt__array_create_1d(elem_size: u32, size: u64) -> *const QirArray { + let elem_size = elem_size.try_into().unwrap(); + let size: usize = size.try_into().unwrap(); + let data = vec![0_u8; elem_size * size]; + Rc::into_raw(Rc::new(QirArray { elem_size, data })) } /// # Safety @@ -25,9 +28,9 @@ pub extern "C" fn __quantum__rt__array_create_1d( /// This function should only be called with an array created by `__quantum__rt__array_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_copy( - arr: *const (usize, Vec), + arr: *const QirArray, force: bool, -) -> *const (usize, Vec) { +) -> *const QirArray { let rc = Rc::from_raw(arr); if force || Rc::weak_count(&rc) > 0 { let copy = rc.as_ref().clone(); @@ -48,27 +51,30 @@ pub unsafe extern "C" fn __quantum__rt__array_copy( /// This function will panic if the given arrays use different element sizes. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_concatenate( - arr1: *const (usize, Vec), - arr2: *const (usize, Vec), -) -> *const (usize, Vec) { + arr1: *const QirArray, + arr2: *const QirArray, +) -> *const QirArray { let array1 = Rc::from_raw(arr1); let array2 = Rc::from_raw(arr2); assert!( - array1.0 == array2.0, + array1.elem_size == array2.elem_size, "Cannot concatenate arrays with differing element sizes: {} vs {}", - array1.0, - array2.0 + array1.elem_size, + array2.elem_size ); - let mut new_array = (array1.0, Vec::new()); - new_array.1.resize(array1.1.len(), 0_u8); - new_array.1.copy_from_slice(array1.1.as_slice()); + let mut new_array = QirArray { + elem_size: array1.elem_size, + data: Vec::new(), + }; + new_array.data.resize(array1.data.len(), 0_u8); + new_array.data.copy_from_slice(array1.data.as_slice()); let mut copy = Vec::new(); - copy.resize(array2.1.len(), 0_u8); - copy.copy_from_slice(array2.1.as_slice()); + copy.resize(array2.data.len(), 0_u8); + copy.copy_from_slice(array2.data.as_slice()); - new_array.1.append(&mut copy); + new_array.data.append(&mut copy); let _ = Rc::into_raw(array1); let _ = Rc::into_raw(array2); Rc::into_raw(Rc::new(new_array)) @@ -81,9 +87,9 @@ pub unsafe extern "C" fn __quantum__rt__array_concatenate( /// /// This function panics if the array size is larger than u64. This shouldn't happen. #[no_mangle] -pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const (usize, Vec)) -> u64 { +pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const QirArray) -> u64 { let array = Rc::from_raw(arr); - let size = array.1.len() / array.0; + let size = array.data.len() / array.elem_size; let _ = Rc::into_raw(array); size.try_into().unwrap() } @@ -96,12 +102,12 @@ pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const (usize, Ve /// This function panics if the given index is larger than the usize type for the current platform. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( - arr: *const (usize, Vec), + arr: *const QirArray, index: u64, ) -> *mut i8 { let array = Rc::from_raw(arr); let i: usize = index.try_into().unwrap(); - let ptr = array.1.as_ptr().wrapping_add(array.0 * i) as *mut i8; + let ptr = array.data.as_ptr().wrapping_add(array.elem_size * i) as *mut i8; let _ = Rc::into_raw(array); ptr } @@ -113,7 +119,7 @@ pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( /// and the pointer is no longer valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_update_reference_count( - arr: *const (usize, Vec), + arr: *const QirArray, update: i32, ) { update_counts(arr, update, false); @@ -124,7 +130,7 @@ pub unsafe extern "C" fn __quantum__rt__array_update_reference_count( /// This function should only be called with an array created by `__quantum__rt__array_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_update_alias_count( - arr: *const (usize, Vec), + arr: *const QirArray, update: i32, ) { update_counts(arr, update, true); diff --git a/src/Qir/riqir/runtime/src/callables.rs b/src/Qir/riqir/runtime/src/callables.rs index cdd09026bda..e305c1960e6 100644 --- a/src/Qir/riqir/runtime/src/callables.rs +++ b/src/Qir/riqir/runtime/src/callables.rs @@ -3,7 +3,9 @@ #![deny(clippy::all, clippy::pedantic)] use crate::{ - arrays::{__quantum__rt__array_concatenate, __quantum__rt__array_update_reference_count}, + arrays::{ + QirArray, __quantum__rt__array_concatenate, __quantum__rt__array_update_reference_count, + }, tuples::{__quantum__rt__tuple_copy, __quantum__rt__tuple_update_reference_count}, update_counts, }; @@ -57,28 +59,25 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( // If there are any controls, increment the reference count on the control list. This is just // to balance the decrement that will happen in the loop and at the end of invoking the callable // to ensure the original, non-owned list does not get incorrectly cleaned up. - __quantum__rt__array_update_reference_count( - *args_copy.cast::<*const (usize, Vec)>(), - 1, - ); + __quantum__rt__array_update_reference_count(*args_copy.cast::<*const QirArray>(), 1); let mut ctl_count = *call.is_ctl.borrow(); while ctl_count > 1 { - let ctls = *args_copy.cast::<*const (usize, Vec)>(); + let ctls = *args_copy.cast::<*const QirArray>(); let inner_tuple = *args_copy - .cast::<*const (usize, Vec)>() + .cast::<*const QirArray>() .wrapping_add(1) .cast::<*mut *const Vec>(); - let inner_ctls = *inner_tuple.cast::<*const (usize, Vec)>(); + let inner_ctls = *inner_tuple.cast::<*const QirArray>(); let new_ctls = __quantum__rt__array_concatenate(ctls, inner_ctls); let new_args = __quantum__rt__tuple_copy(inner_tuple, true); - *new_args.cast::<*const (usize, Vec)>() = new_ctls; + *new_args.cast::<*const QirArray>() = new_ctls; // Decrementing the reference count is either the extra count added above or the new // list created when performing concatenate above. In the latter case, the concatenated // list will get cleaned up, preventing memory from leaking. __quantum__rt__array_update_reference_count( - *args_copy.cast::<*const (usize, Vec)>(), + *args_copy.cast::<*const QirArray>(), -1, ); // Decrement the count on the copy to clean it up as well, since we created a new copy @@ -99,10 +98,7 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( res_tup, ); if *call.is_ctl.borrow() > 0 { - __quantum__rt__array_update_reference_count( - *args_copy.cast::<*const (usize, Vec)>(), - -1, - ); + __quantum__rt__array_update_reference_count(*args_copy.cast::<*const QirArray>(), -1); } if !args_copy.is_null() { __quantum__rt__tuple_update_reference_count(args_copy, -1); diff --git a/src/Qir/riqir/runtime/src/lib.rs b/src/Qir/riqir/runtime/src/lib.rs index 3ea9371ba8b..8846cc532af 100644 --- a/src/Qir/riqir/runtime/src/lib.rs +++ b/src/Qir/riqir/runtime/src/lib.rs @@ -47,6 +47,8 @@ unsafe fn update_counts(raw_rc: *const T, update: i32, is_alias: bool) { remaining += 1; } + // To make sure the local Rc does not decrement the underlying count when it goes out of scope, + // it must be converted into a raw pointer first. let _ = Rc::into_raw(rc); } } diff --git a/src/Qir/riqir/runtime/src/range_support.rs b/src/Qir/riqir/runtime/src/range_support.rs index dea3ca046a2..872a88b12a3 100644 --- a/src/Qir/riqir/runtime/src/range_support.rs +++ b/src/Qir/riqir/runtime/src/range_support.rs @@ -4,7 +4,7 @@ // Functionality in this file can be removed when range support is dropped from the QIR runtime. -use crate::strings::convert; +use crate::{arrays::QirArray, strings::convert}; use std::{ffi::CString, rc::Rc}; #[repr(C)] @@ -34,12 +34,15 @@ pub extern "C" fn quantum__rt__range_to_string(input: Range) -> *const CString { /// u64. This should never happen. #[no_mangle] pub unsafe extern "C" fn quantum__rt__array_slice_1d( - arr: *const (usize, Vec), + arr: *const QirArray, range: Range, -) -> *const (usize, Vec) { +) -> *const QirArray { let array = Rc::from_raw(arr); - let item_size: i64 = array.0.try_into().unwrap(); - let mut slice = (array.0, Vec::new()); + let item_size: i64 = array.elem_size.try_into().unwrap(); + let mut slice = QirArray { + elem_size: array.elem_size, + data: Vec::new(), + }; let iter: Box> = if range.step > 0 { Box::new(range.start * item_size..=range.end * item_size) } else { @@ -50,9 +53,9 @@ pub unsafe extern "C" fn quantum__rt__array_slice_1d( for i in iter.step_by((step * item_size).try_into().unwrap()) { let index = i.try_into().unwrap(); let mut copy = Vec::new(); - copy.resize(array.0, 0_u8); - copy.copy_from_slice(&array.1[index..index + array.0]); - slice.1.append(&mut copy); + copy.resize(array.elem_size, 0_u8); + copy.copy_from_slice(&array.data[index..index + array.elem_size]); + slice.data.append(&mut copy); } let _ = Rc::into_raw(array); diff --git a/src/Qir/riqir/runtime/src/strings.rs b/src/Qir/riqir/runtime/src/strings.rs index a8a0fa0b943..13da297e375 100644 --- a/src/Qir/riqir/runtime/src/strings.rs +++ b/src/Qir/riqir/runtime/src/strings.rs @@ -60,21 +60,11 @@ pub unsafe extern "C" fn __quantum__rt__string_concatenate( s1: *const CString, s2: *const CString, ) -> *const CString { - let mut new_str = (*s1) - .clone() - .into_string() - .expect("Unable to convert string"); - - new_str.push_str( - (*s2) - .clone() - .into_string() - .expect("Unable to convert string") - .as_str(), - ); + let mut new_str = (*s1).clone().into_bytes(); + new_str.extend_from_slice((*s2).to_bytes()); Rc::into_raw(Rc::new( - CString::new(new_str.as_bytes()).expect("Unable to convert string"), + CString::new(new_str).expect("Unable to convert string"), )) } @@ -86,13 +76,12 @@ pub unsafe extern "C" fn __quantum__rt__string_equal( s1: *const CString, s2: *const CString, ) -> bool { - (*s1).to_str().expect("Unable to convert string") - == (*s2).to_str().expect("Unable to convert string") + *s1 == *s2 } pub(crate) fn convert(input: &T) -> *const CString where - T: Sized + ToString, + T: ToString, { unsafe { __quantum__rt__string_create( diff --git a/src/Qir/riqir/runtime/src/tuples.rs b/src/Qir/riqir/runtime/src/tuples.rs index e40f5b4d650..e1ab5d15351 100644 --- a/src/Qir/riqir/runtime/src/tuples.rs +++ b/src/Qir/riqir/runtime/src/tuples.rs @@ -18,7 +18,7 @@ pub extern "C" fn __quantum__rt__tuple_create(size: u64) -> *mut *const Vec ]; unsafe { - let header = mem.as_mut_ptr().cast::<*const std::vec::Vec>(); + let header = mem.as_mut_ptr().cast::<*const Vec>(); *header = Rc::into_raw(Rc::new(mem)); header.wrapping_add(1) } @@ -37,7 +37,7 @@ pub unsafe extern "C" fn __quantum__rt__tuple_copy( if force || Rc::weak_count(&rc) > 0 { let mut copy = rc.as_ref().clone(); let _ = Rc::into_raw(rc); - let header = copy.as_mut_ptr().cast::<*const std::vec::Vec>(); + let header = copy.as_mut_ptr().cast::<*const Vec>(); *header = Rc::into_raw(Rc::new(copy)); header.wrapping_add(1) } else { @@ -88,7 +88,7 @@ mod tests { fn test_tuple_update_reference_count() { let tup = __quantum__rt__tuple_create(size_of::() as u64); unsafe { - let rc = Rc::from_raw(*tup.cast::<*const std::vec::Vec>().wrapping_sub(1)); + let rc = Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1)); assert_eq!(Rc::strong_count(&rc), 1); __quantum__rt__tuple_update_reference_count(tup, 2); assert_eq!(Rc::strong_count(&rc), 3); @@ -103,7 +103,7 @@ mod tests { fn test_tuple_update_alias_count() { let tup = __quantum__rt__tuple_create(size_of::() as u64); unsafe { - let rc = Rc::from_raw(*tup.cast::<*const std::vec::Vec>().wrapping_sub(1)); + let rc = Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1)); assert_eq!(Rc::strong_count(&rc), 1); assert_eq!(Rc::weak_count(&rc), 0); __quantum__rt__tuple_update_alias_count(tup, 2); diff --git a/src/Qir/riqir/sim/src/conditionals.rs b/src/Qir/riqir/sim/src/conditionals.rs index 10d699972ae..4ee11ae230d 100644 --- a/src/Qir/riqir/sim/src/conditionals.rs +++ b/src/Qir/riqir/sim/src/conditionals.rs @@ -6,7 +6,7 @@ use super::result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_ use qir_runtime::{ __quantum__rt__fail, - arrays::{__quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, + arrays::{QirArray, __quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, callables::{Callable, __quantum__rt__callable_invoke}, strings::__quantum__rt__string_create, }; @@ -41,8 +41,8 @@ pub unsafe extern "C" fn __quantum__qis__applyifelseintrinsic__body( #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn __quantum__qis__applyconditionallyintrinsic__body( - results: *const (usize, Vec), - expected: *const (usize, Vec), + results: *const QirArray, + expected: *const QirArray, true_callable: *const Callable, false_callable: *const Callable, ) { diff --git a/src/Qir/riqir/sim/src/lib.rs b/src/Qir/riqir/sim/src/lib.rs index 04a67a17e5c..4e425b2b63c 100644 --- a/src/Qir/riqir/sim/src/lib.rs +++ b/src/Qir/riqir/sim/src/lib.rs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![deny(clippy::all, clippy::pedantic)] //! Module defining QIR compliant APIs for quantum simulation. @@ -7,6 +8,7 @@ pub mod conditionals; pub mod result_bool; mod common_matrices; +mod nearly_zero; mod simulator; mod sparsestate; @@ -45,8 +47,8 @@ fn ensure_sufficient_qubits(sim: &mut SparseStateQuantumSim, qubit_id: usize) { #[allow(clippy::cast_ptr_alignment)] unsafe fn map_paulis( sim: &mut SparseStateQuantumSim, - paulis: *const (usize, Vec), - qubits: *const (usize, Vec), + paulis: *const QirArray, + qubits: *const QirArray, ) -> Vec<(Pauli, usize)> { let paulis_size = __quantum__rt__array_get_size_1d(paulis); let qubits_size = __quantum__rt__array_get_size_1d(qubits); @@ -286,7 +288,7 @@ macro_rules! multicontrolled_qubit_gate { /// /// This function should only be called with arrays and tuples created by the QIR runtime library. #[no_mangle] - pub unsafe extern "C" fn $qir_name(ctls: *const (usize, Vec), qubit: *mut c_void) { + pub unsafe extern "C" fn $qir_name(ctls: *const QirArray, qubit: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() @@ -366,7 +368,7 @@ macro_rules! multicontrolled_qubit_rotation { /// This function should only be called with arrays and tuples created by the QIR runtime library. #[no_mangle] pub unsafe extern "C" fn $qir_name( - ctls: *const (usize, Vec), + ctls: *const QirArray, arg_tuple: *mut *const Vec, ) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); @@ -451,7 +453,7 @@ struct PauliRotationArgs { #[allow(clippy::cast_ptr_alignment)] #[no_mangle] pub unsafe extern "C" fn __quantum__qis__r__ctl( - ctls: *const (usize, Vec), + ctls: *const QirArray, arg_tuple: *mut *const Vec, ) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); @@ -492,7 +494,7 @@ pub unsafe extern "C" fn __quantum__qis__r__ctl( /// This function should only be called with arrays and tuples created by the QIR runtime library. #[no_mangle] pub unsafe extern "C" fn __quantum__qis__r__ctladj( - ctls: *const (usize, Vec), + ctls: *const QirArray, arg_tuple: *mut *const Vec, ) { let args = *arg_tuple.cast::(); @@ -529,9 +531,9 @@ pub extern "C" fn __quantum__qis__swap__body(qubit0: *mut c_void, qubit1: *mut c /// QIR API for performing the exponential of a multi-qubit Pauli operator with the given angle. #[no_mangle] pub extern "C" fn __quantum__qis__exp__body( - _paulis: *const (usize, Vec), + _paulis: *const QirArray, _theta: c_double, - _qubits: *const (usize, Vec), + _qubits: *const QirArray, ) { unimplemented!("Exp is not implemented.") } @@ -539,9 +541,9 @@ pub extern "C" fn __quantum__qis__exp__body( /// QIR API for performing the adjoint exponential of a multi-qubit Pauli operator with the given angle. #[no_mangle] pub extern "C" fn __quantum__qis__exp__adj( - paulis: *const (usize, Vec), + paulis: *const QirArray, theta: c_double, - qubits: *const (usize, Vec), + qubits: *const QirArray, ) { __quantum__qis__exp__body(paulis, -theta, qubits); } @@ -549,7 +551,7 @@ pub extern "C" fn __quantum__qis__exp__adj( /// QIR API for performing the multicontrolled exponential of a multi-qubit Pauli operator with the given angle. #[no_mangle] pub extern "C" fn __quantum__qis__exp__ctl( - _ctls: *const (usize, Vec), + _ctls: *const QirArray, _arg_tuple: *mut *const Vec, ) { unimplemented!("Controlled Exp is not implemented.") @@ -558,7 +560,7 @@ pub extern "C" fn __quantum__qis__exp__ctl( /// QIR API for performing the adjoint multicontrolled exponential of a multi-qubit Pauli operator with the given angle. #[no_mangle] pub extern "C" fn __quantum__qis__exp__ctladj( - _ctls: *const (usize, Vec), + _ctls: *const QirArray, _arg_tuple: *mut *const Vec, ) { unimplemented!("Controlled Exp is not implemented.") @@ -648,8 +650,8 @@ pub extern "C" fn __quantum__qis__m__body(qubit: *mut c_void) -> *mut c_void { #[allow(clippy::cast_ptr_alignment)] #[no_mangle] pub unsafe extern "C" fn __quantum__qis__measure__body( - paulis: *const (usize, Vec), - qubits: *const (usize, Vec), + paulis: *const QirArray, + qubits: *const QirArray, ) -> *mut c_void { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock @@ -682,8 +684,8 @@ pub unsafe extern "C" fn __quantum__qis__measure__body( /// This function should only be called with arrays created by the QIR runtime library. #[no_mangle] pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body( - paulis: *const (usize, Vec), - qubits: *const (usize, Vec), + paulis: *const QirArray, + qubits: *const QirArray, result: *mut c_void, prob: c_double, msg: *const CString, @@ -719,8 +721,8 @@ pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body( #[derive(Copy, Clone)] #[repr(C)] struct AssertMeasurementProbabilityArgs { - paulis: *const (usize, Vec), - qubits: *const (usize, Vec), + paulis: *const QirArray, + qubits: *const QirArray, result: *mut c_void, prob: c_double, msg: *const CString, @@ -735,7 +737,7 @@ struct AssertMeasurementProbabilityArgs { /// This function should only be called with arrays created by the QIR runtime library. #[no_mangle] pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__ctl( - _ctls: *const (usize, Vec), + _ctls: *const QirArray, arg_tuple: *mut *const Vec, ) { let args = *arg_tuple.cast::(); @@ -792,7 +794,7 @@ pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void { /// a `u32`. #[allow(clippy::cast_ptr_alignment)] #[no_mangle] -pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const (usize, Vec) { +pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const QirArray { let arr = __quantum__rt__array_create_1d(size_of::().try_into().unwrap(), size); for index in 0..size { unsafe { @@ -809,7 +811,7 @@ pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const (usiz /// This function should only be called with arrays created by `__quantum__rt__qubit_allocate_array`. #[allow(clippy::cast_ptr_alignment)] #[no_mangle] -pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const (usize, Vec)) { +pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const QirArray) { for index in 0..__quantum__rt__array_get_size_1d(arr) { let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>(); __quantum__rt__qubit_release(*elem); @@ -858,7 +860,7 @@ pub extern "C" fn __quantum__qis__dumpmachine__body(location: *mut c_void) { #[no_mangle] pub extern "C" fn __quantum__qis__dumpregister__body( _location: *mut c_void, - _qubits: *const (usize, Vec), + _qubits: *const QirArray, ) { unimplemented!("Dump of qubit register is not implemented.") } diff --git a/src/Qir/riqir/sim/src/nearly_zero.rs b/src/Qir/riqir/sim/src/nearly_zero.rs new file mode 100644 index 00000000000..ad7918c56e8 --- /dev/null +++ b/src/Qir/riqir/sim/src/nearly_zero.rs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use num_complex::Complex; + +/// `NearlyZero` trait allows for approximate evaluation of a value to the additive identity. +pub(crate) trait NearlyZero { + fn is_nearly_zero(&self) -> bool; +} + +impl NearlyZero for f64 { + fn is_nearly_zero(&self) -> bool { + self.max(0.0) - 0.0_f64.min(*self) <= 1e-10 + } +} + +impl NearlyZero for Complex +where + T: NearlyZero, +{ + fn is_nearly_zero(&self) -> bool { + self.re.is_nearly_zero() && self.im.is_nearly_zero() + } +} diff --git a/src/Qir/riqir/sim/src/simulator.rs b/src/Qir/riqir/sim/src/simulator.rs index fb8e3c1a34c..122cfca8a68 100644 --- a/src/Qir/riqir/sim/src/simulator.rs +++ b/src/Qir/riqir/sim/src/simulator.rs @@ -17,6 +17,11 @@ use std::ops::ControlFlow; /// just the value used to determine whether the buffer should accept the current incoming operation. const BUFFER_LIMIT: usize = 4; +/// Scaling used to determine when an operation is big enough to warrant multithreading. If the size +/// is too small the overhead of threading leads to performance degredation compared to the single threaded +/// approach. The scaling must be a power of two. +const CHUNK_SCALING: usize = 512; + /// Get the chunk size we want to use in parallel for the given number of items based on the threads available in the /// thread pool. pub(crate) fn parallel_chunk_size(total_size: usize) -> usize { @@ -25,20 +30,11 @@ pub(crate) fn parallel_chunk_size(total_size: usize) -> usize { while chunk_count << 1 <= current_num_threads() { chunk_count <<= 1; } - if total_size > chunk_count { + if chunk_count > 1 && total_size / CHUNK_SCALING > chunk_count { total_size / chunk_count } else { - // Try some well known sizes. - if total_size > 8 { - total_size / 8 - } else if total_size > 4 { - total_size / 4 - } else if total_size > 2 { - total_size / 2 - } else { - // Treat as a single chunk. - total_size - } + // Treat as a single chunk. + total_size } } @@ -307,7 +303,7 @@ where )); ndarray::Zip::from(out.exact_chunks_mut((dim_beta_rows, dim_beta_cols))) .and(alpha) - .par_for_each(|out, &a| { + .for_each(|out, &a| { ndarray::Zip::from(out).and(beta).for_each(|out, &b| { *out = MaybeUninit::new(a * b); }); diff --git a/src/Qir/riqir/sim/src/sparsestate.rs b/src/Qir/riqir/sim/src/sparsestate.rs index a792e9ef347..dd423d5b329 100644 --- a/src/Qir/riqir/sim/src/sparsestate.rs +++ b/src/Qir/riqir/sim/src/sparsestate.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::nearly_zero::NearlyZero; use ndarray::Array2; use num_bigint::BigUint; use num_complex::Complex64; @@ -157,8 +158,8 @@ impl QuantumSimImpl for QuantumSim { let l = (index % op_size) .to_usize() .expect("Cannot operate on more than 64 qubits at a time."); - for j in - (0..op_size).filter(|j| !self.op_buffer.ops.row(*j as usize)[l].is_zero()) + for j in (0..op_size) + .filter(|j| !self.op_buffer.ops.row(*j as usize)[l].is_nearly_zero()) { let loc = (&i * op_size) + (j as u128); if let Some(entry) = accum.get_mut(&loc) { @@ -168,8 +169,7 @@ impl QuantumSimImpl for QuantumSim { } if accum .get(&loc) - .map_or_else(Complex64::one, |entry| *entry) - .is_zero() + .map_or_else(|| false, |entry| (*entry).is_nearly_zero()) { accum.remove(&loc); } @@ -181,13 +181,13 @@ impl QuantumSimImpl for QuantumSim { for (k, v) in sparse_chunk.drain() { if let Some(entry) = accum.get_mut(&k) { *entry += v; - } else if !v.is_zero() { + } else if !v.is_nearly_zero() { accum.insert(k.clone(), v); } if accum .get(&k) .map_or_else(Complex64::one, |entry| *entry) - .is_zero() + .is_nearly_zero() { accum.remove(&k); } From 62e3ff69ecc309137276743232f50e54d59d492b Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 2 Sep 2022 10:01:02 -0700 Subject: [PATCH 17/55] Consolidation and optimization of sparse sim --- Cargo.toml | 1 + src/Qir/riqir/sim/src/common_matrices.rs | 3 + src/Qir/riqir/sim/src/lib.rs | 178 +++++--- src/Qir/riqir/sim/src/simulator.rs | 554 +++++++++++++++++++++-- src/Qir/riqir/sim/src/sparsestate.rs | 273 ----------- 5 files changed, 618 insertions(+), 391 deletions(-) delete mode 100644 src/Qir/riqir/sim/src/sparsestate.rs diff --git a/Cargo.toml b/Cargo.toml index 3f168d57383..e11b2e4deb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ ] [profile.release] +debug = true codegen-units = 1 # Reduce number of codegen units to increase optimizations. opt-level = 3 panic = 'unwind' diff --git a/src/Qir/riqir/sim/src/common_matrices.rs b/src/Qir/riqir/sim/src/common_matrices.rs index 548e4fd2f55..cfdf8ee826a 100644 --- a/src/Qir/riqir/sim/src/common_matrices.rs +++ b/src/Qir/riqir/sim/src/common_matrices.rs @@ -7,6 +7,7 @@ use num_complex::Complex64; use num_traits::{One, Zero}; /// Returns a unitary matrix representing the `X` operation. +#[cfg(test)] #[must_use] pub fn x() -> Array2 { array![ @@ -16,6 +17,7 @@ pub fn x() -> Array2 { } /// Returns a unitary matrix representing the `Y` operation. +#[cfg(test)] #[must_use] pub fn y() -> Array2 { array![ @@ -25,6 +27,7 @@ pub fn y() -> Array2 { } /// Returns a unitary matrix representing the `Z` operation. +#[cfg(test)] #[must_use] pub fn z() -> Array2 { array![ diff --git a/src/Qir/riqir/sim/src/lib.rs b/src/Qir/riqir/sim/src/lib.rs index 4e425b2b63c..55128ec6a72 100644 --- a/src/Qir/riqir/sim/src/lib.rs +++ b/src/Qir/riqir/sim/src/lib.rs @@ -10,11 +10,10 @@ pub mod result_bool; mod common_matrices; mod nearly_zero; mod simulator; -mod sparsestate; use bitvec::prelude::*; use lazy_static::lazy_static; -use sparsestate::SparseStateQuantumSim; +use simulator::QuantumSim; use std::convert::TryInto; use std::ffi::{c_void, CString}; use std::mem::size_of; @@ -32,12 +31,12 @@ pub use qir_runtime::{ }; lazy_static! { - static ref SIM: Mutex> = Mutex::new(None); + static ref SIM: Mutex> = Mutex::new(None); static ref RESULTS: Mutex = Mutex::new(bitvec![]); static ref MAX_QUBIT_ID: AtomicUsize = AtomicUsize::new(0); } -fn ensure_sufficient_qubits(sim: &mut SparseStateQuantumSim, qubit_id: usize) { +fn ensure_sufficient_qubits(sim: &mut QuantumSim, qubit_id: usize) { while qubit_id + 1 > (*MAX_QUBIT_ID).load(Relaxed) { let _ = sim.allocate(); (*MAX_QUBIT_ID).fetch_add(1, Relaxed); @@ -46,7 +45,7 @@ fn ensure_sufficient_qubits(sim: &mut SparseStateQuantumSim, qubit_id: usize) { #[allow(clippy::cast_ptr_alignment)] unsafe fn map_paulis( - sim: &mut SparseStateQuantumSim, + sim: &mut QuantumSim, paulis: *const QirArray, qubits: *const QirArray, ) -> Vec<(Pauli, usize)> { @@ -91,7 +90,7 @@ unsafe fn map_paulis( combined_list } -fn unmap_paulis(sim: &mut SparseStateQuantumSim, combined_list: Vec<(Pauli, usize)>) { +fn unmap_paulis(sim: &mut QuantumSim, combined_list: Vec<(Pauli, usize)>) { for (pauli, qubit) in combined_list { match pauli { Pauli::X => sim.apply(&common_matrices::h(), &[qubit], None), @@ -118,7 +117,7 @@ macro_rules! single_qubit_gate { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + .map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, qubit as usize); sim.apply(&$gate_matrix, &[qubit as usize], None); @@ -128,6 +127,26 @@ macro_rules! single_qubit_gate { }; } +macro_rules! fast_single_qubit_gate { + ($(#[$meta:meta])* + $qir_name:ident, $fast_gate:expr) => { + $(#[$meta])* + #[no_mangle] + pub extern "C" fn $qir_name(qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(QuantumSim::default, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + + sim.flush(); + $fast_gate(&mut sim, qubit as usize); + + *sim_lock = Some(sim); + } + }; +} + single_qubit_gate!( /// QIR API for performing the H gate on the given qubit. __quantum__qis__h__body, @@ -153,42 +172,43 @@ single_qubit_gate!( __quantum__qis__t__adj, common_matrices::adjoint(&common_matrices::t()) ); -single_qubit_gate!( +fast_single_qubit_gate!( /// QIR API for performing the X gate on the given qubit. __quantum__qis__x__body, - common_matrices::x() + QuantumSim::fast_x ); -single_qubit_gate!( +fast_single_qubit_gate!( /// QIR API for performing the Y gate on the given qubit. __quantum__qis__y__body, - common_matrices::y() + QuantumSim::fast_y ); -single_qubit_gate!( +fast_single_qubit_gate!( /// QIR API for performing the Z gate on the given qubit. __quantum__qis__z__body, - common_matrices::z() + QuantumSim::fast_z ); -macro_rules! controlled_qubit_gate { +macro_rules! fast_controlled_qubit_gate { ($(#[$meta:meta])* - $qir_name:ident, $gate_matrix:expr, 1) => { + $qir_name:ident, $fast_gate:expr, 1) => { $(#[$meta])* #[no_mangle] pub extern "C" fn $qir_name(control: *mut c_void, target: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + .map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, target as usize); ensure_sufficient_qubits(&mut sim, control as usize); - sim.apply(&$gate_matrix, &[target as usize], Some(&[control as usize])); + sim.flush(); + $fast_gate(&mut sim, &[control as usize], target as usize); *sim_lock = Some(sim); } }; ($(#[$meta:meta])* - $qir_name:ident, $gate_matrix:expr, 2) => { + $qir_name:ident, $fast_gate:expr, 2) => { $(#[$meta])* #[no_mangle] pub extern "C" fn $qir_name( @@ -199,44 +219,41 @@ macro_rules! controlled_qubit_gate { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + .map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, target as usize); ensure_sufficient_qubits(&mut sim, control_1 as usize); ensure_sufficient_qubits(&mut sim, control_2 as usize); - sim.apply( - &$gate_matrix, - &[target as usize], - Some(&[control_1 as usize, control_2 as usize]), - ); + sim.flush(); + $fast_gate(&mut sim, &[control_1 as usize, control_2 as usize], target as usize); *sim_lock = Some(sim); } }; } -controlled_qubit_gate!( +fast_controlled_qubit_gate!( /// QIR API for performing the CNOT gate with the given qubits. __quantum__qis__cnot__body, - common_matrices::x(), + QuantumSim::fast_mcx, 1 ); -controlled_qubit_gate!( - /// QIR API for performing the CX gate with the given qubits. +fast_controlled_qubit_gate!( + /// QIR API for performing the CNOT gate with the given qubits. __quantum__qis__cx__body, - common_matrices::x(), + QuantumSim::fast_mcx, 1 ); -controlled_qubit_gate!( - /// QIR API for performing the CCX gate with the given qubits. +fast_controlled_qubit_gate!( + /// QIR API for performing the CNOT gate with the given qubits. __quantum__qis__ccx__body, - common_matrices::x(), + QuantumSim::fast_mcx, 2 ); -controlled_qubit_gate!( +fast_controlled_qubit_gate!( /// QIR API for performing the CZ gate with the given qubits. __quantum__qis__cz__body, - common_matrices::z(), + QuantumSim::fast_mcz, 1 ); @@ -249,7 +266,7 @@ macro_rules! single_qubit_rotation { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + .map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, qubit as usize); sim.apply(&$gate_matrix(theta), &[qubit as usize], None); @@ -292,7 +309,7 @@ macro_rules! multicontrolled_qubit_gate { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + .map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, qubit as usize); let ctls_size = __quantum__rt__array_get_size_1d(ctls); let ctls_list: Vec = (0..ctls_size) @@ -311,6 +328,38 @@ macro_rules! multicontrolled_qubit_gate { }; } +macro_rules! fast_multicontrolled_qubit_gate { + ($(#[$meta:meta])* + $qir_name:ident, $fast_gate:expr) => { + $(#[$meta])* + /// # Safety + /// + /// This function should only be called with arrays and tuples created by the QIR runtime library. + #[no_mangle] + pub unsafe extern "C" fn $qir_name(ctls: *const QirArray, qubit: *mut c_void) { + let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); + let mut sim = sim_lock + .take() + .map_or_else(QuantumSim::default, |s| s); + ensure_sufficient_qubits(&mut sim, qubit as usize); + let ctls_size = __quantum__rt__array_get_size_1d(ctls); + let ctls_list: Vec = (0..ctls_size) + .map(|index| { + let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index) + .cast::<*mut c_void>() as usize; + ensure_sufficient_qubits(&mut sim, q); + q + }) + .collect(); + + sim.flush(); + $fast_gate(&mut sim, &ctls_list, qubit as usize); + + *sim_lock = Some(sim); + } + }; +} + multicontrolled_qubit_gate!( /// QIR API for performing the multicontrolled H gate with the given qubits. __quantum__qis__h__ctl, @@ -336,20 +385,20 @@ multicontrolled_qubit_gate!( __quantum__qis__t__ctladj, common_matrices::adjoint(&common_matrices::t()) ); -multicontrolled_qubit_gate!( +fast_multicontrolled_qubit_gate!( /// QIR API for performing the multicontrolled X gate with the given qubits. __quantum__qis__x__ctl, - common_matrices::x() + QuantumSim::fast_mcx ); -multicontrolled_qubit_gate!( +fast_multicontrolled_qubit_gate!( /// QIR API for performing the multicontrolled Y gate with the given qubits. __quantum__qis__y__ctl, - common_matrices::y() + QuantumSim::fast_mcy ); -multicontrolled_qubit_gate!( +fast_multicontrolled_qubit_gate!( /// QIR API for performing the multicontrolled Z gate with the given qubits. __quantum__qis__z__ctl, - common_matrices::z() + QuantumSim::fast_mcz ); #[derive(Copy, Clone)] @@ -374,7 +423,7 @@ macro_rules! multicontrolled_qubit_rotation { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); let mut sim = sim_lock .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + .map_or_else(QuantumSim::default, |s| s); let args = *arg_tuple.cast::(); @@ -457,9 +506,7 @@ pub unsafe extern "C" fn __quantum__qis__r__ctl( arg_tuple: *mut *const Vec, ) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); let args = *arg_tuple.cast::(); @@ -513,9 +560,7 @@ pub unsafe extern "C" fn __quantum__qis__r__ctladj( #[no_mangle] pub extern "C" fn __quantum__qis__swap__body(qubit0: *mut c_void, qubit1: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, qubit0 as usize); ensure_sufficient_qubits(&mut sim, qubit1 as usize); @@ -570,13 +615,12 @@ pub extern "C" fn __quantum__qis__exp__ctladj( #[no_mangle] pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, qubit as usize); if sim.measure(qubit as usize) { - sim.apply(&common_matrices::x(), &[qubit as usize], None); + sim.flush(); + sim.fast_x(qubit as usize); } *sim_lock = Some(sim); @@ -586,9 +630,7 @@ pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) { #[no_mangle] pub extern "C" fn __quantum__qis__mz__body(qubit: *mut c_void, result: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); let mut res = RESULTS.lock().expect("Unable to lock global result state."); let res_id = result as usize; ensure_sufficient_qubits(&mut sim, qubit as usize); @@ -625,9 +667,7 @@ pub extern "C" fn __quantum__qis__read_result__body(result: *mut c_void) -> bool #[no_mangle] pub extern "C" fn __quantum__qis__m__body(qubit: *mut c_void) -> *mut c_void { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); ensure_sufficient_qubits(&mut sim, qubit as usize); let res = sim.measure(qubit as usize); @@ -654,9 +694,7 @@ pub unsafe extern "C" fn __quantum__qis__measure__body( qubits: *const QirArray, ) -> *mut c_void { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); let combined_list = map_paulis(&mut sim, paulis, qubits); @@ -692,9 +730,7 @@ pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body( tol: c_double, ) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); let combined_list = map_paulis(&mut sim, paulis, qubits); @@ -774,9 +810,7 @@ pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) { #[no_mangle] pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); let qubit_id = sim.allocate(); // Increase the max qubit id global so that `ensure_sufficient_qubits` wont trigger more allocations. @@ -823,9 +857,7 @@ pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const QirArray #[no_mangle] pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); sim.release(qubit as usize); *sim_lock = Some(sim); } @@ -834,9 +866,7 @@ pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { #[no_mangle] pub extern "C" fn dump_state() { let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(SparseStateQuantumSim::new, |s| s); + let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); let res = RESULTS.lock().expect("Unable to lock global result state."); if !(*res).is_empty() { diff --git a/src/Qir/riqir/sim/src/simulator.rs b/src/Qir/riqir/sim/src/simulator.rs index 122cfca8a68..da5d4329b4a 100644 --- a/src/Qir/riqir/sim/src/simulator.rs +++ b/src/Qir/riqir/sim/src/simulator.rs @@ -3,10 +3,14 @@ use super::common_matrices; +use crate::nearly_zero::NearlyZero; use ndarray::Array2; +use num_bigint::BigUint; use num_complex::Complex64; +use num_traits::{One, ToPrimitive, Zero}; use rand::Rng; use rayon::current_num_threads; +use rayon::prelude::*; use rustc_hash::FxHashMap; use std::convert::TryInto; use std::mem::MaybeUninit; @@ -24,7 +28,7 @@ const CHUNK_SCALING: usize = 512; /// Get the chunk size we want to use in parallel for the given number of items based on the threads available in the /// thread pool. -pub(crate) fn parallel_chunk_size(total_size: usize) -> usize { +fn parallel_chunk_size(total_size: usize) -> usize { // Chunk count needs to be a power of two to work with the state vector math. let mut chunk_count = 1_usize; while chunk_count << 1 <= current_num_threads() { @@ -38,57 +42,60 @@ pub(crate) fn parallel_chunk_size(total_size: usize) -> usize { } } -pub(crate) struct OpBuffer { +struct OpBuffer { pub targets: Vec, pub ops: Array2, } +pub type SparseState = FxHashMap; + /// The `QuantumSim` struct contains the necessary state for tracking the simulation. Each instance of a /// `QuantumSim` represents an independant simulation. -pub(crate) struct QuantumSim { +pub(crate) struct QuantumSim { /// The structure that describes the current quantum state. - pub state: T, + state: FxHashMap, /// The mapping from qubit identifiers to internal state locations. - pub id_map: FxHashMap, + id_map: FxHashMap, /// The ordered buffer containing queued quantum operations that have not yet been applied to the /// state. - pub op_buffer: OpBuffer, + op_buffer: OpBuffer, } -pub(crate) mod detail { - pub trait QuantumSimImpl { - fn extend_state(&mut self); - fn swap_qubits(&mut self, qubit1: usize, qubit2: usize); - fn cleanup_state(&mut self, res: bool); - fn dump_impl(&self, print_id_map: bool); - fn apply_impl(&mut self); - fn check_joint_probability(&self, locs: &[usize]) -> f64; - fn collapse(&mut self, loc: usize, val: bool); - fn joint_collapse(&mut self, locs: &[usize], val: bool); - fn normalize(&mut self); +impl Default for QuantumSim { + fn default() -> Self { + Self::new() } } -use detail::QuantumSimImpl; /// Provides the common set of functionality across all quantum simulation types. -impl QuantumSim -where - Self: detail::QuantumSimImpl, -{ +impl QuantumSim { + /// Creates a new sparse state quantum simulator object with empty initial state (no qubits allocated, no operations buffered). + #[must_use] + fn new() -> Self { + QuantumSim { + state: FxHashMap::default(), + + id_map: FxHashMap::default(), + + op_buffer: OpBuffer { + targets: vec![], + ops: Array2::default((0, 0)), + }, + } + } + /// Allocates a fresh qubit, returning its identifier. Note that this will use the lowest available /// identifier, and may result in qubits being allocated "in the middle" of an existing register /// if those identifiers are available. #[must_use] pub(crate) fn allocate(&mut self) -> usize { - self.extend_state(); - self.allocate_next_id() - } + if self.id_map.is_empty() { + // Add the intial value for the zero state. + self.state.insert(BigUint::zero(), Complex64::one()); + } - /// Utility that finds and reserves the next available qubit id in the map, returning the reserved - /// id. - fn allocate_next_id(&mut self) -> usize { // Add the new entry into the FxHashMap at the first available sequential ID. let mut sorted_keys: Vec<&usize> = self.id_map.keys().collect(); sorted_keys.sort(); @@ -105,6 +112,43 @@ where new_key } + /// Utility function that will swap states of two qubits throughout the sparse state map. + fn swap_qubits(&mut self, qubit1: usize, qubit2: usize) { + if qubit1 == qubit2 { + return; + } + + let (q1, q2) = (qubit1 as u64, qubit2 as u64); + + // In parallel, swap entries in the sparse state to correspond to swapping of two qubits' + // locations. + let chunk_size = parallel_chunk_size(self.state.len()); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (k, v) in chunk { + if k.bit(q1) == k.bit(q2) { + accum.insert(k.clone(), *v); + } else { + let mut new_k = k.clone(); + new_k.set_bit(q1, !k.bit(q1)); + new_k.set_bit(q2, !k.bit(q2)); + accum.insert(new_k, *v); + } + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut chunk| { + for (k, v) in chunk.drain() { + accum.insert(k, v); + } + accum + }); + } + /// Releases the given qubit, collapsing its state in the process. After release that identifier is /// no longer valid for use in other functions and will cause an error if used. /// # Panics @@ -141,12 +185,43 @@ where self.cleanup_state(res); } + /// Releases the given qubit, collapsing its state in the process. After release that identifier is + /// no longer valid for use in other functions and will cause an error if used. + /// # Panics + /// + /// The function will panic if the given id does not correpsond to an allocated qubit. + fn cleanup_state(&mut self, res: bool) { + if res { + let qubit = self.id_map.len() as u64; + let chunk_size = parallel_chunk_size(self.state.len()); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (k, v) in chunk { + let mut new_k = k.clone(); + new_k.set_bit(qubit, !k.bit(qubit)); + accum.insert(new_k, *v); + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut chunk| { + for (k, v) in chunk.drain() { + accum.insert(k, v); + } + accum + }); + } + } + /// Flushes the current operations buffer and updates the resulting state, clearing the buffer /// in the process. /// # Panics /// /// This function will panic if the given ids that do not correspond to allocated qubits. - fn flush(&mut self) { + pub(crate) fn flush(&mut self) { if !self.op_buffer.targets.is_empty() { // Swap each of the target qubits to the right-most entries in the state, in order. self.op_buffer @@ -427,6 +502,289 @@ where self.joint_collapse(&locs, res); res } + + /// Utility function that performs the actual output of state (and optionally map) to screen. Can + /// be called internally from other functions to aid in debugging and does not perform any modification + /// of the internal structures. + fn dump_impl(&self, print_id_map: bool) { + if print_id_map { + println!("MAP: {:?}", self.id_map); + }; + print!("STATE: [ "); + let mut sorted_keys = self.state.keys().collect::>(); + sorted_keys.sort_unstable(); + for key in sorted_keys { + print!( + "|{}\u{27e9}: {}, ", + key, + self.state.get(key).map_or_else(Complex64::zero, |v| *v) + ); + } + println!("]"); + } + + /// Utility that actually performs the application of the buffered unitary to the targets within the + /// sparse state. + fn apply_impl(&mut self) { + // let state_size = 1_usize << self.id_map.len(); + let chunk_size = parallel_chunk_size(self.state.len()); + let op_size = self.op_buffer.ops.nrows(); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (index, val) in chunk { + let i = index / op_size; + let l = (index % op_size) + .to_usize() + .expect("Cannot operate on more than 64 qubits at a time."); + for j in + (0..op_size).filter(|j| !self.op_buffer.ops.row(*j)[l].is_nearly_zero()) + { + let loc = (&i * op_size) + j; + if let Some(entry) = accum.get_mut(&loc) { + *entry += self.op_buffer.ops.row(j)[l] * val; + } else { + accum.insert((&i * op_size) + j, self.op_buffer.ops.row(j)[l] * val); + } + if accum + .get(&loc) + .map_or_else(|| false, |entry| (*entry).is_nearly_zero()) + { + accum.remove(&loc); + } + } + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut sparse_chunk| { + for (k, v) in sparse_chunk.drain() { + if let Some(entry) = accum.get_mut(&k) { + *entry += v; + } else if !v.is_nearly_zero() { + accum.insert(k.clone(), v); + } + if accum + .get(&k) + .map_or_else(Complex64::one, |entry| *entry) + .is_nearly_zero() + { + accum.remove(&k); + } + } + accum + }); + assert!( + !self.state.is_empty(), + "State vector should never be empty." + ); + } + + /// Utility to get the sum of all probabilies where an odd number of the bits at the given locations + /// are set. This corresponds to the probability of jointly measuring those qubits in the computational + /// basis. + fn check_joint_probability(&self, locs: &[usize]) -> f64 { + let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { + accum | (BigUint::one() << loc) + }); + (&self.state) + .into_par_iter() + .fold( + || 0.0_f64, + |accum, (index, val)| { + if (index & &mask).count_ones() & 1 > 0 { + accum + val.norm_sqr() + } else { + accum + } + }, + ) + .sum() + } + + /// Utility to perform the normalize of the state. + fn normalize(&mut self) { + let scale = 1.0 + / self + .state + .par_iter() + .fold(|| 0.0_f64, |sum, (_, val)| sum + val.norm_sqr()) + .sum::() + .sqrt(); + + self.state.par_iter_mut().for_each(|(_, val)| *val *= scale); + } + + /// Utility to collapse the probability at the given location based on the boolean value. This means + /// that if the given value is 'true' then all keys in the sparse state where the given location + /// has a zero bit will be reduced to zero and removed. Then the sparse state is normalized. + fn collapse(&mut self, loc: usize, val: bool) { + self.joint_collapse(&[loc], val); + } + + /// Utility to collapse the joint probability of a particular set of locations in the sparse state. + /// The entries that do not correspond to the given boolean value are removed, and then the whole + /// state is normalized. + fn joint_collapse(&mut self, locs: &[usize], val: bool) { + let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { + accum | (BigUint::one() << loc) + }); + + let chunk_size = parallel_chunk_size(self.state.len()); + self.state = self + .state + .drain() + .collect::>() + .par_chunks(chunk_size) + .fold(FxHashMap::default, |mut accum, chunk| { + for (k, v) in chunk + .iter() + .filter(|(index, _)| ((index & &mask).count_ones() & 1 > 0) == val) + { + accum.insert(k.clone(), *v); + } + accum + }) + .reduce(FxHashMap::default, |mut accum, mut chunk| { + for (k, v) in chunk.drain() { + accum.insert(k, v); + } + accum + }); + + self.normalize(); + } + + fn fast_gate(&mut self, target: usize, mut op: F) + where + F: FnMut((BigUint, Complex64), u64) -> (BigUint, Complex64), + { + assert!(self.op_buffer.targets.is_empty()); + let target = *self + .id_map + .get(&target) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", target)); + self.state = self + .state + .drain() + .into_iter() + .map(|kvp| op(kvp, target as u64)) + .fold(SparseState::default(), |mut accum, (k, v)| { + accum.insert(k, v); + accum + }); + } + + fn fast_controlled_gate(&mut self, ctls: &[usize], target: usize, mut op: F) + where + F: FnMut((BigUint, Complex64), u64) -> (BigUint, Complex64), + { + assert!(self.op_buffer.targets.is_empty()); + + let target = *self + .id_map + .get(&target) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", target)); + + let ctls: Vec = ctls + .iter() + .map(|c| { + *self + .id_map + .get(c) + .unwrap_or_else(|| panic!("Unable to find qubit with id {}", c)) + }) + .collect(); + + let mut sorted_qubits = ctls.clone(); + sorted_qubits.push(target); + sorted_qubits.sort_unstable(); + if let ControlFlow::Break(Some(duplicate)) = + sorted_qubits.iter().try_fold(None, |last, current| { + last.map_or_else( + || ControlFlow::Continue(Some(current)), + |last| { + if last == current { + ControlFlow::Break(Some(current)) + } else { + ControlFlow::Continue(Some(current)) + } + }, + ) + }) + { + panic!("Duplicate qubit id '{}' found in application.", duplicate); + } + + self.state = self + .state + .drain() + .into_iter() + .map(|kvp| { + if ctls.iter().all(|c| kvp.0.bit(*c as u64)) { + op(kvp, target as u64) + } else { + kvp + } + }) + .fold(SparseState::default(), |mut accum, (k, v)| { + accum.insert(k, v); + accum + }); + } + + fn x_transform((mut index, val): (BigUint, Complex64), target: u64) -> (BigUint, Complex64) { + index.set_bit(target, !index.bit(target)); + (index, val) + } + + pub(crate) fn fast_x(&mut self, target: usize) { + self.fast_gate(target, Self::x_transform); + } + + pub(crate) fn fast_mcx(&mut self, ctls: &[usize], target: usize) { + self.fast_controlled_gate(ctls, target, Self::x_transform); + } + + fn y_transform( + (mut index, mut val): (BigUint, Complex64), + target: u64, + ) -> (BigUint, Complex64) { + index.set_bit(target, !index.bit(target)); + val *= if index.bit(target) { + Complex64::i() + } else { + -Complex64::i() + }; + (index, val) + } + + pub(crate) fn fast_y(&mut self, target: usize) { + self.fast_gate(target, Self::y_transform); + } + + pub(crate) fn fast_mcy(&mut self, ctls: &[usize], target: usize) { + self.fast_controlled_gate(ctls, target, Self::y_transform); + } + + fn z_transform((index, mut val): (BigUint, Complex64), target: u64) -> (BigUint, Complex64) { + val *= if index.bit(target) { + -Complex64::one() + } else { + Complex64::one() + }; + (index, val) + } + + pub(crate) fn fast_z(&mut self, target: usize) { + self.fast_gate(target, Self::z_transform); + } + + pub(crate) fn fast_mcz(&mut self, ctls: &[usize], target: usize) { + self.fast_controlled_gate(ctls, target, Self::z_transform); + } } #[cfg(test)] @@ -435,7 +793,6 @@ mod tests { common_matrices::{adjoint, controlled, h, s, x}, *, }; - use crate::sparsestate::SparseStateQuantumSim; use ndarray::array; use num_traits::{One, Zero}; @@ -446,7 +803,7 @@ mod tests { // Test that basic allocation and release of qubits doesn't fail. #[test] fn test_alloc_release() { - let sim = &mut SparseStateQuantumSim::default(); + let sim = &mut QuantumSim::default(); for i in 0..16 { assert_eq!(sim.allocate(), i); } @@ -469,7 +826,7 @@ mod tests { /// of the state. #[test] fn test_apply1() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); for i in 0..6 { assert_eq!(sim.allocate(), i); } @@ -489,7 +846,7 @@ mod tests { /// of the state. #[test] fn test_apply2() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); let q0 = sim.allocate(); let q1 = sim.allocate(); let q2 = sim.allocate(); @@ -507,7 +864,7 @@ mod tests { /// Verifies that application of gates to a qubit results in the correct probabilities. #[test] fn test_probability() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); let q = sim.allocate(); let extra = sim.allocate(); assert!(almost_equal(0.0, sim.joint_probability(&[q]))); @@ -535,7 +892,7 @@ mod tests { /// can be operationally reset back into the ground state. #[test] fn test_measure() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); let q = sim.allocate(); let extra = sim.allocate(); assert!(!sim.measure(q)); @@ -562,7 +919,7 @@ mod tests { /// qubits. #[test] fn test_joint_probability() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); let q0 = sim.allocate(); let q1 = sim.allocate(); assert!(almost_equal(0.0, sim.joint_probability(&[q0, q1]))); @@ -582,7 +939,7 @@ mod tests { /// qubits. #[test] fn test_joint_measurement() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); let q0 = sim.allocate(); let q1 = sim.allocate(); assert!(!sim.joint_measure(&[q0, q1])); @@ -605,7 +962,7 @@ mod tests { /// Test arbitrary controls, which should extend the applied unitary matrix. #[test] fn test_arbitrary_controls() { - let mut sim = SparseStateQuantumSim::default(); + let mut sim = QuantumSim::default(); let q0 = sim.allocate(); let q1 = sim.allocate(); let q2 = sim.allocate(); @@ -635,7 +992,7 @@ mod tests { #[test] #[should_panic(expected = "Duplicate qubit id '0' found in application.")] fn test_duplicate_target() { - let mut sim = SparseStateQuantumSim::new(); + let mut sim = QuantumSim::new(); let q = sim.allocate(); sim.apply(&controlled(&x(), 1), &[q, q], None); } @@ -644,7 +1001,7 @@ mod tests { #[test] #[should_panic(expected = "Duplicate qubit id '1' found in application.")] fn test_duplicate_control() { - let mut sim = SparseStateQuantumSim::new(); + let mut sim = QuantumSim::new(); let q = sim.allocate(); let c = sim.allocate(); sim.apply(&x(), &[q], Some(&[c, c])); @@ -654,7 +1011,7 @@ mod tests { #[test] #[should_panic(expected = "Duplicate qubit id '0' found in application.")] fn test_target_in_control() { - let mut sim = SparseStateQuantumSim::new(); + let mut sim = QuantumSim::new(); let q = sim.allocate(); let c = sim.allocate(); sim.apply(&x(), &[q], Some(&[c, q])); @@ -667,7 +1024,7 @@ mod tests { expected = "Application given incorrect number of targets; expected 2, given 1." )] fn test_target_count() { - let mut sim = SparseStateQuantumSim::new(); + let mut sim = QuantumSim::new(); let q = sim.allocate(); sim.apply(&controlled(&x(), 1), &[q], None); } @@ -676,7 +1033,7 @@ mod tests { #[test] #[should_panic(expected = "Application given non-square matrix.")] fn test_nonsquare() { - let mut sim = SparseStateQuantumSim::new(); + let mut sim = QuantumSim::new(); let q = sim.allocate(); sim.apply(&array![[Complex64::zero()], [Complex64::one()]], &[q], None); } @@ -684,7 +1041,7 @@ mod tests { /// Large, entangled state handling. #[test] fn test_large_state() { - let mut sim = SparseStateQuantumSim::new(); + let mut sim = QuantumSim::new(); let ctl = sim.allocate(); sim.apply(&h(), &[ctl], None); for _ in 0..4999 { @@ -696,4 +1053,113 @@ mod tests { sim.release(i); } } + + /// Utility for testing operation equivalence. + fn assert_operation_equal_referenced(mut op: F1, mut reference: F2, count: usize) + where + F1: FnMut(&mut QuantumSim, &[usize]), + F2: FnMut(&mut QuantumSim, &[usize]), + { + let mut sim = QuantumSim::new(); + + // Allocte the control we use to verify behavior. + let ctl = sim.allocate(); + sim.apply(&common_matrices::h(), &[ctl], None); + + // Allocate the requested number of targets, entangling the control with them. + let mut qs = vec![]; + for _ in 0..count { + let q = sim.allocate(); + sim.apply(&common_matrices::x(), &[q], Some(&[ctl])); + qs.push(q); + } + + op(&mut sim, &qs); + reference(&mut sim, &qs); + + // Undo the entanglement. + for q in qs { + sim.apply(&common_matrices::x(), &[q], Some(&[ctl])); + sim.release(q); + } + sim.apply(&common_matrices::h(), &[ctl], None); + + // We know the operations are equal if the control is left in the zero state. + assert!(sim.joint_probability(&[ctl]).is_nearly_zero()); + } + + /// Test fast X + #[test] + fn test_fast_x() { + assert_operation_equal_referenced( + |sim, qs| { + sim.flush(); + sim.fast_x(qs[0]); + }, + |sim, qs| { + sim.apply(&common_matrices::x(), &[qs[0]], None); + }, + 1, + ); + } + + /// Test fast Y + #[test] + fn test_fast_y() { + assert_operation_equal_referenced( + |sim, qs| { + sim.flush(); + sim.fast_y(qs[0]); + }, + |sim, qs| { + sim.apply(&common_matrices::y(), &[qs[0]], None); + }, + 1, + ); + } + + /// Test fast Z + #[test] + fn test_fast_z() { + assert_operation_equal_referenced( + |sim, qs| { + sim.flush(); + sim.fast_z(qs[0]); + }, + |sim, qs| { + sim.apply(&common_matrices::z(), &[qs[0]], None); + }, + 1, + ); + } + + /// Test fast CX + #[test] + fn test_fast_cx() { + assert_operation_equal_referenced( + |sim, qs| { + sim.flush(); + sim.fast_mcx(&[qs[0]], qs[1]); + }, + |sim, qs| { + sim.apply(&common_matrices::x(), &[qs[1]], Some(&[qs[0]])); + }, + 2, + ); + } + + /// Test fast CZ + #[test] + fn test_fast_cz() { + assert_operation_equal_referenced( + |sim, qs| { + sim.flush(); + sim.fast_mcz(&[qs[0]], qs[1]); + }, + |sim, qs| { + sim.apply(&common_matrices::z(), &[qs[1]], Some(&[qs[0]])); + }, + 2, + ); + } } diff --git a/src/Qir/riqir/sim/src/sparsestate.rs b/src/Qir/riqir/sim/src/sparsestate.rs deleted file mode 100644 index dd423d5b329..00000000000 --- a/src/Qir/riqir/sim/src/sparsestate.rs +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use crate::nearly_zero::NearlyZero; -use ndarray::Array2; -use num_bigint::BigUint; -use num_complex::Complex64; -use num_traits::{One, ToPrimitive, Zero}; -use rayon::prelude::*; -use rustc_hash::FxHashMap; - -use super::simulator::{detail::QuantumSimImpl, parallel_chunk_size, OpBuffer, QuantumSim}; - -pub type SparseState = FxHashMap; - -/// Sparse quantum state simulation using a dictionary of states with non-zero amplitudes, based on . -/// This simulator is memory efficient for highly entangled states, allowing for the use of up to 128 qubits, but less performant -/// than full state simulation for dense quantum states. -pub(crate) type SparseStateQuantumSim = QuantumSim; - -impl Default for QuantumSim { - fn default() -> Self { - Self::new() - } -} - -impl QuantumSim { - /// Creates a new sparse state quantum simulator object with empty initial state (no qubits allocated, no operations buffered). - #[must_use] - pub fn new() -> Self { - QuantumSim { - state: FxHashMap::default(), - - id_map: FxHashMap::default(), - - op_buffer: OpBuffer { - targets: vec![], - ops: Array2::default((0, 0)), - }, - } - } -} - -impl QuantumSimImpl for QuantumSim { - /// Utility that extends the internal state to make room for a newly allocated qubit. - /// # Panics - /// - /// This function will panic if the total number of allocated qubits would increase beyond 128. - fn extend_state(&mut self) { - if self.id_map.is_empty() { - // Add the intial value for the zero state. - self.state.insert(BigUint::zero(), Complex64::one()); - } - } - - /// Utility function that will swap states of two qubits throughout the sparse state map. - fn swap_qubits(&mut self, qubit1: usize, qubit2: usize) { - if qubit1 == qubit2 { - return; - } - - let (q1, q2) = (qubit1 as u64, qubit2 as u64); - - // In parallel, swap entries in the sparse state to correspond to swapping of two qubits' - // locations. - let chunk_size = parallel_chunk_size(self.state.len()); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (k, v) in chunk { - if k.bit(q1) == k.bit(q2) { - accum.insert(k.clone(), *v); - } else { - let mut new_k = k.clone(); - new_k.set_bit(q1, !k.bit(q1)); - new_k.set_bit(q2, !k.bit(q2)); - accum.insert(new_k, *v); - } - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut chunk| { - for (k, v) in chunk.drain() { - accum.insert(k, v); - } - accum - }); - } - - /// Releases the given qubit, collapsing its state in the process. After release that identifier is - /// no longer valid for use in other functions and will cause an error if used. - /// # Panics - /// - /// The function will panic if the given id does not correpsond to an allocated qubit. - fn cleanup_state(&mut self, res: bool) { - if res { - let qubit = self.id_map.len() as u64; - let chunk_size = parallel_chunk_size(self.state.len()); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (k, v) in chunk { - let mut new_k = k.clone(); - new_k.set_bit(qubit, !k.bit(qubit)); - accum.insert(new_k, *v); - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut chunk| { - for (k, v) in chunk.drain() { - accum.insert(k, v); - } - accum - }); - } - } - - /// Utility function that performs the actual output of state (and optionally map) to screen. Can - /// be called internally from other functions to aid in debugging and does not perform any modification - /// of the internal structures. - fn dump_impl(&self, print_id_map: bool) { - if print_id_map { - println!("MAP: {:?}", self.id_map); - }; - print!("STATE: [ "); - let mut sorted_keys = self.state.keys().collect::>(); - sorted_keys.sort_unstable(); - for key in sorted_keys { - print!( - "|{}\u{27e9}: {}, ", - key, - self.state.get(key).map_or_else(Complex64::zero, |v| *v) - ); - } - println!("]"); - } - - /// Utility that actually performs the application of the buffered unitary to the targets within the - /// sparse state. - fn apply_impl(&mut self) { - // let state_size = 1_usize << self.id_map.len(); - let chunk_size = parallel_chunk_size(self.state.len()); - let op_size = self.op_buffer.ops.nrows(); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (index, val) in chunk { - let i = index / op_size; - let l = (index % op_size) - .to_usize() - .expect("Cannot operate on more than 64 qubits at a time."); - for j in (0..op_size) - .filter(|j| !self.op_buffer.ops.row(*j as usize)[l].is_nearly_zero()) - { - let loc = (&i * op_size) + (j as u128); - if let Some(entry) = accum.get_mut(&loc) { - *entry += self.op_buffer.ops.row(j)[l] * val; - } else { - accum.insert((&i * op_size) + j, self.op_buffer.ops.row(j)[l] * val); - } - if accum - .get(&loc) - .map_or_else(|| false, |entry| (*entry).is_nearly_zero()) - { - accum.remove(&loc); - } - } - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut sparse_chunk| { - for (k, v) in sparse_chunk.drain() { - if let Some(entry) = accum.get_mut(&k) { - *entry += v; - } else if !v.is_nearly_zero() { - accum.insert(k.clone(), v); - } - if accum - .get(&k) - .map_or_else(Complex64::one, |entry| *entry) - .is_nearly_zero() - { - accum.remove(&k); - } - } - accum - }); - } - - /// Utility to get the sum of all probabilies where an odd number of the bits at the given locations - /// are set. This corresponds to the probability of jointly measuring those qubits in the computational - /// basis. - fn check_joint_probability(&self, locs: &[usize]) -> f64 { - let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { - accum | (BigUint::one() << loc) - }); - (&self.state) - .into_par_iter() - .fold( - || 0.0_f64, - |accum, (index, val)| { - if (index & &mask).count_ones() & 1 > 0 { - accum + val.norm_sqr() - } else { - accum - } - }, - ) - .sum() - } - - /// Utility to perform the normalize of the state. - fn normalize(&mut self) { - let scale = 1.0 - / self - .state - .par_iter() - .fold(|| 0.0_f64, |sum, (_, val)| sum + val.norm_sqr()) - .sum::() - .sqrt(); - - self.state.par_iter_mut().for_each(|(_, val)| *val *= scale); - } - - /// Utility to collapse the probability at the given location based on the boolean value. This means - /// that if the given value is 'true' then all keys in the sparse state where the given location - /// has a zero bit will be reduced to zero and removed. Then the sparse state is normalized. - fn collapse(&mut self, loc: usize, val: bool) { - self.joint_collapse(&[loc], val); - } - - /// Utility to collapse the joint probability of a particular set of locations in the sparse state. - /// The entries that do not correspond to the given boolean value are removed, and then the whole - /// state is normalized. - fn joint_collapse(&mut self, locs: &[usize], val: bool) { - let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { - accum | (BigUint::one() << loc) - }); - - let chunk_size = parallel_chunk_size(self.state.len()); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (k, v) in chunk - .iter() - .filter(|(index, _)| ((index & &mask).count_ones() & 1 > 0) == val) - { - accum.insert(k.clone(), *v); - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut chunk| { - for (k, v) in chunk.drain() { - accum.insert(k, v); - } - accum - }); - - self.normalize(); - } -} From 4640bf23fa6dd98c6046255ddb3a6ac4fdbd9706 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 2 Sep 2022 22:40:50 -0700 Subject: [PATCH 18/55] Rust qubit/result to string fixes --- src/Qir/riqir/sim/src/lib.rs | 16 ++++++++++++++++ src/Qir/riqir/sim/src/result_bool.rs | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Qir/riqir/sim/src/lib.rs b/src/Qir/riqir/sim/src/lib.rs index 55128ec6a72..14c5e154889 100644 --- a/src/Qir/riqir/sim/src/lib.rs +++ b/src/Qir/riqir/sim/src/lib.rs @@ -862,6 +862,22 @@ pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { *sim_lock = Some(sim); } +/// QIR API for getting the string interpretation of a qubit identifier. +/// # Panics +/// +/// This function will panic if it is unable to allocate the memory for the string. +#[no_mangle] +pub extern "C" fn __quantum__rt__qubit_to_string(qubit: *mut c_void) -> *const CString { + unsafe { + __quantum__rt__string_create( + CString::new(format!("{}", qubit as usize)) + .unwrap() + .as_bytes_with_nul() + .as_ptr() as *mut i8, + ) + } +} + /// API for viewing the current global result and quantum state for the simulator. #[no_mangle] pub extern "C" fn dump_state() { diff --git a/src/Qir/riqir/sim/src/result_bool.rs b/src/Qir/riqir/sim/src/result_bool.rs index 08a200edac3..6e6adb509cc 100644 --- a/src/Qir/riqir/sim/src/result_bool.rs +++ b/src/Qir/riqir/sim/src/result_bool.rs @@ -31,9 +31,9 @@ pub extern "C" fn __quantum__rt__result_to_string(res: *mut c_void) -> *const CS __quantum__rt__string_create( CString::new( if __quantum__rt__result_equal(res, __quantum__rt__result_get_one()) { - "1" + "One" } else { - "0" + "Zero" }, ) .expect("Failed to allocate memory for result string.") From 402bc440a9cf5517cc94dffdf3e4f978d7fca20a Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 3 Sep 2022 00:22:55 -0700 Subject: [PATCH 19/55] Fullstate sim as QIR backend, staticlib runtime --- build/steps-init.yml | 4 +- src/Qir/Runtime/lib/CMakeLists.txt | 1 - .../Runtime/lib/range_support/CMakeLists.txt | 28 - .../Runtime/lib/range_support/bridge-rt.cpp | 4 - src/Qir/Tests/QIR-static/CMakeLists.txt | 1 - .../QIR-static/qir-test-conditionals.cpp | 175 ------ .../qsharp/qir-test-conditionals.qs | 66 --- src/Qir/riqir-test/CMakeLists.txt | 7 +- .../FullstateSimulator/CMakeLists.txt | 5 +- src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt | 5 +- src/Qir/riqir-test/QIR-static/CMakeLists.txt | 6 +- src/Qir/riqir/build-qir-runtime.ps1 | 14 +- src/Qir/riqir/runtime/Cargo.toml | 5 +- src/Qir/riqir/runtime/build.rs | 57 ++ src/Qir/riqir/runtime/include/QirRuntime.h | 230 ++++++++ src/Qir/riqir/runtime/include/qir_runtime.def | 76 +++ .../runtime/src}/bridge-rt.ll | 0 src/Simulation/Native/src/CMakeLists.txt | 23 +- src/Simulation/Native/src/simulator/capi.cpp | 5 + src/Simulation/Native/src/simulator/capi.hpp | 1 + src/Simulation/Native/src/simulator/qir.cpp | 497 ++++++++++++++++++ src/Simulation/Native/src/simulator/qir.hpp | 117 +++++ .../src/simulator/simulatorinterface.hpp | 1 + src/Simulation/qdk_sim_rs/prerequisites.ps1 | 4 +- 24 files changed, 1032 insertions(+), 300 deletions(-) delete mode 100644 src/Qir/Runtime/lib/range_support/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/range_support/bridge-rt.cpp delete mode 100644 src/Qir/Tests/QIR-static/qir-test-conditionals.cpp delete mode 100644 src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs create mode 100644 src/Qir/riqir/runtime/build.rs create mode 100644 src/Qir/riqir/runtime/include/QirRuntime.h create mode 100644 src/Qir/riqir/runtime/include/qir_runtime.def rename src/Qir/{Runtime/lib/range_support => riqir/runtime/src}/bridge-rt.ll (100%) create mode 100644 src/Simulation/Native/src/simulator/qir.cpp create mode 100644 src/Simulation/Native/src/simulator/qir.hpp diff --git a/build/steps-init.yml b/build/steps-init.yml index feb3e4cc351..d0283749e8c 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -28,8 +28,8 @@ steps: - script: | rustup install nightly - rustup component add rustfmt clippy - rustup component add rustfmt clippy --toolchain nightly + rustup component add rustfmt clippy llvm-tools-preview + rustup component add rustfmt clippy llvm-tools-preview --toolchain nightly displayName: Enable Rust formatting and nightly options. ## diff --git a/src/Qir/Runtime/lib/CMakeLists.txt b/src/Qir/Runtime/lib/CMakeLists.txt index 38525545c07..343d7162ebc 100644 --- a/src/Qir/Runtime/lib/CMakeLists.txt +++ b/src/Qir/Runtime/lib/CMakeLists.txt @@ -3,4 +3,3 @@ add_subdirectory(QSharpFoundation) add_subdirectory(QSharpCore) add_subdirectory(Simulators) add_subdirectory(Tracer) -add_subdirectory(range_support) diff --git a/src/Qir/Runtime/lib/range_support/CMakeLists.txt b/src/Qir/Runtime/lib/range_support/CMakeLists.txt deleted file mode 100644 index 3e28a0727ca..00000000000 --- a/src/Qir/Runtime/lib/range_support/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -#=============================================================================== -# Produce the qir_range_support dynamic library -# -add_library(qir_range_support SHARED bridge-rt.cpp) - -target_source_from_qir(qir_range_support bridge-rt.ll) - -set(QIR_LIB_PATH "${CMAKE_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") - -target_link_libraries(qir_range_support - ${CMAKE_DL_LIBS} - "-L${QIR_LIB_PATH}" - -lqir_sim - ${SPECTRE_LIBS} -) - -target_include_directories(qir_range_support PUBLIC - ${public_includes} - ${common_includes} -) - -set_property(TARGET qir_range_support PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS qir_range_support - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) \ No newline at end of file diff --git a/src/Qir/Runtime/lib/range_support/bridge-rt.cpp b/src/Qir/Runtime/lib/range_support/bridge-rt.cpp deleted file mode 100644 index f6e9440b83e..00000000000 --- a/src/Qir/Runtime/lib/range_support/bridge-rt.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QirRuntime.hpp" diff --git a/src/Qir/Tests/QIR-static/CMakeLists.txt b/src/Qir/Tests/QIR-static/CMakeLists.txt index 323d8222a7e..6d38a5a1cce 100644 --- a/src/Qir/Tests/QIR-static/CMakeLists.txt +++ b/src/Qir/Tests/QIR-static/CMakeLists.txt @@ -8,7 +8,6 @@ set(TEST_FILES # add_executable(qir-static-tests qir-driver.cpp - qir-test-conditionals.cpp qir-test-math.cpp qir-test-strings.cpp qir-test-ouput.cpp diff --git a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp deleted file mode 100644 index 361db7dbb81..00000000000 --- a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include -#include -#include -#include - -#include "catch.hpp" - -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimulatorStub.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -// TestConditionalOnResult() is authored in a way that the expected path through the function only applies X operator -// for the chosen sequence of measurement results, and all other paths apply Y. Thus, the correct execution must get the -// expected maximum number of X and ControlledX callbacks. -struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - int nGateCallback = 0; - vector xCallbacks; - vector cxCallbacks; - vector otherCallbacks; - - vector mockMeasurements; - size_t nextMeasureResult = 0; - - explicit ConditionalsTestSimulator(vector&& results) : mockMeasurements(results) - { - } - - std::string GetHistory() - { - std::stringstream out; - out << "X: "; - for (int i : this->xCallbacks) - { - out << i << ","; - } - - out << std::endl << "CX: "; - for (int i : this->cxCallbacks) - { - out << i << ","; - } - - out << std::endl << "Other: "; - for (int i : this->otherCallbacks) - { - out << i << ","; - } - return out.str(); - } - - QubitIdType AllocateQubit() override - { - return 0; - } - void ReleaseQubit(QubitIdType /*qubit*/) override - { - } - - void X(QubitIdType) override - { - this->xCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - void ControlledX(long /* numControls */, QubitIdType* /* controls */, QubitIdType /* qubit */) override - { - this->cxCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - void Y(QubitIdType) override - { - this->otherCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - void ControlledY(long /* numControls */, QubitIdType* /* controls */, QubitIdType /* qubit */) override - { - this->otherCallbacks.push_back(this->nGateCallback); - this->nGateCallback++; - } - - Result Measure(long /* numBases */, PauliId* /* bases */, long /* numTargets */, - QubitIdType* /* targets */) override - { - assert(this->nextMeasureResult < this->mockMeasurements.size() && - "ConditionalsTestSimulator isn't set up correctly"); - - Result r = (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); - this->nextMeasureResult++; - return r; - } - - bool AreEqualResults(Result r1, Result r2) override - { - // those are bogus pointers but it's ok to compare them _as pointers_ - return (r1 == r2); - } - - void ReleaseResult(Result /*result*/) override - { - } // the results aren't allocated by this test simulator - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; - -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIf__Interop(); // NOLINT -TEST_CASE("QIR: ApplyIf", "[qir][qir.conditionals]") -{ -#ifndef RIQIR_TESTING - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); -#endif - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIf__Interop()); - -#ifndef RIQIR_TESTING - INFO(qapi->GetHistory()); - CHECK(qapi->xCallbacks.size() == 8); - CHECK(qapi->cxCallbacks.size() == 0); - CHECK(qapi->otherCallbacks.size() == 0); -#endif -} - -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__Interop(); // NOLINT -TEST_CASE("QIR: ApplyIf with functors", "[qir][qir.conditionals]") -{ -#ifndef RIQIR_TESTING - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); -#endif - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyIfWithFunctors__Interop()); - -#ifndef RIQIR_TESTING - INFO(qapi->GetHistory()); - CHECK(qapi->xCallbacks.size() == 5); - CHECK(qapi->cxCallbacks.size() == 7); - CHECK(qapi->otherCallbacks.size() == 0); -#endif -} - -extern "C" void Microsoft__Quantum__Testing__QIR__TestApplyConditionally__Interop(); // NOLINT -TEST_CASE("QIR: ApplyConditionally", "[qir][qir.conditionals]") -{ -#ifndef RIQIR_TESTING - unique_ptr qapi = - make_unique(vector{Result_Zero, Result_One}); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); -#endif - - CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestApplyConditionally__Interop()); - -#ifndef RIQIR_TESTING - INFO(qapi->GetHistory()); - CHECK(qapi->xCallbacks.size() == 4); - CHECK(qapi->cxCallbacks.size() == 2); - CHECK(qapi->otherCallbacks.size() == 0); -#endif -} diff --git a/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs b/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs deleted file mode 100644 index 14faa88321c..00000000000 --- a/src/Qir/Tests/QIR-static/qsharp/qir-test-conditionals.qs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -namespace Microsoft.Quantum.Testing.QIR { - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.ClassicalControl; - - @EntryPoint() - operation TestApplyIf() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - let r1 = M(q1); // expected: r1 = Zero - X(q2); - let r2 = M(q2); // expected: r2 = One - - ApplyIfElseR(r1, (X, q1), (Y, q1)); - ApplyIfElseR(r2, (Y, q1), (X, q1)); - - // Other variants - ApplyIfElseRA(r1, (X, q1), (Y, q1)); - ApplyIfElseRC(r1, (X, q1), (Y, q1)); - ApplyIfElseRCA(r1, (X, q1), (Y, q1)); - ApplyIfOne(r2, (X, q1)); - ApplyIfZero(r1, (X, q1)); - } - - @EntryPoint() - operation TestApplyIfWithFunctors() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - let r1 = M(q1); - X(q2); - let r2 = M(q2); - - Adjoint ApplyIfElseRCA(r1, (X, q1), (Y, q1)); - Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); - Adjoint Controlled ApplyIfElseRCA([q2], (r1, (X, q1), (Y, q1))); - Adjoint ApplyIfElseRA(r1, (X, q1), (Y, q1)); - Controlled ApplyIfElseRC([q2], (r1, (X, q1), (Y, q1))); - Adjoint ApplyIfOneA(r2, (X, q1)); - Controlled ApplyIfOneC([q2], (r2, (X, q1))); - Adjoint Controlled ApplyIfOneCA([q2], (r2, (X, q1))); - Adjoint ApplyIfZeroA(r1, (X, q1)); - Controlled ApplyIfZeroC([q2], (r1, (X, q1))); - Adjoint Controlled ApplyIfZeroCA([q2], (r1, (X, q1))); - } - - @EntryPoint() - operation TestApplyConditionally() : Unit { - use q1 = Qubit(); - use q2 = Qubit(); - - let r1 = M(q1); - X(q2); - let r2 = M(q2); - - ApplyConditionally([r1], [r2], (Y, q1), (X, q1)); - ApplyConditionally([r1, One], [Zero, r2], (X, q1), (Y, q1)); - - Adjoint ApplyConditionallyA([r1], [r2], (Y, q1), (X, q1)); - Controlled ApplyConditionallyC([q2], ([r1], [r2], (Y, q1), (X, q1))); - Adjoint Controlled ApplyConditionallyCA([q2], ([r1], [r2], (Y, q1), (X, q1))); - } - -} \ No newline at end of file diff --git a/src/Qir/riqir-test/CMakeLists.txt b/src/Qir/riqir-test/CMakeLists.txt index 77eebe4ebe2..35a282bf9d2 100644 --- a/src/Qir/riqir-test/CMakeLists.txt +++ b/src/Qir/riqir-test/CMakeLists.txt @@ -22,7 +22,12 @@ set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") -set(runtime_lib_path "${PROJECT_SOURCE_DIR}/../drops/bin/$ENV{BUILD_PLATFORM}/native") +# set the environment path for loading shared libs the tests are using +if(DEFINED ENV{NATIVE_SIMULATOR}) + set(simulator_lib_path $ENV{NATIVE_SIMULATOR}) +else() + set(simulator_lib_path "${PROJECT_SOURCE_DIR}/../../Simulation/Native/build/drop") +endif() include(qir_cmake_include) include(unit_test_include) diff --git a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt index a036bb2e1fc..c3fc6685cfd 100644 --- a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt +++ b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt @@ -4,9 +4,8 @@ add_executable(fullstate-simulator-tests target_source_from_qir(fullstate-simulator-tests ../../Tests/FullstateSimulator/qsharp/obj/qsharp/qir-test-simulator.bc) target_link_libraries(fullstate-simulator-tests PUBLIC - "-L${runtime_lib_path}" - -lqir_range_support - -lqir_sim + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(fullstate-simulator-tests PUBLIC diff --git a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt index b43ce0c27ab..0a47549335e 100644 --- a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt +++ b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt @@ -15,9 +15,8 @@ foreach(file ${TEST_FILES}) endforeach() target_link_libraries(qir-dynamic-tests PUBLIC - "-L${runtime_lib_path}" - -lqir_range_support - -lqir_sim + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(qir-dynamic-tests PUBLIC diff --git a/src/Qir/riqir-test/QIR-static/CMakeLists.txt b/src/Qir/riqir-test/QIR-static/CMakeLists.txt index c7ce4b710c0..e3da5d882f0 100644 --- a/src/Qir/riqir-test/QIR-static/CMakeLists.txt +++ b/src/Qir/riqir-test/QIR-static/CMakeLists.txt @@ -7,7 +7,6 @@ set(TEST_FILES # add_executable(qir-static-tests ../../Tests/QIR-static/qir-driver.cpp - ../../Tests/QIR-static/qir-test-conditionals.cpp ../../Tests/QIR-static/qir-test-math.cpp ../../Tests/QIR-static/qir-test-strings.cpp ../../Tests/QIR-static/qir-test-other.cpp @@ -18,9 +17,8 @@ foreach(file ${TEST_FILES}) endforeach() target_link_libraries(qir-static-tests PUBLIC - "-L${runtime_lib_path}" - -lqir_range_support - -lqir_sim + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(qir-static-tests PUBLIC diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-runtime.ps1 index 895c518598a..4e7a137038e 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-runtime.ps1 @@ -34,22 +34,22 @@ foreach ($folder in "runtime","sim") { # Copy the results of compilation and the corresponding headers to the QIR drops folder so # they can be included in pipeline artifacts. $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) + $qirDropsInclude = (Join-Path $Env:QIR_DROPS include) if (-not (Test-Path $Env:QIR_DROPS)) { New-Item -Path $Env:QIR_DROPS -ItemType "directory" } if (-not (Test-Path $qirDropsBin)) { New-Item -Path $qirDropsBin -ItemType "directory" } + if (-not (Test-Path $qirDropsInclude)) { + New-Item -Path $qirDropsInclude -ItemType "directory" + } $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) Copy-Item $qirBinaries $qirDropsBin -Include "*qir_$folder*" -Exclude "*.rlib","*.d","*.exp" - # For Windows platforms, make sure to update the extension of the lib file used for linking - # from *.dll.lib to just .lib - $rustlib = (Join-Path $qirDropsBin "qir_$folder.dll.lib") - if (Test-Path $rustlib) { - Remove-Item (Join-Path $qirDropsBin "qir_$folder.lib") -ErrorAction SilentlyContinue - Rename-Item $rustlib "qir_$folder.lib" - } + # Copy the C API header and def file + Copy-Item (Join-Path $PSScriptRoot runtime include QirRuntime.h) (Join-Path $Env:QIR_DROPS include) + Copy-Item (Join-Path $PSScriptRoot runtime include qir_runtime.def) (Join-Path $Env:QIR_DROPS include) # When building in CI, free disk space by cleaning up. # Note that this takes longer, but saves ~1 GB of space. diff --git a/src/Qir/riqir/runtime/Cargo.toml b/src/Qir/riqir/runtime/Cargo.toml index df3da8a8e30..14a140c65b1 100644 --- a/src/Qir/riqir/runtime/Cargo.toml +++ b/src/Qir/riqir/runtime/Cargo.toml @@ -8,5 +8,8 @@ edition = "2021" num-bigint = { version = "0.4.3", default-features = false } rand = "0.8.5" +[build-dependencies] +llvm-tools = "0.1.1" + [lib] -crate-type = ["cdylib","rlib"] +crate-type = ["staticlib","rlib"] diff --git a/src/Qir/riqir/runtime/build.rs b/src/Qir/riqir/runtime/build.rs new file mode 100644 index 00000000000..3c4d47bfb32 --- /dev/null +++ b/src/Qir/riqir/runtime/build.rs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +use llvm_tools::LlvmTools; + +fn main() -> Result<(), String> { + // Compile the LLVM IR bridge file. Requires the llvm-tools-preview component. + let out_dir = env::var_os("OUT_DIR") + .map(PathBuf::from) + .ok_or_else(|| "Environment variable OUT_DIR not defined.".to_string())?; + + let llvm_tools = LlvmTools::new().map_err(|err| { + format!( + "Failed to locate llvm tools: {:?}. Is the llvm-tools-preview component installed?", + err + ) + })?; + + let llc_path = llvm_tools + .tool(llvm_tools::exe("llc").to_string().as_str()) + .ok_or_else(|| "Failed to find llc.".to_string())?; + let llvm_ar_path = llvm_tools + .tool(llvm_tools::exe("llvm-ar").to_string().as_str()) + .ok_or_else(|| "Failed to find llvm-ar.".to_string())?; + let lib_name = if cfg!(target_os = "windows") { + "bridge-rt.lib" + } else { + "libbridge-rt.a" + }; + + Command::new(llc_path) + .args(&[ + "--filetype=obj", + "./src/bridge-rt.ll", + "-o", + &format!("{}/bridge-rt.o", out_dir.display()), + ]) + .status() + .map_err(|err| format!("llc failed: {}.", err))?; + Command::new(llvm_ar_path) + .args(&[ + "-r", + &format!("{}/{}", out_dir.display(), lib_name), + &format!("{}/bridge-rt.o", out_dir.display()), + ]) + .status() + .map_err(|err| format!("llvm-ar failed: {}.", err))?; + + println!("cargo:rustc-link-lib=static=bridge-rt"); + println!("cargo:rustc-link-search=native={}", out_dir.display()); + + Ok(()) +} diff --git a/src/Qir/riqir/runtime/include/QirRuntime.h b/src/Qir/riqir/runtime/include/QirRuntime.h new file mode 100644 index 00000000000..b264e776dbc --- /dev/null +++ b/src/Qir/riqir/runtime/include/QirRuntime.h @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include + +#ifdef _WIN32 +#define QIR_SHARED_API __declspec(dllexport) +#else +#define QIR_SHARED_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + struct QirString; + struct QirArray; + struct QirCallable; + struct QirTuple; + struct QirBigInt; + + enum PauliId : int8_t + { + PauliId_I = 0, + PauliId_X = 1, + PauliId_Z = 2, + PauliId_Y = 3, + }; + + typedef void (*t_CallableEntry)(QirTuple*, QirTuple*, QirTuple*); + typedef void (*t_CaptureCallback)(QirTuple*, int32_t); + + // Returns a pointer to the malloc-allocated block. + QIR_SHARED_API char* __quantum__rt__memory_allocate(uint64_t size); // NOLINT + + // Fail the computation with the given error message. + [[noreturn]] QIR_SHARED_API void __quantum__rt__fail(QirString* msg); // NOLINT + + // Include the given message in the computation's execution log or equivalent. + QIR_SHARED_API void __quantum__rt__message(QirString* msg); // NOLINT + + // Creates a new 1-dimensional array. The int is the size of each element in bytes. The int64_t is the length + // of the array. The bytes of the new array should be set to zero. + QIR_SHARED_API QirArray* __quantum__rt__array_create_1d(int32_t, int64_t); // NOLINT + + // Adds the given integer value to the reference count for the array. Deallocates the array if the reference count + // becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__array_update_reference_count(QirArray*, int32_t); // NOLINT + + // Adds the given integer value to the alias count for the array. Fails if the count becomes negative. + QIR_SHARED_API void __quantum__rt__array_update_alias_count(QirArray*, int32_t); // NOLINT + + // Creates a shallow copy of the array if the user count is larger than 0 or the second argument is `true`. + QIR_SHARED_API QirArray* __quantum__rt__array_copy(QirArray*, bool); // NOLINT + + // Returns a new array which is the concatenation of the two passed-in arrays. + QIR_SHARED_API QirArray* __quantum__rt__array_concatenate(QirArray*, QirArray*); // NOLINT + + // Returns the length of a dimension of the array. The int is the zero-based dimension to return the length of; it + // must be 0 for a 1-dimensional array. + QIR_SHARED_API int64_t __quantum__rt__array_get_size_1d(QirArray*); // NOLINT + + // Returns a pointer to the element of the array at the zero-based index given by the int64_t. + QIR_SHARED_API char* __quantum__rt__array_get_element_ptr_1d(QirArray*, int64_t); // NOLINT + + // Initializes the callable with the provided function table and capture tuple. The capture tuple pointer + // should be null if there is no capture. + QIR_SHARED_API QirCallable* __quantum__rt__callable_create(t_CallableEntry*, t_CaptureCallback*, + QirTuple*); // NOLINT + + // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference + // count becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__callable_update_reference_count(QirCallable*, int32_t); // NOLINT + + // Adds the given integer value to the alias count for the callable. Fails if the count becomes negative. + QIR_SHARED_API void __quantum__rt__callable_update_alias_count(QirCallable*, int32_t); // NOLINT + + // Creates a shallow copy of the callable if the alias count is larger than 0 or the second argument is `true`. + // Returns the given callable pointer otherwise, after increasing its reference count by 1. + QIR_SHARED_API QirCallable* __quantum__rt__callable_copy(QirCallable*, bool); // NOLINT + + // Invokes the callable with the provided argument tuple and fills in the result tuple. + QIR_SHARED_API void __quantum__rt__callable_invoke(QirCallable*, QirTuple*, QirTuple*); // NOLINT + + // Updates the callable by applying the Adjoint functor. + QIR_SHARED_API void __quantum__rt__callable_make_adjoint(QirCallable*); // NOLINT + + // Updates the callable by applying the Controlled functor. + QIR_SHARED_API void __quantum__rt__callable_make_controlled(QirCallable*); // NOLINT + + // Invokes the function in the corresponding index in the memory management table of the callable with the capture + // tuple and the given 32-bit integer. Does nothing if the memory management table pointer or the function pointer + // at that index is null. + QIR_SHARED_API void __quantum__rt__capture_update_reference_count(QirCallable*, int32_t); // NOLINT + QIR_SHARED_API void __quantum__rt__capture_update_alias_count(QirCallable*, int32_t); // NOLINT + + // Creates a string from an array of UTF-8 bytes. + QIR_SHARED_API QirString* __quantum__rt__string_create(const char*); // NOLINT + + // Adds the given integer value to the reference count for the string. Deallocates the string if the reference count + // becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__string_update_reference_count(QirString*, int32_t); // NOLINT + + // Creates a new string that is the concatenation of the two argument strings. + QIR_SHARED_API QirString* __quantum__rt__string_concatenate(QirString*, QirString*); // NOLINT + + // Returns true if the two strings are equal, false otherwise. + QIR_SHARED_API bool __quantum__rt__string_equal(QirString*, QirString*); // NOLINT + + // Returns a string representation of the integer. + QIR_SHARED_API QirString* __quantum__rt__int_to_string(int64_t); // NOLINT + + // Returns a string representation of the double. + QIR_SHARED_API QirString* __quantum__rt__double_to_string(double); // NOLINT + + // Returns a string representation of the Boolean. + QIR_SHARED_API QirString* __quantum__rt__bool_to_string(bool); // NOLINT + + // Returns a string representation of the Pauli. + QIR_SHARED_API QirString* __quantum__rt__pauli_to_string(PauliId); // NOLINT + + // Returns a pointer to an array that contains a null-terminated sequence of characters + // (i.e., a C-string) representing the current value of the string object. + QIR_SHARED_API const char* __quantum__rt__string_get_data(QirString* str); // NOLINT + + // Returns the length of the string, in terms of bytes. + // http://www.cplusplus.com/reference/string/string/size/ + QIR_SHARED_API uint32_t __quantum__rt__string_get_length(QirString* str); // NOLINT + + // Allocates space for a tuple requiring the given number of bytes and sets the reference count to 1. + QIR_SHARED_API QirTuple* __quantum__rt__tuple_create(int64_t); // NOLINT + + // Adds the given integer value to the reference count for the tuple. Deallocates the tuple if the reference count + // becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__tuple_update_reference_count(QirTuple*, int32_t); // NOLINT + + // Adds the given integer value to the alias count for the tuple. Fails if the count becomes negative. + QIR_SHARED_API void __quantum__rt__tuple_update_alias_count(QirTuple*, int32_t); // NOLINT + + // Creates a shallow copy of the tuple if the user count is larger than 0 or the second argument is `true`. + QIR_SHARED_API QirTuple* __quantum__rt__tuple_copy(QirTuple*, bool force); // NOLINT + + // Returns a string representation of the big integer. + QIR_SHARED_API QirString* __quantum__rt__bigint_to_string(QirBigInt*); // NOLINT + + // Creates a big integer with the specified initial value. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_i64(int64_t); // NOLINT + + // Creates a big integer with the initial value specified by the i8 array. The 0-th element of the array is the + // highest-order byte, followed by the first element, etc. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_array(int, char*); // NOLINT + + // Adds the given integer value to the reference count for the big integer. Deallocates the big integer if the + // reference count becomes 0. The behavior is undefined if the reference count becomes negative. + QIR_SHARED_API void __quantum__rt__bigint_update_reference_count(QirBigInt*, int32_t); // NOLINT + + // Returns the negative of the big integer. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_negate(QirBigInt*); // NOLINT + + // Adds two big integers and returns their sum. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_add(QirBigInt*, QirBigInt*); // NOLINT + + // Subtracts the second big integer from the first and returns their difference. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_subtract(QirBigInt*, QirBigInt*); // NOLINT + + // Multiplies two big integers and returns their product. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_multiply(QirBigInt*, QirBigInt*); // NOLINT + + // Divides the first big integer by the second and returns their quotient. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_divide(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the first big integer modulo the second. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_modulus(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the big integer raised to the integer power. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_power(QirBigInt*, int); // NOLINT + + // Returns the bitwise-AND of two big integers. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitand(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the bitwise-OR of two big integers. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitor(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the bitwise-XOR of two big integers. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitxor(QirBigInt*, QirBigInt*); // NOLINT + + // Returns the bitwise complement of the big integer. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitnot(QirBigInt*); // NOLINT + + // Returns the big integer arithmetically shifted left by the integer amount of bits. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftleft(QirBigInt*, int64_t); // NOLINT + + // Returns the big integer arithmetically shifted right by the integer amount of bits. + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftright(QirBigInt*, int64_t); // NOLINT + + // Returns true if the two big integers are equal, false otherwise. + QIR_SHARED_API bool __quantum__rt__bigint_equal(QirBigInt*, QirBigInt*); // NOLINT + + // Returns true if the first big integer is greater than the second, false otherwise. + QIR_SHARED_API bool __quantum__rt__bigint_greater(QirBigInt*, QirBigInt*); // NOLINT + + // Returns true if the first big integer is greater than or equal to the second, false otherwise. + QIR_SHARED_API bool __quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT + + // Q# Math: + QIR_SHARED_API double __quantum__qis__nan__body(); // NOLINT + QIR_SHARED_API bool __quantum__qis__isnan__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__infinity__body(); // NOLINT + QIR_SHARED_API bool __quantum__qis__isinf__body(double d); // NOLINT + QIR_SHARED_API bool __quantum__qis__isnegativeinfinity__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__sin__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__cos__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__tan__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__arctan2__body(double y, double x); // NOLINT + QIR_SHARED_API double __quantum__qis__sinh__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__cosh__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__tanh__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__arcsin__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__arccos__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__arctan__body(double theta); // NOLINT + QIR_SHARED_API double __quantum__qis__sqrt__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__log__body(double d); // NOLINT + QIR_SHARED_API double __quantum__qis__ieeeremainder__body(double x, double y); // NOLINT + QIR_SHARED_API int64_t __quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT + QIR_SHARED_API double __quantum__qis__drawrandomdouble__body(double minimum, double maximum); // NOLINT + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/Qir/riqir/runtime/include/qir_runtime.def b/src/Qir/riqir/runtime/include/qir_runtime.def new file mode 100644 index 00000000000..940cf636dab --- /dev/null +++ b/src/Qir/riqir/runtime/include/qir_runtime.def @@ -0,0 +1,76 @@ +EXPORTS + __quantum__rt__memory_allocate + __quantum__rt__fail + __quantum__rt__message + __quantum__rt__array_create_1d + __quantum__rt__array_update_reference_count + __quantum__rt__array_update_alias_count + __quantum__rt__array_copy + __quantum__rt__array_concatenate + __quantum__rt__array_get_size_1d + __quantum__rt__array_get_element_ptr_1d + __quantum__rt__array_slice_1d + __quantum__rt__callable_create + __quantum__rt__callable_update_reference_count + __quantum__rt__callable_update_alias_count + __quantum__rt__callable_copy + __quantum__rt__callable_invoke + __quantum__rt__callable_make_adjoint + __quantum__rt__callable_make_controlled + __quantum__rt__capture_update_reference_count + __quantum__rt__capture_update_alias_count + __quantum__rt__string_create + __quantum__rt__string_update_reference_count + __quantum__rt__string_concatenate + __quantum__rt__string_equal + __quantum__rt__int_to_string + __quantum__rt__double_to_string + __quantum__rt__bool_to_string + __quantum__rt__pauli_to_string + __quantum__rt__range_to_string + __quantum__rt__string_get_data + __quantum__rt__string_get_length + __quantum__rt__tuple_create + __quantum__rt__tuple_update_reference_count + __quantum__rt__tuple_update_alias_count + __quantum__rt__tuple_copy + __quantum__rt__bigint_to_string + __quantum__rt__bigint_create_i64 + __quantum__rt__bigint_create_array + __quantum__rt__bigint_update_reference_count + __quantum__rt__bigint_negate + __quantum__rt__bigint_add + __quantum__rt__bigint_subtract + __quantum__rt__bigint_multiply + __quantum__rt__bigint_divide + __quantum__rt__bigint_modulus + __quantum__rt__bigint_power + __quantum__rt__bigint_bitand + __quantum__rt__bigint_bitor + __quantum__rt__bigint_bitxor + __quantum__rt__bigint_bitnot + __quantum__rt__bigint_shiftleft + __quantum__rt__bigint_shiftright + __quantum__rt__bigint_equal + __quantum__rt__bigint_greater + __quantum__rt__bigint_greater_eq + __quantum__qis__nan__body + __quantum__qis__isnan__body + __quantum__qis__infinity__body + __quantum__qis__isinf__body + __quantum__qis__isnegativeinfinity__body + __quantum__qis__sin__body + __quantum__qis__cos__body + __quantum__qis__tan__body + __quantum__qis__arctan2__body + __quantum__qis__sinh__body + __quantum__qis__cosh__body + __quantum__qis__tanh__body + __quantum__qis__arcsin__body + __quantum__qis__arccos__body + __quantum__qis__arctan__body + __quantum__qis__sqrt__body + __quantum__qis__log__body + __quantum__qis__ieeeremainder__body + __quantum__qis__drawrandomint__body + __quantum__qis__drawrandomdouble__body \ No newline at end of file diff --git a/src/Qir/Runtime/lib/range_support/bridge-rt.ll b/src/Qir/riqir/runtime/src/bridge-rt.ll similarity index 100% rename from src/Qir/Runtime/lib/range_support/bridge-rt.ll rename to src/Qir/riqir/runtime/src/bridge-rt.ll diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 5f062824764..cf7eadc9af7 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -13,7 +13,7 @@ configure_file(version.hpp.in ${PROJECT_BINARY_DIR}/src/version.hpp) add_subdirectory(util) add_subdirectory(simulator) -set(SOURCES simulator/factory.cpp simulator/capi.cpp simulator/simulator.cpp util/openmp.cpp simulator/simulatoravx.cpp simulator/simulatoravx2.cpp simulator/simulatoravx512.cpp ) +set(SOURCES simulator/factory.cpp simulator/capi.cpp simulator/simulator.cpp util/openmp.cpp simulator/simulatoravx.cpp simulator/simulatoravx2.cpp simulator/simulatoravx512.cpp simulator/qir.cpp) if(BUILD_SHARED_LIBS) add_library(Microsoft.Quantum.Simulator.Runtime SHARED ${SOURCES}) set_source_files_properties(simulator/capi.cpp PROPERTIES COMPILE_FLAGS ${AVXFLAGS}) @@ -34,9 +34,28 @@ else(BUILD_SHARED_LIBS) set_source_files_properties(simulator/simulatoravx512.cpp PROPERTIES COMPILE_FLAGS ${AVX512FLAGS}) endif(BUILD_SHARED_LIBS) -target_link_libraries(Microsoft.Quantum.Simulator.Runtime ${SPECTRE_LIBS}) +target_include_directories(Microsoft.Quantum.Simulator.Runtime PRIVATE "${PROJECT_BINARY_DIR}/../../../Qir/drops/include") +target_link_libraries(Microsoft.Quantum.Simulator.Runtime + ${SPECTRE_LIBS} + "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" + -lqir_runtime +) + +if (WIN32) + target_link_libraries(Microsoft.Quantum.Simulator.Runtime + Ws2_32 + Bcrypt + Userenv + ) + target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_runtime.def") +else() + target_link_libraries(Microsoft.Quantum.Simulator.Runtime + pthread + ) +endif() install(TARGETS Microsoft.Quantum.Simulator.Runtime RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/drop" LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/drop" + ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/drop" ) diff --git a/src/Simulation/Native/src/simulator/capi.cpp b/src/Simulation/Native/src/simulator/capi.cpp index 50c3d7ffd42..48e7d491f8e 100644 --- a/src/Simulation/Native/src/simulator/capi.cpp +++ b/src/Simulation/Native/src/simulator/capi.cpp @@ -64,6 +64,11 @@ extern "C" return Microsoft::Quantum::Simulator::get(sid)->InjectState(qubits, amplitudes); } + MICROSOFT_QUANTUM_DECL unsigned allocate(unsigned id) + { + return Microsoft::Quantum::Simulator::get(id)->allocate(); + } + MICROSOFT_QUANTUM_DECL void allocateQubit(unsigned id, unsigned q) { Microsoft::Quantum::Simulator::get(id)->allocateQubit(q); diff --git a/src/Simulation/Native/src/simulator/capi.hpp b/src/Simulation/Native/src/simulator/capi.hpp index c033060dc19..22a86bd4e99 100644 --- a/src/Simulation/Native/src/simulator/capi.hpp +++ b/src/Simulation/Native/src/simulator/capi.hpp @@ -61,6 +61,7 @@ extern "C" ); // allocate and release + MICROSOFT_QUANTUM_DECL unsigned allocate(unsigned sid); // NOLINT MICROSOFT_QUANTUM_DECL void allocateQubit(unsigned sid, unsigned qid); // NOLINT MICROSOFT_QUANTUM_DECL bool release(unsigned sid, unsigned q); // NOLINT MICROSOFT_QUANTUM_DECL unsigned num_qubits(unsigned sid); // NOLINT diff --git a/src/Simulation/Native/src/simulator/qir.cpp b/src/Simulation/Native/src/simulator/qir.cpp new file mode 100644 index 00000000000..ea3ea8d7b61 --- /dev/null +++ b/src/Simulation/Native/src/simulator/qir.cpp @@ -0,0 +1,497 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/*============================================================================= + QIR assumes a single global execution context. + To support the dispatch over the qir-bridge, the clients must register their + Microsoft::Quantum::IRuntimeDriver* first. +=============================================================================*/ +#include +#include +#include +#include +#include + +#include "capi.hpp" +#include "qir.hpp" + +static const unsigned GLOBAL_SIM = init(); + +// Pauli consts are {i2} in QIR, likely stored as {i8} in arrays, but we are using the standard C++ enum type based on +// {i32} so cannot pass through the buffer and have to allocate a new one instead and copy. +std::vector ExtractPauliIds(QirArray* paulis) +{ + const auto count = __quantum__rt__array_get_size_1d(paulis); + std::vector pauliIds; + pauliIds.reserve(count); + for (auto i = 0; i < count; i++) + { + pauliIds.push_back(static_cast(*__quantum__rt__array_get_element_ptr_1d(paulis, i))); + } + return pauliIds; +} + +std::vector GetQubitIds(long num, QubitIdType* qubits) +{ + std::vector ids; + ids.reserve((size_t)num); + for (long i = 0; i < num; i++) + { + ids.push_back(static_cast(qubits[i])); + } + return ids; +} + +std::vector GetBases(long num, PauliId* paulis) +{ + std::vector convertedBases; + convertedBases.reserve((size_t)num); + for (auto i = 0; i < num; i++) + { + convertedBases.push_back(static_cast(paulis[i])); + } + return convertedBases; +} + +// Comparing floating point values with `==` or `!=` is not reliable. +// The values can be extremely close but still not exactly equal. +// It is more reliable to check if one value is within certain small tolerance near the other value. +// This template function is for comparing two floating point values. +template +inline bool Close(TFloat val1, TFloat val2) +{ + assert( + std::is_floating_point_v> && + "Unexpected type is passed as a template argument"); + + constexpr TFloat tolerance = 1e-10; + + // Both val1 and val2 can be close (or equal) to the maximum (or minimum) value representable with its type. + // Adding to (or subtracting from) such a value can cause overflow (or underflow). + // To avoid the overflow (or underflow) we don't add to the greater value (and don't sutract from a lesser value). + if (val1 < val2) + { + return (val2 - val1) <= tolerance; + } + return (val1 - val2) <= tolerance; +} + +std::ostream& GetOutStream(void* location, std::ofstream& outFileStream) +{ + // If the location is not nullptr and not empty string then dump to a file: + if ((location != nullptr) && ((__quantum__rt__string_get_length(static_cast(location))) != 0)) + { + // Open the file for appending: + const std::string& filePath = __quantum__rt__string_get_data(static_cast(location)); + + bool openException = false; + try + { + outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); + } + catch (const std::ofstream::failure& e) + { + openException = true; + std::cerr << "Exception caught: \"" << e.what() << "\".\n"; + } + + if (((outFileStream.rdstate() & std::ofstream::failbit) != 0) || openException) + { + std::cerr << "Failed to open dump file \"" + filePath + "\".\n"; + return std::cout; + } + + return outFileStream; + } + + // Otherwise dump to std::cout: + return std::cout; +} + +TDumpToLocationCallback const dumpToLocationCallback = + [](size_t idx, double re, double im, TDumpLocation location) -> bool +{ + std::ostream& outStream = *reinterpret_cast(location); + + if (!Close(re, 0.0) || !Close(im, 0.0)) + { + outStream << "|" << std::bitset<8>(idx) << ">: " << re << "+" << im << "i" << std::endl; + } + return true; +}; + +void DumpMachineImpl(std::ostream& outStream) +{ + outStream << "# wave function for qubits (least to most significant qubit ids):" << std::endl; + DumpToLocation(GLOBAL_SIM, dumpToLocationCallback, (TDumpLocation)&outStream); + outStream.flush(); +} + +void DumpRegisterImpl(std::ostream& outStream, QirArray* qubits) +{ + outStream << "# wave function for qubits with ids (least to most significant): "; + + auto count = __quantum__rt__array_get_size_1d(qubits); + std::vector ids = + GetQubitIds(count, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); + for (auto idx = 0; idx < ids.size(); ++idx) + { + if (idx != 0) + { + outStream << "; "; + } + outStream << ids[idx]; + } + outStream << ':' << std::endl; + + if (!DumpQubitsToLocation(GLOBAL_SIM, ids.size(), ids.data(), dumpToLocationCallback, (TDumpLocation)&outStream)) + { + outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" + << std::endl; + } + outStream.flush(); +} + +bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) +{ + assert( + rs1 != nullptr && rs2 != nullptr && + __quantum__rt__array_get_size_1d(rs1) == __quantum__rt__array_get_size_1d(rs2)); + + RESULT** results1 = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(rs1, 0)); + RESULT** results2 = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(rs2, 0)); + for (auto i = 0; i < __quantum__rt__array_get_size_1d(rs1); i++) + { + if (!__quantum__rt__result_equal(results1[i], results2[i])) + { + return false; + } + } + return true; +} + +extern "C" +{ + QUBIT* __quantum__rt__qubit_allocate() + { + return reinterpret_cast(allocate(GLOBAL_SIM)); + } + + QirArray* __quantum__rt__qubit_allocate_array(int64_t count) + { + QirArray* array = __quantum__rt__array_create_1d(sizeof(intptr_t), count); + for (auto i = 0; i < count; i++) + { + *reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(array, i)) = + __quantum__rt__qubit_allocate(); + } + return array; + } + + void __quantum__rt__qubit_release(QUBIT* qubit) + { + release(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__rt__qubit_release_array(QirArray* array) + { + auto count = __quantum__rt__array_get_size_1d(array); + for (auto i = 0; i < count; i++) + { + __quantum__rt__qubit_release(*reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(array, i))); + } + __quantum__rt__array_update_reference_count(array, -1); + } + + QirString* __quantum__rt__qubit_to_string(QUBIT* qubit) + { + return __quantum__rt__string_create(std::to_string(reinterpret_cast(qubit)).c_str()); + } + + RESULT* __quantum__rt__result_get_one() + { + return reinterpret_cast(1); + } + + RESULT* __quantum__rt__result_get_zero() + { + return reinterpret_cast(0); + } + + bool __quantum__rt__result_equal(RESULT* r1, RESULT* r2) + { + return r1 == r2; + } + + void __quantum__rt__result_update_reference_count(RESULT* r, int32_t increment) + { + // No-op + } + + QirString* __quantum__rt__result_to_string(RESULT* result) + { + return __quantum__rt__result_equal(result, __quantum__rt__result_get_zero()) + ? __quantum__rt__string_create("Zero") + : __quantum__rt__string_create("One"); + } + + void __quantum__rt__result_record_output(RESULT* res) + { + if (__quantum__rt__result_equal(res, __quantum__rt__result_get_zero())) + { + __quantum__rt__message(__quantum__rt__string_create("RESULT\t0\n")); + } + else + { + __quantum__rt__message(__quantum__rt__string_create("RESULT\t1\n")); + } + } + + void __quantum__qis__exp__body(QirArray* paulis, double angle, QirArray* qubits) + { + assert(__quantum__rt__array_get_size_1d(paulis) == __quantum__rt__array_get_size_1d(qubits)); + std::vector pauliIds = ExtractPauliIds(paulis); + auto numTargets = pauliIds.size(); + auto targets = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0)); + std::vector ids = GetQubitIds(numTargets, targets); + std::vector convertedBases = GetBases(numTargets, pauliIds.data()); + Exp(GLOBAL_SIM, (unsigned)numTargets, convertedBases.data(), angle, ids.data()); + } + + void __quantum__qis__exp__adj(QirArray* paulis, double angle, QirArray* qubits) + { + __quantum__qis__exp__body(paulis, -angle, qubits); + } + + void __quantum__qis__exp__ctl(QirArray* ctls, QirExpTuple* args) + { + assert(__quantum__rt__array_get_size_1d(args->paulis) == __quantum__rt__array_get_size_1d(args->targets)); + + std::vector pauliIds = ExtractPauliIds(args->paulis); + auto numControls = __quantum__rt__array_get_size_1d(ctls); + auto numTargets = pauliIds.size(); + auto controls = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0)); + auto targets = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(args->targets, 0)); + std::vector idsTargets = GetQubitIds(numTargets, targets); + std::vector idsControls = GetQubitIds(numControls, controls); + std::vector convertedBases = GetBases(numTargets, pauliIds.data()); + MCExp( + GLOBAL_SIM, (unsigned)numTargets, convertedBases.data(), args->angle, (unsigned)numControls, idsControls.data(), + idsTargets.data()); + } + + void __quantum__qis__exp__ctladj(QirArray* ctls, QirExpTuple* args) + { + assert(__quantum__rt__array_get_size_1d(args->paulis) == __quantum__rt__array_get_size_1d(args->targets)); + + QirExpTuple updatedArgs = {args->paulis, -(args->angle), args->targets}; + + __quantum__qis__exp__ctl(ctls, &updatedArgs); + } + + void __quantum__qis__h__body(QUBIT* qubit) + { + H(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__h__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCH(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + RESULT* __quantum__qis__measure__body(QirArray* paulis, QirArray* qubits) + { + assert(__quantum__rt__array_get_size_1d(qubits) == __quantum__rt__array_get_size_1d(paulis)); + + std::vector pauliIds = ExtractPauliIds(paulis); + auto count = pauliIds.size(); + std::vector convertedBases = GetBases(count, pauliIds.data()); + std::vector ids = + GetQubitIds(count, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); + return Measure(GLOBAL_SIM, count, convertedBases.data(), ids.data()) ? __quantum__rt__result_get_one() + : __quantum__rt__result_get_zero(); + } + + void __quantum__qis__r__body(PauliId axis, double angle, QUBIT* qubit) + { + R(GLOBAL_SIM, static_cast(axis), angle, reinterpret_cast(qubit)); + } + + void __quantum__qis__r__adj(PauliId axis, double angle, QUBIT* qubit) + { + __quantum__qis__r__body(axis, -angle, qubit); + } + + void __quantum__qis__r__ctl(QirArray* ctls, QirRTuple* args) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector controls = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCR(GLOBAL_SIM, static_cast(args->pauli), args->angle, numControls, controls.data(), + reinterpret_cast(args->target)); + } + + void __quantum__qis__r__ctladj(QirArray* ctls, QirRTuple* args) + { + QirRTuple updated = {args->pauli, -args->angle, args->target}; + __quantum__qis__r__ctl(ctls, &updated); + } + + void __quantum__qis__s__body(QUBIT* qubit) + { + S(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__s__adj(QUBIT* qubit) + { + AdjS(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__s__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCS(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__s__ctladj(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCAdjS(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__t__body(QUBIT* qubit) + { + T(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__t__adj(QUBIT* qubit) + { + AdjT(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__t__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCT(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__t__ctladj(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCAdjT(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__x__body(QUBIT* qubit) + { + X(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__x__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCX(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__y__body(QUBIT* qubit) + { + Y(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__y__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCY(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__z__body(QUBIT* qubit) + { + Z(GLOBAL_SIM, reinterpret_cast(qubit)); + } + + void __quantum__qis__z__ctl(QirArray* ctls, QUBIT* qubit) + { + auto numControls = __quantum__rt__array_get_size_1d(ctls); + std::vector ids = + GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); + MCZ(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + } + + void __quantum__qis__dumpmachine__body(uint8_t* location) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpMachineImpl(outStream); + } + + void __quantum__qis__dumpregister__body(uint8_t* location, QirArray* qubits) + { + std::ofstream outFileStream; + std::ostream& outStream = GetOutStream(location, outFileStream); + DumpRegisterImpl(outStream, qubits); + } + + void __quantum__qis__assertmeasurementprobability__body( + QirArray* bases, + QirArray* qubits, + RESULT* result, + double prob, + QirString* msg, + double tol) + { + if (__quantum__rt__array_get_size_1d(bases) != __quantum__rt__array_get_size_1d(qubits)) + { + __quantum__rt__fail( + __quantum__rt__string_create("Both input arrays - bases, qubits - for AssertMeasurementProbability(), " + "must be of same size.")); + } + + if (__quantum__rt__result_equal(result, __quantum__rt__result_get_one())) + { + prob = 1.0 - prob; + } + + // Convert paulis from sequence of bytes to sequence of PauliId: + std::vector paulis((uint64_t)__quantum__rt__array_get_size_1d(bases)); + for (auto i = 0; i < __quantum__rt__array_get_size_1d(bases); ++i) + { + paulis[i] = (PauliId)*__quantum__rt__array_get_element_ptr_1d(bases, i); + } + + std::vector ids = GetQubitIds( + paulis.size(), reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); + std::vector convertedBases = GetBases(paulis.size(), paulis.data()); + double actualProbability = + 1.0 - JointEnsembleProbability( + GLOBAL_SIM, (unsigned)paulis.size(), reinterpret_cast(convertedBases.data()), ids.data()); + + if (!(std::abs(actualProbability - prob) < 1e-10)) + { + __quantum__rt__fail(msg); + } + } + + void __quantum__qis__assertmeasurementprobability__ctl( + QirArray* /* ctls */, + QirAssertMeasurementProbabilityTuple* args) + { + // Controlled AssertMeasurementProbability ignores control bits. See the discussion on + // https://github.com/microsoft/qsharp-runtime/pull/450 for more details. + __quantum__qis__assertmeasurementprobability__body( + args->bases, args->qubits, args->result, args->prob, args->msg, args->tol); + } +} diff --git a/src/Simulation/Native/src/simulator/qir.hpp b/src/Simulation/Native/src/simulator/qir.hpp new file mode 100644 index 00000000000..9cf6302b91e --- /dev/null +++ b/src/Simulation/Native/src/simulator/qir.hpp @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "QirRuntime.h" +#include + +#ifdef _WIN32 +#define QIR_EXPORT_API __declspec(dllexport) +#else +#define QIR_EXPORT_API +#endif + +/*============================================================================== + Qubit & Result + + These two types are opaque to the clients: clients cannot directly create, delete, + copy or check state of qubits and results. QUBIT* and RESULT* should never be + dereferenced in client's code. +==============================================================================*/ + +// Although QIR uses an opaque pointer to the type "QUBIT", it never points to an actual memory +// and is never intended to be dereferenced anywhere - in the client code or in our runtime. +// Runtime always operates in terms of qubit ids, which are integers. Qubit ids are casted +// to this pointer type and stored as pointer values. This is done to ensure that qubit type +// is a unique type in the QIR. + +class QUBIT; +typedef intptr_t QubitIdType; + +class RESULT; + +struct QirRTuple +{ + PauliId pauli; + double angle; + QUBIT* target; +}; + +struct QirExpTuple +{ + QirArray* paulis; + double angle; + QirArray* targets; +}; + +struct QirAssertMeasurementProbabilityTuple +{ + QirArray* bases; + QirArray* qubits; + RESULT* result; + double prob; + QirString* msg; + double tol; +}; + +extern "C" +{ + // Quantum Runtime + QIR_EXPORT_API QUBIT* __quantum__rt__qubit_allocate(); // NOLINT + QIR_EXPORT_API QirArray* __quantum__rt__qubit_allocate_array(int64_t count); // NOLINT + QIR_EXPORT_API void __quantum__rt__qubit_release(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__rt__qubit_release_array(QirArray*); // NOLINT + QIR_EXPORT_API QirString* __quantum__rt__qubit_to_string(QUBIT*); // NOLINT + QIR_EXPORT_API bool __quantum__rt__result_equal(RESULT*, RESULT*); // NOLINT + QIR_EXPORT_API void __quantum__rt__result_update_reference_count(RESULT*, int32_t); // NOLINT + QIR_EXPORT_API RESULT* __quantum__rt__result_get_one(); // NOLINT + QIR_EXPORT_API RESULT* __quantum__rt__result_get_zero(); // NOLINT + QIR_EXPORT_API QirString* __quantum__rt__result_to_string(RESULT*); // NOLINT + QIR_EXPORT_API void __quantum__rt__result_record_output(RESULT*); // NOLINT + + // Q# Gate Set + QIR_EXPORT_API void __quantum__qis__exp__body(QirArray*, double, QirArray*); // NOLINT + QIR_EXPORT_API void __quantum__qis__exp__adj(QirArray*, double, QirArray*); // NOLINT + QIR_EXPORT_API void __quantum__qis__exp__ctl(QirArray*, QirExpTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__exp__ctladj(QirArray*, QirExpTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__h__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__h__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API RESULT* __quantum__qis__measure__body(QirArray*, QirArray*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__body(PauliId, double, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__adj(PauliId, double, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__ctl(QirArray*, QirRTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__r__ctladj(QirArray*, QirRTuple*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__adj(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__s__ctladj(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__adj(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__t__ctladj(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__x__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__x__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__y__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__y__ctl(QirArray*, QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__z__body(QUBIT*); // NOLINT + QIR_EXPORT_API void __quantum__qis__z__ctl(QirArray*, QUBIT*); // NOLINT + + // Q# Dump: + // Note: The param `location` must be `const void*`, + // but it is called from .ll, where `const void*` is not supported. + QIR_EXPORT_API void __quantum__qis__dumpmachine__body(uint8_t* location); // NOLINT + QIR_EXPORT_API void __quantum__qis__dumpregister__body(uint8_t* location, QirArray* qubits); // NOLINT + + // Q# Assert Measurement: + QIR_EXPORT_API void __quantum__qis__assertmeasurementprobability__body( // NOLINT + QirArray* bases, + QirArray* qubits, + RESULT* result, + double prob, + QirString* msg, + double tol); + QIR_EXPORT_API void __quantum__qis__assertmeasurementprobability__ctl( // NOLINT + QirArray* ctls, + QirAssertMeasurementProbabilityTuple* args); +} diff --git a/src/Simulation/Native/src/simulator/simulatorinterface.hpp b/src/Simulation/Native/src/simulator/simulatorinterface.hpp index 0d075951c68..0c9e239be37 100644 --- a/src/Simulation/Native/src/simulator/simulatorinterface.hpp +++ b/src/Simulation/Native/src/simulator/simulatorinterface.hpp @@ -36,6 +36,7 @@ class SimulatorInterface const std::vector& amplitudes) = 0; // allocate and release + virtual unsigned allocate() = 0; virtual void allocateQubit(unsigned q) = 0; virtual bool release(unsigned q) = 0; virtual unsigned num_qubits() const = 0; diff --git a/src/Simulation/qdk_sim_rs/prerequisites.ps1 b/src/Simulation/qdk_sim_rs/prerequisites.ps1 index 5ad244230c4..c8a597edf2c 100644 --- a/src/Simulation/qdk_sim_rs/prerequisites.ps1 +++ b/src/Simulation/qdk_sim_rs/prerequisites.ps1 @@ -22,5 +22,5 @@ if (-not (Get-Command rustup -ErrorAction SilentlyContinue)) { # rustfmt and clippy is available. rustup install nightly rustup toolchain install nightly -rustup component add rustfmt clippy -rustup component add rustfmt clippy --toolchain nightly +rustup component add rustfmt clippy llvm-tools-preview +rustup component add rustfmt clippy llvm-tools-preview --toolchain nightly From 8f1cda84a31dfe4ceebdff7bc168f5ce23bf5512 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 3 Sep 2022 00:43:27 -0700 Subject: [PATCH 20/55] rename runtime to stdlib --- Cargo.toml | 2 +- bootstrap.ps1 | 16 ++++++++-------- build/build.ps1 | 2 +- build/test.ps1 | 2 +- ...uild-qir-runtime.ps1 => build-qir-stdlib.ps1} | 6 +++--- src/Qir/riqir/sim/Cargo.toml | 2 +- src/Qir/riqir/sim/src/conditionals.rs | 2 +- src/Qir/riqir/sim/src/lib.rs | 4 ++-- src/Qir/riqir/sim/src/result_bool.rs | 2 +- src/Qir/riqir/{runtime => stdlib}/Cargo.toml | 2 +- src/Qir/riqir/{runtime => stdlib}/build.rs | 0 .../include/qir_stdlib.def} | 0 .../QirRuntime.h => stdlib/include/qir_stdlib.h} | 0 src/Qir/riqir/{runtime => stdlib}/src/arrays.rs | 0 src/Qir/riqir/{runtime => stdlib}/src/bigints.rs | 0 .../riqir/{runtime => stdlib}/src/bridge-rt.ll | 0 .../riqir/{runtime => stdlib}/src/callables.rs | 0 src/Qir/riqir/{runtime => stdlib}/src/lib.rs | 0 src/Qir/riqir/{runtime => stdlib}/src/math.rs | 0 .../{runtime => stdlib}/src/output_recording.rs | 0 .../{runtime => stdlib}/src/range_support.rs | 0 src/Qir/riqir/{runtime => stdlib}/src/strings.rs | 0 src/Qir/riqir/{runtime => stdlib}/src/tuples.rs | 0 ...{test-qir-runtime.ps1 => test-qir-stdlib.ps1} | 2 +- src/Simulation/Native/src/CMakeLists.txt | 4 ++-- src/Simulation/Native/src/simulator/qir.hpp | 2 +- 26 files changed, 24 insertions(+), 24 deletions(-) rename src/Qir/riqir/{build-qir-runtime.ps1 => build-qir-stdlib.ps1} (90%) rename src/Qir/riqir/{runtime => stdlib}/Cargo.toml (92%) rename src/Qir/riqir/{runtime => stdlib}/build.rs (100%) rename src/Qir/riqir/{runtime/include/qir_runtime.def => stdlib/include/qir_stdlib.def} (100%) rename src/Qir/riqir/{runtime/include/QirRuntime.h => stdlib/include/qir_stdlib.h} (100%) rename src/Qir/riqir/{runtime => stdlib}/src/arrays.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/bigints.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/bridge-rt.ll (100%) rename src/Qir/riqir/{runtime => stdlib}/src/callables.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/lib.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/math.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/output_recording.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/range_support.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/strings.rs (100%) rename src/Qir/riqir/{runtime => stdlib}/src/tuples.rs (100%) rename src/Qir/riqir/{test-qir-runtime.ps1 => test-qir-stdlib.ps1} (96%) diff --git a/Cargo.toml b/Cargo.toml index e11b2e4deb7..f3d8aa14ef8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = [ "src/Simulation/qdk_sim_rs", - "src/Qir/riqir/runtime", + "src/Qir/riqir/stdlib", "src/Qir/riqir/sim", ] diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 302106c3be3..86063d483be 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -13,6 +13,14 @@ Push-Location (Join-Path $PSScriptRoot "./src/Simulation/qdk_sim_rs") Pop-Location if (-not (Test-Path Env:/AGENT_OS)) { # If not CI build, i.e. local build (if AGENT_OS envvar is not defined) + if ($Env:ENABLE_QIRRUNTIME -ne "false") { + Write-Host "Build release flavor of the QIR standard library" + $Env:BUILD_CONFIGURATION = "Release" + Push-Location (Join-Path $PSScriptRoot "src/Qir/riqir") + .\build-qir-stdlib.ps1 + Pop-Location + $Env:BUILD_CONFIGURATION = $null + } if ($Env:ENABLE_NATIVE -ne "false") { $Env:BUILD_CONFIGURATION = "Release" Write-Host "Build release flavor of the full state simulator" @@ -35,14 +43,6 @@ if (-not (Test-Path Env:/AGENT_OS)) { # If no Pop-Location $Env:BUILD_CONFIGURATION = $null } - if ($Env:ENABLE_QIRRUNTIME -ne "false") { - Write-Host "Build release flavor of the QIR Runtime" - $Env:BUILD_CONFIGURATION = "Release" - Push-Location (Join-Path $PSScriptRoot "src/Qir/Runtime") - .\build-qir-runtime.ps1 - Pop-Location - $Env:BUILD_CONFIGURATION = $null - } Write-Host "Build simulation solution" dotnet build Simulation.sln diff --git a/build/build.ps1 b/build/build.ps1 index b92d59aa480..5477434cd88 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -8,7 +8,7 @@ $all_ok = $True if ($Env:ENABLE_QIRRUNTIME -ne "false") { $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") - & "$qirRuntime/build-qir-runtime.ps1" + & "$qirRuntime/build-qir-stdlib.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } diff --git a/build/test.ps1 b/build/test.ps1 index 2f838a7b895..b1398aca751 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -50,7 +50,7 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { } $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") - & "$qirRuntime/test-qir-runtime.ps1" + & "$qirRuntime/test-qir-stdlib.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } diff --git a/src/Qir/riqir/build-qir-runtime.ps1 b/src/Qir/riqir/build-qir-stdlib.ps1 similarity index 90% rename from src/Qir/riqir/build-qir-runtime.ps1 rename to src/Qir/riqir/build-qir-stdlib.ps1 index 4e7a137038e..9934ec794dc 100644 --- a/src/Qir/riqir/build-qir-runtime.ps1 +++ b/src/Qir/riqir/build-qir-stdlib.ps1 @@ -7,7 +7,7 @@ $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; -foreach ($folder in "runtime","sim") { +foreach ($folder in "stdlib","sim") { Push-Location (Join-Path $PSScriptRoot $folder) try { # Start with the quick check first and make sure that Rust sources @@ -48,8 +48,8 @@ foreach ($folder in "runtime","sim") { Copy-Item $qirBinaries $qirDropsBin -Include "*qir_$folder*" -Exclude "*.rlib","*.d","*.exp" # Copy the C API header and def file - Copy-Item (Join-Path $PSScriptRoot runtime include QirRuntime.h) (Join-Path $Env:QIR_DROPS include) - Copy-Item (Join-Path $PSScriptRoot runtime include qir_runtime.def) (Join-Path $Env:QIR_DROPS include) + Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.h) (Join-Path $Env:QIR_DROPS include) + Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.def) (Join-Path $Env:QIR_DROPS include) # When building in CI, free disk space by cleaning up. # Note that this takes longer, but saves ~1 GB of space. diff --git a/src/Qir/riqir/sim/Cargo.toml b/src/Qir/riqir/sim/Cargo.toml index 5d9728306b8..b1226788b17 100644 --- a/src/Qir/riqir/sim/Cargo.toml +++ b/src/Qir/riqir/sim/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Microsoft"] edition = "2021" [dependencies] -qir-runtime = { path = "../runtime" } +qir-stdlib = { path = "../stdlib" } rand = "0.8.5" rustc-hash = "1.1.0" num-complex = "0.4" diff --git a/src/Qir/riqir/sim/src/conditionals.rs b/src/Qir/riqir/sim/src/conditionals.rs index 4ee11ae230d..ac88d058a9d 100644 --- a/src/Qir/riqir/sim/src/conditionals.rs +++ b/src/Qir/riqir/sim/src/conditionals.rs @@ -4,7 +4,7 @@ use super::result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one}; -use qir_runtime::{ +use qir_stdlib::{ __quantum__rt__fail, arrays::{QirArray, __quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, callables::{Callable, __quantum__rt__callable_invoke}, diff --git a/src/Qir/riqir/sim/src/lib.rs b/src/Qir/riqir/sim/src/lib.rs index 14c5e154889..9d58f7bbd9a 100644 --- a/src/Qir/riqir/sim/src/lib.rs +++ b/src/Qir/riqir/sim/src/lib.rs @@ -25,7 +25,7 @@ use result_bool::{ __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero, }; -pub use qir_runtime::{ +pub use qir_stdlib::{ arrays::*, bigints::*, callables::*, math::*, output_recording::*, range_support::*, strings::*, tuples::*, *, }; @@ -922,7 +922,7 @@ mod tests { __quantum__rt__qubit_release, __quantum__rt__qubit_release_array, __quantum__rt__result_equal, __quantum__rt__result_get_one, dump_state, }; - use qir_runtime::arrays::__quantum__rt__array_get_element_ptr_1d; + use qir_stdlib::arrays::__quantum__rt__array_get_element_ptr_1d; // TODO(swernli): Split and expand simulator unit tests. #[allow(clippy::cast_ptr_alignment)] diff --git a/src/Qir/riqir/sim/src/result_bool.rs b/src/Qir/riqir/sim/src/result_bool.rs index 6e6adb509cc..01a0cdc52eb 100644 --- a/src/Qir/riqir/sim/src/result_bool.rs +++ b/src/Qir/riqir/sim/src/result_bool.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. #![deny(clippy::all, clippy::pedantic)] -use qir_runtime::strings::__quantum__rt__string_create; +use qir_stdlib::strings::__quantum__rt__string_create; use std::ffi::{c_void, CString}; #[no_mangle] diff --git a/src/Qir/riqir/runtime/Cargo.toml b/src/Qir/riqir/stdlib/Cargo.toml similarity index 92% rename from src/Qir/riqir/runtime/Cargo.toml rename to src/Qir/riqir/stdlib/Cargo.toml index 14a140c65b1..b70613b2be5 100644 --- a/src/Qir/riqir/runtime/Cargo.toml +++ b/src/Qir/riqir/stdlib/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "qir-runtime" +name = "qir-stdlib" version = "0.1.0" authors = ["Microsoft"] edition = "2021" diff --git a/src/Qir/riqir/runtime/build.rs b/src/Qir/riqir/stdlib/build.rs similarity index 100% rename from src/Qir/riqir/runtime/build.rs rename to src/Qir/riqir/stdlib/build.rs diff --git a/src/Qir/riqir/runtime/include/qir_runtime.def b/src/Qir/riqir/stdlib/include/qir_stdlib.def similarity index 100% rename from src/Qir/riqir/runtime/include/qir_runtime.def rename to src/Qir/riqir/stdlib/include/qir_stdlib.def diff --git a/src/Qir/riqir/runtime/include/QirRuntime.h b/src/Qir/riqir/stdlib/include/qir_stdlib.h similarity index 100% rename from src/Qir/riqir/runtime/include/QirRuntime.h rename to src/Qir/riqir/stdlib/include/qir_stdlib.h diff --git a/src/Qir/riqir/runtime/src/arrays.rs b/src/Qir/riqir/stdlib/src/arrays.rs similarity index 100% rename from src/Qir/riqir/runtime/src/arrays.rs rename to src/Qir/riqir/stdlib/src/arrays.rs diff --git a/src/Qir/riqir/runtime/src/bigints.rs b/src/Qir/riqir/stdlib/src/bigints.rs similarity index 100% rename from src/Qir/riqir/runtime/src/bigints.rs rename to src/Qir/riqir/stdlib/src/bigints.rs diff --git a/src/Qir/riqir/runtime/src/bridge-rt.ll b/src/Qir/riqir/stdlib/src/bridge-rt.ll similarity index 100% rename from src/Qir/riqir/runtime/src/bridge-rt.ll rename to src/Qir/riqir/stdlib/src/bridge-rt.ll diff --git a/src/Qir/riqir/runtime/src/callables.rs b/src/Qir/riqir/stdlib/src/callables.rs similarity index 100% rename from src/Qir/riqir/runtime/src/callables.rs rename to src/Qir/riqir/stdlib/src/callables.rs diff --git a/src/Qir/riqir/runtime/src/lib.rs b/src/Qir/riqir/stdlib/src/lib.rs similarity index 100% rename from src/Qir/riqir/runtime/src/lib.rs rename to src/Qir/riqir/stdlib/src/lib.rs diff --git a/src/Qir/riqir/runtime/src/math.rs b/src/Qir/riqir/stdlib/src/math.rs similarity index 100% rename from src/Qir/riqir/runtime/src/math.rs rename to src/Qir/riqir/stdlib/src/math.rs diff --git a/src/Qir/riqir/runtime/src/output_recording.rs b/src/Qir/riqir/stdlib/src/output_recording.rs similarity index 100% rename from src/Qir/riqir/runtime/src/output_recording.rs rename to src/Qir/riqir/stdlib/src/output_recording.rs diff --git a/src/Qir/riqir/runtime/src/range_support.rs b/src/Qir/riqir/stdlib/src/range_support.rs similarity index 100% rename from src/Qir/riqir/runtime/src/range_support.rs rename to src/Qir/riqir/stdlib/src/range_support.rs diff --git a/src/Qir/riqir/runtime/src/strings.rs b/src/Qir/riqir/stdlib/src/strings.rs similarity index 100% rename from src/Qir/riqir/runtime/src/strings.rs rename to src/Qir/riqir/stdlib/src/strings.rs diff --git a/src/Qir/riqir/runtime/src/tuples.rs b/src/Qir/riqir/stdlib/src/tuples.rs similarity index 100% rename from src/Qir/riqir/runtime/src/tuples.rs rename to src/Qir/riqir/stdlib/src/tuples.rs diff --git a/src/Qir/riqir/test-qir-runtime.ps1 b/src/Qir/riqir/test-qir-stdlib.ps1 similarity index 96% rename from src/Qir/riqir/test-qir-runtime.ps1 rename to src/Qir/riqir/test-qir-stdlib.ps1 index d2ddbac5711..f74d8663aff 100644 --- a/src/Qir/riqir/test-qir-runtime.ps1 +++ b/src/Qir/riqir/test-qir-stdlib.ps1 @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -foreach ($folder in "runtime","sim") { +foreach ($folder in "stdlib","sim") { Push-Location (Join-Path $PSScriptRoot $folder) try { $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index cf7eadc9af7..e9fef2afd53 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -38,7 +38,7 @@ target_include_directories(Microsoft.Quantum.Simulator.Runtime PRIVATE "${PROJEC target_link_libraries(Microsoft.Quantum.Simulator.Runtime ${SPECTRE_LIBS} "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" - -lqir_runtime + -lqir_stdlib ) if (WIN32) @@ -47,7 +47,7 @@ if (WIN32) Bcrypt Userenv ) - target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_runtime.def") + target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") else() target_link_libraries(Microsoft.Quantum.Simulator.Runtime pthread diff --git a/src/Simulation/Native/src/simulator/qir.hpp b/src/Simulation/Native/src/simulator/qir.hpp index 9cf6302b91e..7bbf45d11cc 100644 --- a/src/Simulation/Native/src/simulator/qir.hpp +++ b/src/Simulation/Native/src/simulator/qir.hpp @@ -3,7 +3,7 @@ #pragma once -#include "QirRuntime.h" +#include "qir_stdlib.h" #include #ifdef _WIN32 From cd16687d44f83d2b2134525050953d8939bfc986 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 3 Sep 2022 00:45:14 -0700 Subject: [PATCH 21/55] rename sim to backend --- Cargo.toml | 2 +- src/Qir/riqir/{sim => backend}/Cargo.toml | 2 +- src/Qir/riqir/{sim => backend}/src/common_matrices.rs | 0 src/Qir/riqir/{sim => backend}/src/conditionals.rs | 0 src/Qir/riqir/{sim => backend}/src/lib.rs | 0 src/Qir/riqir/{sim => backend}/src/nearly_zero.rs | 0 src/Qir/riqir/{sim => backend}/src/result_bool.rs | 0 src/Qir/riqir/{sim => backend}/src/simulator.rs | 0 src/Qir/riqir/build-qir-stdlib.ps1 | 2 +- src/Qir/riqir/test-qir-stdlib.ps1 | 2 +- 10 files changed, 4 insertions(+), 4 deletions(-) rename src/Qir/riqir/{sim => backend}/Cargo.toml (95%) rename src/Qir/riqir/{sim => backend}/src/common_matrices.rs (100%) rename src/Qir/riqir/{sim => backend}/src/conditionals.rs (100%) rename src/Qir/riqir/{sim => backend}/src/lib.rs (100%) rename src/Qir/riqir/{sim => backend}/src/nearly_zero.rs (100%) rename src/Qir/riqir/{sim => backend}/src/result_bool.rs (100%) rename src/Qir/riqir/{sim => backend}/src/simulator.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index f3d8aa14ef8..b96c13d014b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "src/Simulation/qdk_sim_rs", "src/Qir/riqir/stdlib", - "src/Qir/riqir/sim", + "src/Qir/riqir/backend", ] [profile.release] diff --git a/src/Qir/riqir/sim/Cargo.toml b/src/Qir/riqir/backend/Cargo.toml similarity index 95% rename from src/Qir/riqir/sim/Cargo.toml rename to src/Qir/riqir/backend/Cargo.toml index b1226788b17..9a887cbb501 100644 --- a/src/Qir/riqir/sim/Cargo.toml +++ b/src/Qir/riqir/backend/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "qir-sim" +name = "qir-backend" version = "0.1.0" authors = ["Microsoft"] edition = "2021" diff --git a/src/Qir/riqir/sim/src/common_matrices.rs b/src/Qir/riqir/backend/src/common_matrices.rs similarity index 100% rename from src/Qir/riqir/sim/src/common_matrices.rs rename to src/Qir/riqir/backend/src/common_matrices.rs diff --git a/src/Qir/riqir/sim/src/conditionals.rs b/src/Qir/riqir/backend/src/conditionals.rs similarity index 100% rename from src/Qir/riqir/sim/src/conditionals.rs rename to src/Qir/riqir/backend/src/conditionals.rs diff --git a/src/Qir/riqir/sim/src/lib.rs b/src/Qir/riqir/backend/src/lib.rs similarity index 100% rename from src/Qir/riqir/sim/src/lib.rs rename to src/Qir/riqir/backend/src/lib.rs diff --git a/src/Qir/riqir/sim/src/nearly_zero.rs b/src/Qir/riqir/backend/src/nearly_zero.rs similarity index 100% rename from src/Qir/riqir/sim/src/nearly_zero.rs rename to src/Qir/riqir/backend/src/nearly_zero.rs diff --git a/src/Qir/riqir/sim/src/result_bool.rs b/src/Qir/riqir/backend/src/result_bool.rs similarity index 100% rename from src/Qir/riqir/sim/src/result_bool.rs rename to src/Qir/riqir/backend/src/result_bool.rs diff --git a/src/Qir/riqir/sim/src/simulator.rs b/src/Qir/riqir/backend/src/simulator.rs similarity index 100% rename from src/Qir/riqir/sim/src/simulator.rs rename to src/Qir/riqir/backend/src/simulator.rs diff --git a/src/Qir/riqir/build-qir-stdlib.ps1 b/src/Qir/riqir/build-qir-stdlib.ps1 index 9934ec794dc..f58a7572115 100644 --- a/src/Qir/riqir/build-qir-stdlib.ps1 +++ b/src/Qir/riqir/build-qir-stdlib.ps1 @@ -7,7 +7,7 @@ $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; -foreach ($folder in "stdlib","sim") { +foreach ($folder in "stdlib","backend") { Push-Location (Join-Path $PSScriptRoot $folder) try { # Start with the quick check first and make sure that Rust sources diff --git a/src/Qir/riqir/test-qir-stdlib.ps1 b/src/Qir/riqir/test-qir-stdlib.ps1 index f74d8663aff..c2238361489 100644 --- a/src/Qir/riqir/test-qir-stdlib.ps1 +++ b/src/Qir/riqir/test-qir-stdlib.ps1 @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -foreach ($folder in "stdlib","sim") { +foreach ($folder in "stdlib","backend") { Push-Location (Join-Path $PSScriptRoot $folder) try { $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); From de289c0c5b3a241f5234b343c64c0a477178f394 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 3 Sep 2022 00:54:22 -0700 Subject: [PATCH 22/55] remove conditionals support --- src/Qir/riqir/backend/src/conditionals.rs | 72 ----------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/Qir/riqir/backend/src/conditionals.rs diff --git a/src/Qir/riqir/backend/src/conditionals.rs b/src/Qir/riqir/backend/src/conditionals.rs deleted file mode 100644 index ac88d058a9d..00000000000 --- a/src/Qir/riqir/backend/src/conditionals.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] - -use super::result_bool::{__quantum__rt__result_equal, __quantum__rt__result_get_one}; - -use qir_stdlib::{ - __quantum__rt__fail, - arrays::{QirArray, __quantum__rt__array_get_element_ptr_1d, __quantum__rt__array_get_size_1d}, - callables::{Callable, __quantum__rt__callable_invoke}, - strings::__quantum__rt__string_create, -}; -use std::{ - ffi::{c_void, CString}, - ptr::null_mut, -}; - -/// # Safety -/// -/// This function should only be called with results and callables generated by this runtime. -#[no_mangle] -pub unsafe extern "C" fn __quantum__qis__applyifelseintrinsic__body( - result: *mut c_void, - true_callable: *const Callable, - false_callable: *const Callable, -) { - __quantum__rt__callable_invoke( - if __quantum__rt__result_equal(result, __quantum__rt__result_get_one()) { - true_callable - } else { - false_callable - }, - null_mut(), - null_mut(), - ); -} - -/// # Safety -/// -/// This function should only be called with arrays of results and callables generated by this runtime. -#[no_mangle] -#[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn __quantum__qis__applyconditionallyintrinsic__body( - results: *const QirArray, - expected: *const QirArray, - true_callable: *const Callable, - false_callable: *const Callable, -) { - let result_len = __quantum__rt__array_get_size_1d(results); - if result_len != __quantum__rt__array_get_size_1d(expected) { - __quantum__rt__fail(__quantum__rt__string_create( - CString::new( - "Invalid Argument: expected and actual result arrays must have the same size.", - ) - .expect("Unable to allocate string for error message.") - .as_bytes_with_nul() - .as_ptr() as *mut i8, - )); - } - - for index in 0..result_len { - let res = *__quantum__rt__array_get_element_ptr_1d(results, index).cast::<*mut c_void>(); - let expect = - *__quantum__rt__array_get_element_ptr_1d(expected, index).cast::<*mut c_void>(); - if !__quantum__rt__result_equal(res, expect) { - // We've found a mismatch, so invoke false and early return. - return __quantum__rt__callable_invoke(false_callable, null_mut(), null_mut()); - } - } - - __quantum__rt__callable_invoke(true_callable, null_mut(), null_mut()); -} From 781c0969e205b7b84ec914eca11feaaeab798e84 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Sat, 3 Sep 2022 00:56:29 -0700 Subject: [PATCH 23/55] Fix public module ref --- src/Qir/riqir/backend/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Qir/riqir/backend/src/lib.rs b/src/Qir/riqir/backend/src/lib.rs index 9d58f7bbd9a..29c3bb42827 100644 --- a/src/Qir/riqir/backend/src/lib.rs +++ b/src/Qir/riqir/backend/src/lib.rs @@ -4,7 +4,6 @@ //! Module defining QIR compliant APIs for quantum simulation. -pub mod conditionals; pub mod result_bool; mod common_matrices; From c329e582fabfbfa422ec59b798486dddc3637c82 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 5 Sep 2022 23:42:43 -0700 Subject: [PATCH 24/55] Update cmdline tool to use simulator QIR backend --- src/Qir/Samples/CMakeLists.txt | 8 +- .../StandaloneInputReference/CMakeLists.txt | 7 +- .../StandaloneInputReference/qir-driver.cpp | 50 +-- .../Tests/Tools/QirDriverGeneratorTests.cs | 41 ++- .../FullStateDriverGenerator/UseBoolArg.cpp | 32 +- .../UseBoolArrayArg.cpp | 32 +- .../FullStateDriverGenerator/UseDoubleArg.cpp | 32 +- .../UseDoubleArrayArg.cpp | 32 +- .../UseIntegerArg.cpp | 32 +- .../UseIntegerArrayArg.cpp | 32 +- .../FullStateDriverGenerator/UseMiscArgs.cpp | 32 +- .../FullStateDriverGenerator/UseNoArgs.cpp | 32 +- .../UseNoArgsDebug.cpp | 67 ---- .../FullStateDriverGenerator/UsePauliArg.cpp | 53 +-- .../UsePauliArrayArg.cpp | 53 +-- .../FullStateDriverGenerator/UseRangeArg.cpp | 32 +- .../UseRangeArrayArg.cpp | 32 +- .../FullStateDriverGenerator/UseResultArg.cpp | 32 +- .../UseResultArrayArg.cpp | 32 +- .../FullStateDriverGenerator/UseStringArg.cpp | 32 +- ...Microsoft.Quantum.Qir.Runtime.Tools.csproj | 3 - src/Qir/Tools/Driver/QirCppDriver.cs | 39 +-- src/Qir/Tools/Driver/QirCppDriver.tt | 41 +-- .../QirCppFullStateSimulatorInitializer.cs | 303 ------------------ .../QirCppFullStateSimulatorInitializer.tt | 3 - .../QirCppFullStateSimulatorInitializerEx.cs | 9 - src/Qir/Tools/Driver/QirCppInterop.cs | 6 +- .../Driver/QirFullStateDriverGenerator.cs | 4 +- .../QirFullStateSimulatorInitializer.cs | 10 +- .../Executable/QirFullStateExecutable.cs | 8 +- ...Microsoft.Quantum.Qir.Runtime.Tools.csproj | 9 - src/Qir/Tools/QirTools.cs | 4 +- 32 files changed, 88 insertions(+), 1046 deletions(-) delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp delete mode 100644 src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs delete mode 100644 src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt delete mode 100644 src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs diff --git a/src/Qir/Samples/CMakeLists.txt b/src/Qir/Samples/CMakeLists.txt index 7854f614a00..037cb639641 100644 --- a/src/Qir/Samples/CMakeLists.txt +++ b/src/Qir/Samples/CMakeLists.txt @@ -18,11 +18,15 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") -set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") -set(runtime_lib_path "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") +# set the environment path for loading shared libs the tests are using +if(DEFINED ENV{NATIVE_SIMULATOR}) + set(simulator_lib_path $ENV{NATIVE_SIMULATOR}) +else() + set(simulator_lib_path "${PROJECT_SOURCE_DIR}/../../Simulation/Native/build/drop") +endif() include(qir_cmake_include) diff --git a/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt index 8006d3a5694..a162dc8c2f9 100644 --- a/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt +++ b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt @@ -9,17 +9,14 @@ add_executable(qir-input-reference-standalone target_source_from_qir(qir-input-reference-standalone qsharp/obj/qsharp/qir-standalone-input-reference.bc) target_link_libraries(qir-input-reference-standalone PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) set(standalone_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/CLI11") target_include_directories(qir-input-reference-standalone PUBLIC "${standalone_includes}" - "${public_includes}" ) install(TARGETS qir-input-reference-standalone RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") diff --git a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp index 71c224bcbc5..36253d57e4c 100644 --- a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp +++ b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp @@ -15,11 +15,6 @@ #pragma clang diagnostic pop // clang-format on -#include "QirContext.hpp" -#include "SimFactory.hpp" -#include "OutputStream.hpp" - -using namespace Microsoft::Quantum; using namespace std; namespace // == `static` @@ -68,10 +63,7 @@ static map BoolAsCharMap{{"0", InteropFalseAsChar}, {"1", InteropTrueAsChar}, {"true", InteropTrueAsChar}}; -static map PauliMap{{"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z}}; +static map PauliMap{{"PauliI", 0}, {"PauliX", 1}, {"PauliY", 3}, {"PauliZ", 2}}; static const char InteropResultZeroAsChar = 0x0; static const char InteropResultOneAsChar = 0x1; @@ -102,11 +94,6 @@ void FreePointerVector(vector& v) } } -static char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} - template void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) { @@ -130,15 +117,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point Inputs Reference"); // Initialize simulator. - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output options. - // N.B. This option should be present in all standalone drivers. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = - app.add_option("-s,--simulation-output", simulationOutputFile, - "File where the output produced during the simulation is written"); // Add the options that correspond to the parameters that the QIR entry-point needs. // Option for a Q# Int type. @@ -172,13 +150,13 @@ int main(int argc, char* argv[]) ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); // Option for Q# Pauli type. - PauliId pauliValue = PauliId::PauliId_I; + char pauliValue = 0; app.add_option("--pauli-value", pauliValue, "A Pauli value") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); // Option for a Q# Array type. - std::vector pauliVector; + std::vector pauliVector; app.add_option("--pauli-array", pauliVector, "A Pauli array") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); @@ -232,12 +210,10 @@ int main(int argc, char* argv[]) unique_ptr boolArray = CreateInteropArray(boolAsCharVector); // Translate a PauliID value to its char representation. - char pauliAsCharValue = TranslatePauliToChar(pauliValue); + char pauliAsCharValue = pauliValue; // Create an interop array of Pauli values represented as chars. - vector pauliAsCharVector; - TranslateVector(pauliVector, pauliAsCharVector, TranslatePauliToChar); - unique_ptr pauliArray = CreateInteropArray(pauliAsCharVector); + unique_ptr pauliArray = CreateInteropArray(pauliVector); // Create an interop range. unique_ptr rangeValue = CreateInteropRange(rangeTuple); @@ -253,16 +229,6 @@ int main(int argc, char* argv[]) TranslateVector(stringVector, stringBufferVector, TranslateStringToCharBuffer); unique_ptr stringArray = CreateInteropArray(stringBufferVector); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - Microsoft::Quantum::OutputStream::Set(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Run simulation and write the output of the operation to the corresponding stream. Quantum__StandaloneSupportedInputs__ExerciseInputs(intValue, integerArray.get(), doubleValue, doubleArray.get(), boolAsCharValue, boolArray.get(), pauliAsCharValue, @@ -270,9 +236,5 @@ int main(int argc, char* argv[]) resultArray.get(), stringValue.c_str()); FreePointerVector(rangeVector); - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); } diff --git a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs index 395877bcf61..ea65c7069e5 100644 --- a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs +++ b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs @@ -23,10 +23,6 @@ public class QirDriverGeneratorTests "UseNoArgs", new EntryPointOperation{Name = "UseNoArgs"} }, - { - "UseNoArgsDebug", - new EntryPointOperation{Name = "UseNoArgsDebug"} - }, { "UseBoolArg", new EntryPointOperation @@ -150,26 +146,25 @@ private static string RemoveLineEndings(string str) => str.Replace("\n", string.Empty).Replace("\r", string.Empty); [Theory] - [InlineData("UseNoArgs", false)] - [InlineData("UseNoArgsDebug", true)] - [InlineData("UseBoolArg", false)] - [InlineData("UseBoolArrayArg", false)] - [InlineData("UseDoubleArg", false)] - [InlineData("UseDoubleArrayArg", false)] - [InlineData("UseIntegerArg", false)] - [InlineData("UseIntegerArrayArg", false)] - [InlineData("UsePauliArg", false)] - [InlineData("UsePauliArrayArg", false)] - [InlineData("UseRangeArg", false)] - [InlineData("UseRangeArrayArg", false)] - [InlineData("UseResultArg", false)] - [InlineData("UseResultArrayArg", false)] - [InlineData("UseStringArg", false)] - [InlineData("UseMiscArgs", false)] - public void GenerateFullStateSimulatorDriver(string testCase, bool debug) + [InlineData("UseNoArgs")] + [InlineData("UseBoolArg")] + [InlineData("UseBoolArrayArg")] + [InlineData("UseDoubleArg")] + [InlineData("UseDoubleArrayArg")] + [InlineData("UseIntegerArg")] + [InlineData("UseIntegerArrayArg")] + [InlineData("UsePauliArg")] + [InlineData("UsePauliArrayArg")] + [InlineData("UseRangeArg")] + [InlineData("UseRangeArrayArg")] + [InlineData("UseResultArg")] + [InlineData("UseResultArrayArg")] + [InlineData("UseStringArg")] + [InlineData("UseMiscArgs")] + public void GenerateFullStateSimulatorDriver(string testCase) { var entryPointOperation = TestCases[testCase]; - var driverGenerator = new QirFullStateDriverGenerator(debug); + var driverGenerator = new QirFullStateDriverGenerator(); var driverFileName = $"{testCase}.cpp"; var verificationCppSourceCode = RemoveLineEndings(File.ReadAllText(Path.Combine(TestCasesDirectory, driverFileName))); Directory.CreateDirectory(TestArtifactsDirectory); @@ -185,7 +180,7 @@ public void GenerateFullStateSimulatorDriver(string testCase, bool debug) [MemberData(nameof(CommandLineArgumentsData))] public void GenerateCommandLineArguments(string expected, ExecutionInformation info) { - var generator = new QirFullStateDriverGenerator(false); + var generator = new QirFullStateDriverGenerator(); Assert.Equal(expected, generator.GetCommandLineArguments(info)); } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp index aa3513a5208..1855912aa69 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Bool type. @@ -40,16 +34,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. char BoolArgCli; BoolArgCli = InteropFalseAsChar; @@ -63,27 +47,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. char BoolArgInterop = BoolArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseBoolArg( BoolArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp index 9984a990f80..5bfbc30196b 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -65,16 +59,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector BoolArrayArgCli; app.add_option("--BoolArrayArg", BoolArrayArgCli, "Option to provide a value for the BoolArrayArg parameter") @@ -88,27 +72,13 @@ int main(int argc, char* argv[]) unique_ptr BoolArrayArgUniquePtr = CreateInteropArray(BoolArrayArgCli); InteropArray* BoolArrayArgInterop = BoolArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseBoolArrayArg( BoolArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp index 4f906afb33b..6eefd32abbe 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; extern "C" void UseDoubleArg( @@ -30,16 +24,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. double_t DoubleArgCli; DoubleArgCli = 0.0; @@ -52,27 +36,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. double_t DoubleArgInterop = DoubleArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseDoubleArg( DoubleArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp index 6c764126b06..d6593923e71 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -55,16 +49,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector DoubleArrayArgCli; app.add_option("--DoubleArrayArg", DoubleArrayArgCli, "Option to provide a value for the DoubleArrayArg parameter") @@ -77,27 +61,13 @@ int main(int argc, char* argv[]) unique_ptr DoubleArrayArgUniquePtr = CreateInteropArray(DoubleArrayArgCli); InteropArray* DoubleArrayArgInterop = DoubleArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseDoubleArrayArg( DoubleArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp index 1f95ee9bdf4..d6e6ebfd3f1 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; extern "C" void UseIntegerArg( @@ -30,16 +24,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. int64_t IntegerArgCli; IntegerArgCli = 0; @@ -52,27 +36,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. int64_t IntegerArgInterop = IntegerArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseIntegerArg( IntegerArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp index dc7fc6ae65c..73ef2abbbe6 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -55,16 +49,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector IntegerArrayArgCli; app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") @@ -77,27 +61,13 @@ int main(int argc, char* argv[]) unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseIntegerArrayArg( IntegerArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp index 301dbec64a5..c43b4506176 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -105,16 +99,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. char BoolArgCli; BoolArgCli = InteropFalseAsChar; @@ -147,16 +131,6 @@ int main(int argc, char* argv[]) const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseMiscArgs( BoolArgInterop, @@ -166,11 +140,7 @@ int main(int argc, char* argv[]) ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp index cfece3ca337..56061656927 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; extern "C" void UseNoArgs( @@ -29,39 +23,15 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // After all the options have been added, parse arguments from the command line. CLI11_PARSE(app, argc, argv); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseNoArgs( ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp deleted file mode 100644 index 847066fd763..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgsDebug.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; -using namespace std; - -extern "C" void UseNoArgsDebug( -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), true /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - - // Execute the entry point operation. - UseNoArgsDebug( - ); - - // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp index 16804e3eb26..cb923423b76 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp @@ -13,27 +13,16 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Pauli type. -map PauliMap{ - {"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z} +map PauliMap{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} }; -char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} - extern "C" void UsePauliArg( char PauliArg ); // QIR interop function. @@ -43,19 +32,9 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. - PauliId PauliArgCli; - PauliArgCli = PauliId::PauliId_I; + char PauliArgCli; + PauliArgCli = 0; app.add_option("--PauliArg", PauliArgCli, "Option to provide a value for the PauliArg parameter") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); @@ -64,17 +43,7 @@ int main(int argc, char* argv[]) CLI11_PARSE(app, argc, argv); // Cast parsed arguments to its interop types. - char PauliArgInterop = TranslatePauliToChar(PauliArgCli); - - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } + char PauliArgInterop = PauliArgCli; // Execute the entry point operation. UsePauliArg( @@ -82,11 +51,7 @@ int main(int argc, char* argv[]) ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp index c889fd73493..c754efbb865 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -47,18 +41,13 @@ void TranslateVector(vector& sourceVector, vector& destinationVector, func } // Auxiliary functions for interop with Q# Pauli type. -map PauliMap{ - {"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z} +map PauliMap{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} }; -char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} - extern "C" void UsePauliArrayArg( InteropArray* PauliArrayArg ); // QIR interop function. @@ -68,18 +57,8 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. - vector PauliArrayArgCli; + vector PauliArrayArgCli; app.add_option("--PauliArrayArg", PauliArrayArgCli, "Option to provide a value for the PauliArrayArg parameter") ->required() ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); @@ -88,32 +67,16 @@ int main(int argc, char* argv[]) CLI11_PARSE(app, argc, argv); // Cast parsed arguments to its interop types. - vector PauliArrayArgIntermediate; - TranslateVector(PauliArrayArgCli, PauliArrayArgIntermediate, TranslatePauliToChar); - unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgIntermediate); + unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgCli); InteropArray* PauliArrayArgInterop = PauliArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UsePauliArrayArg( PauliArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp index 8226efa86ed..e05012edb4a 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Range type. @@ -61,16 +55,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. RangeTuple RangeArgCli; app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") @@ -82,27 +66,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseRangeArg( RangeArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp index df879070ec8..2ea36be72c2 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -96,16 +90,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector RangeArrayArgCli; app.add_option("--RangeArrayArg", RangeArrayArgCli, "Option to provide a value for the RangeArrayArg parameter") @@ -120,27 +104,13 @@ int main(int argc, char* argv[]) unique_ptr RangeArrayArgUniquePtr = CreateInteropArray(RangeArrayArgIntermediate); InteropArray* RangeArrayArgInterop = RangeArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseRangeArrayArg( RangeArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp index 383eb03d9e1..c3757bff52f 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Result type. @@ -40,16 +34,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. char ResultArgCli; ResultArgCli = InteropResultZeroAsChar; @@ -63,27 +47,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. char ResultArgInterop = ResultArgCli; - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseResultArg( ResultArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp index ba5b8f426e8..1bc6992c838 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# Array type. @@ -65,16 +59,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. vector ResultArrayArgCli; app.add_option("--ResultArrayArg", ResultArrayArgCli, "Option to provide a value for the ResultArrayArg parameter") @@ -88,27 +72,13 @@ int main(int argc, char* argv[]) unique_ptr ResultArrayArgUniquePtr = CreateInteropArray(ResultArrayArgCli); InteropArray* ResultArrayArgInterop = ResultArrayArgUniquePtr.get(); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseResultArrayArg( ResultArrayArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp index c7400f6c53a..a1908a9c045 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp @@ -13,12 +13,6 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; using namespace std; // Auxiliary functions for interop with Q# String type. @@ -36,16 +30,6 @@ int main(int argc, char* argv[]) CLI::App app("QIR Standalone Entry Point"); // Initialize runtime. - unique_ptr sim = CreateFullstateSimulator(); - QirContextScope qirctx(sim.get(), false /*trackAllocatedObjects*/); - - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - // Add a command line option for each entry-point parameter. string StringArgCli; app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") @@ -57,27 +41,13 @@ int main(int argc, char* argv[]) // Cast parsed arguments to its interop types. const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. UseStringArg( StringArgInterop ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj index b920292cdd9..f74161bb917 100644 --- a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj +++ b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj @@ -64,9 +64,6 @@ Always - - Always - diff --git a/src/Qir/Tools/Driver/QirCppDriver.cs b/src/Qir/Tools/Driver/QirCppDriver.cs index dfa267f783e..726dd833ca9 100644 --- a/src/Qir/Tools/Driver/QirCppDriver.cs +++ b/src/Qir/Tools/Driver/QirCppDriver.cs @@ -39,16 +39,13 @@ public virtual string TransformText() #include ""CLI11.hpp"" -#include ""QirRuntime.hpp"" -#include ""QirContext.hpp"" - "); foreach (var header in RuntimeInitializer.Headers) { this.Write("#include \""); this.Write(this.ToStringHelper.ToStringWithCulture(header)); this.Write("\"\r\n"); } - this.Write("\r\nusing namespace Microsoft::Quantum;\r\nusing namespace std;\r\n"); + this.Write("\r\nusing namespace std;\r\n"); if (EntryPoint.ContainsArgumentType(DataType.ArrayType)) { this.Write(@" // Auxiliary functions for interop with Q# Array type. @@ -124,12 +121,10 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) "InteropTrueAsChar},\r\n {\"true\", InteropTrueAsChar}\r\n};\r\n"); } if (EntryPoint.ContainsArgumentType(DataType.PauliType) || EntryPoint.ContainsArrayType(DataType.PauliType)) { - this.Write("\r\n// Auxiliary functions for interop with Q# Pauli type.\r\nmap "); + this.Write("\r\n// Auxiliary functions for interop with Q# Pauli type.\r\nmap "); this.Write(this.ToStringHelper.ToStringWithCulture(QirCppInterop.CliOptionTransformerMapName(DataType.PauliType))); - this.Write("{\r\n {\"PauliI\", PauliId::PauliId_I},\r\n {\"PauliX\", PauliId::PauliId_X},\r\n " + - "{\"PauliY\", PauliId::PauliId_Y},\r\n {\"PauliZ\", PauliId::PauliId_Z}\r\n};\r\n\r\nchar " + - "TranslatePauliToChar(PauliId& pauli)\r\n{\r\n return static_cast(pauli);\r\n}" + - "\r\n"); + this.Write("{\r\n {\"PauliI\", 0},\r\n {\"PauliX\", 1},\r\n " + + "{\"PauliY\", 3},\r\n {\"PauliZ\", 2}\r\n};\r\n\r\n"); } if (EntryPoint.ContainsArgumentType(DataType.ResultType) || EntryPoint.ContainsArrayType(DataType.ResultType)) { this.Write("\r\n// Auxiliary functions for interop with Q# Result type.\r\nconst char InteropResu" + @@ -165,15 +160,6 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) this.Write(this.ToStringHelper.ToStringWithCulture(line)); this.Write("\r\n"); } - this.Write(@" - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - ""--simulation-output"", - simulationOutputFile, - ""File where the output produced during the simulation is written""); - -"); if (EntryPoint.Parameters.Count > 0) { this.Write(" // Add a command line option for each entry-point parameter.\r\n"); } @@ -275,17 +261,7 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) } this.Write("\r\n"); } - this.Write(@" // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - - // Execute the entry point operation. + this.Write(@" // Execute the entry point operation. "); this.Write(this.ToStringHelper.ToStringWithCulture(EntryPoint.Name)); this.Write("(\r\n"); @@ -297,9 +273,8 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) this.Write(this.ToStringHelper.ToStringWithCulture((isLastArg) ? "" : ",")); this.Write("\r\n"); } - this.Write(" );\r\n\r\n // Flush the output of the simulation.\r\n simulatorOutputStream->" + - "flush();\r\n if (simulationOutputFileStream.is_open())\r\n {\r\n simulati" + - "onOutputFileStream.close();\r\n }\r\n\r\n return 0;\r\n}\r\n"); + this.Write(" );\r\n\r\n // Flush the output of the simulation.\r\n cout." + + "flush();\r\n\r\n return 0;\r\n}\r\n"); return this.GenerationEnvironment.ToString(); } } diff --git a/src/Qir/Tools/Driver/QirCppDriver.tt b/src/Qir/Tools/Driver/QirCppDriver.tt index 475a06ec943..01aa8b9ebe5 100644 --- a/src/Qir/Tools/Driver/QirCppDriver.tt +++ b/src/Qir/Tools/Driver/QirCppDriver.tt @@ -16,14 +16,10 @@ #include "CLI11.hpp" -#include "QirRuntime.hpp" -#include "QirContext.hpp" - <# foreach (var header in RuntimeInitializer.Headers) { #> #include "<#= header #>" <# } #> -using namespace Microsoft::Quantum; using namespace std; <# if (EntryPoint.ContainsArgumentType(DataType.ArrayType)) { #> @@ -112,17 +108,13 @@ map <#= QirCppInterop.CliOptionTransformerMapName(DataType.BoolTyp <# if (EntryPoint.ContainsArgumentType(DataType.PauliType) || EntryPoint.ContainsArrayType(DataType.PauliType)) { #> // Auxiliary functions for interop with Q# Pauli type. -map <#= QirCppInterop.CliOptionTransformerMapName(DataType.PauliType) #>{ - {"PauliI", PauliId::PauliId_I}, - {"PauliX", PauliId::PauliId_X}, - {"PauliY", PauliId::PauliId_Y}, - {"PauliZ", PauliId::PauliId_Z} +map <#= QirCppInterop.CliOptionTransformerMapName(DataType.PauliType) #>{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} }; -char TranslatePauliToChar(PauliId& pauli) -{ - return static_cast(pauli); -} <# } #> <# if (EntryPoint.ContainsArgumentType(DataType.ResultType) || EntryPoint.ContainsArrayType(DataType.ResultType)) { #> @@ -164,13 +156,6 @@ int main(int argc, char* argv[]) <#= line #> <# } #> - // Add the --simulation-output option. - string simulationOutputFile; - CLI::Option* simulationOutputFileOpt = app.add_option( - "--simulation-output", - simulationOutputFile, - "File where the output produced during the simulation is written"); - <# if (EntryPoint.Parameters.Count > 0) { #> // Add a command line option for each entry-point parameter. <# } #> @@ -218,16 +203,6 @@ int main(int argc, char* argv[]) <# } #> <# } #> - // Redirect the simulator output from std::cout if the --simulation-output option is present. - ostream* simulatorOutputStream = &cout; - ofstream simulationOutputFileStream; - if (!simulationOutputFileOpt->empty()) - { - simulationOutputFileStream.open(simulationOutputFile); - SetOutputStream(simulationOutputFileStream); - simulatorOutputStream = &simulationOutputFileStream; - } - // Execute the entry point operation. <#= EntryPoint.Name #>( <# for (int i = 0; i < EntryPoint.Parameters.Count; i++) { @@ -238,11 +213,7 @@ int main(int argc, char* argv[]) ); // Flush the output of the simulation. - simulatorOutputStream->flush(); - if (simulationOutputFileStream.is_open()) - { - simulationOutputFileStream.close(); - } + cout.flush(); return 0; } diff --git a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs b/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs deleted file mode 100644 index eaa6e5d4cf5..00000000000 --- a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.cs +++ /dev/null @@ -1,303 +0,0 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version: 16.0.0.0 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ -namespace Microsoft.Quantum.Qir.Runtime.Tools.Driver -{ - using System; - - /// - /// Class to produce the template output - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - public partial class QirCppFullStateSimulatorInitializer : QirCppFullStateSimulatorInitializerBase - { - /// - /// Create the template output - /// - public virtual string TransformText() - { - this.Write("unique_ptr sim = CreateFullstateSimulator();\r\nQirContextScope qirctx(sim.get(), "); - this.Write(this.ToStringHelper.ToStringWithCulture(this.debug ? "true" : "false")); - this.Write(" /*trackAllocatedObjects*/);"); - return this.GenerationEnvironment.ToString(); - } - } - #region Base class - /// - /// Base class for this transformation - /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "16.0.0.0")] - public class QirCppFullStateSimulatorInitializerBase - { - #region Fields - private global::System.Text.StringBuilder generationEnvironmentField; - private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; - private global::System.Collections.Generic.List indentLengthsField; - private string currentIndentField = ""; - private bool endsWithNewline; - private global::System.Collections.Generic.IDictionary sessionField; - #endregion - #region Properties - /// - /// The string builder that generation-time code is using to assemble generated output - /// - protected System.Text.StringBuilder GenerationEnvironment - { - get - { - if ((this.generationEnvironmentField == null)) - { - this.generationEnvironmentField = new global::System.Text.StringBuilder(); - } - return this.generationEnvironmentField; - } - set - { - this.generationEnvironmentField = value; - } - } - /// - /// The error collection for the generation process - /// - public System.CodeDom.Compiler.CompilerErrorCollection Errors - { - get - { - if ((this.errorsField == null)) - { - this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); - } - return this.errorsField; - } - } - /// - /// A list of the lengths of each indent that was added with PushIndent - /// - private System.Collections.Generic.List indentLengths - { - get - { - if ((this.indentLengthsField == null)) - { - this.indentLengthsField = new global::System.Collections.Generic.List(); - } - return this.indentLengthsField; - } - } - /// - /// Gets the current indent we use when adding lines to the output - /// - public string CurrentIndent - { - get - { - return this.currentIndentField; - } - } - /// - /// Current transformation session - /// - public virtual global::System.Collections.Generic.IDictionary Session - { - get - { - return this.sessionField; - } - set - { - this.sessionField = value; - } - } - #endregion - #region Transform-time helpers - /// - /// Write text directly into the generated output - /// - public void Write(string textToAppend) - { - if (string.IsNullOrEmpty(textToAppend)) - { - return; - } - // If we're starting off, or if the previous text ended with a newline, - // we have to append the current indent first. - if (((this.GenerationEnvironment.Length == 0) - || this.endsWithNewline)) - { - this.GenerationEnvironment.Append(this.currentIndentField); - this.endsWithNewline = false; - } - // Check if the current text ends with a newline - if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) - { - this.endsWithNewline = true; - } - // This is an optimization. If the current indent is "", then we don't have to do any - // of the more complex stuff further down. - if ((this.currentIndentField.Length == 0)) - { - this.GenerationEnvironment.Append(textToAppend); - return; - } - // Everywhere there is a newline in the text, add an indent after it - textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); - // If the text ends with a newline, then we should strip off the indent added at the very end - // because the appropriate indent will be added when the next time Write() is called - if (this.endsWithNewline) - { - this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); - } - else - { - this.GenerationEnvironment.Append(textToAppend); - } - } - /// - /// Write text directly into the generated output - /// - public void WriteLine(string textToAppend) - { - this.Write(textToAppend); - this.GenerationEnvironment.AppendLine(); - this.endsWithNewline = true; - } - /// - /// Write formatted text directly into the generated output - /// - public void Write(string format, params object[] args) - { - this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Write formatted text directly into the generated output - /// - public void WriteLine(string format, params object[] args) - { - this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); - } - /// - /// Raise an error - /// - public void Error(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - this.Errors.Add(error); - } - /// - /// Raise a warning - /// - public void Warning(string message) - { - System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); - error.ErrorText = message; - error.IsWarning = true; - this.Errors.Add(error); - } - /// - /// Increase the indent - /// - public void PushIndent(string indent) - { - if ((indent == null)) - { - throw new global::System.ArgumentNullException("indent"); - } - this.currentIndentField = (this.currentIndentField + indent); - this.indentLengths.Add(indent.Length); - } - /// - /// Remove the last indent that was added with PushIndent - /// - public string PopIndent() - { - string returnValue = ""; - if ((this.indentLengths.Count > 0)) - { - int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; - this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); - if ((indentLength > 0)) - { - returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); - this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); - } - } - return returnValue; - } - /// - /// Remove any indentation - /// - public void ClearIndent() - { - this.indentLengths.Clear(); - this.currentIndentField = ""; - } - #endregion - #region ToString Helpers - /// - /// Utility class to produce culture-oriented representation of an object as a string. - /// - public class ToStringInstanceHelper - { - private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; - /// - /// Gets or sets format provider to be used by ToStringWithCulture method. - /// - public System.IFormatProvider FormatProvider - { - get - { - return this.formatProviderField ; - } - set - { - if ((value != null)) - { - this.formatProviderField = value; - } - } - } - /// - /// This is called from the compile/run appdomain to convert objects within an expression block to a string - /// - public string ToStringWithCulture(object objectToConvert) - { - if ((objectToConvert == null)) - { - throw new global::System.ArgumentNullException("objectToConvert"); - } - System.Type t = objectToConvert.GetType(); - System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { - typeof(System.IFormatProvider)}); - if ((method == null)) - { - return objectToConvert.ToString(); - } - else - { - return ((string)(method.Invoke(objectToConvert, new object[] { - this.formatProviderField }))); - } - } - } - private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); - /// - /// Helper to produce culture-oriented representation of an object as a string - /// - public ToStringInstanceHelper ToStringHelper - { - get - { - return this.toStringHelperField; - } - } - #endregion - } - #endregion -} diff --git a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt b/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt deleted file mode 100644 index 9b9eb0f4457..00000000000 --- a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializer.tt +++ /dev/null @@ -1,3 +0,0 @@ -<#@ template language="C#" linePragmas="false" #> -unique_ptr sim = CreateFullstateSimulator(); -QirContextScope qirctx(sim.get(), <#= this.debug ? "true" : "false" #> /*trackAllocatedObjects*/); \ No newline at end of file diff --git a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs b/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs deleted file mode 100644 index 793965e657f..00000000000 --- a/src/Qir/Tools/Driver/QirCppFullStateSimulatorInitializerEx.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Microsoft.Quantum.Qir.Runtime.Tools.Driver -{ - public partial class QirCppFullStateSimulatorInitializer - { - private readonly bool debug; - - internal QirCppFullStateSimulatorInitializer(bool debug) => this.debug = debug; - } -} diff --git a/src/Qir/Tools/Driver/QirCppInterop.cs b/src/Qir/Tools/Driver/QirCppInterop.cs index 6d795608106..02cd0244c84 100644 --- a/src/Qir/Tools/Driver/QirCppInterop.cs +++ b/src/Qir/Tools/Driver/QirCppInterop.cs @@ -32,7 +32,7 @@ public static string CliOptionType(DataType? dataType) => DataType.BoolType => "char", DataType.IntegerType => "int64_t", DataType.DoubleType => "double_t", - DataType.PauliType => "PauliId", + DataType.PauliType => "char", DataType.RangeType => "RangeTuple", DataType.ResultType => "char", DataType.StringType => "string", @@ -46,7 +46,7 @@ public static string CliOptionType(DataType? dataType) => DataType.BoolType => null, DataType.IntegerType => null, DataType.DoubleType => null, - DataType.PauliType => "TranslatePauliToChar", + DataType.PauliType => null, DataType.RangeType => "TranslateRangeTupleToInteropRangePointer", DataType.ResultType => null, DataType.StringType => "TranslateStringToCharBuffer", @@ -60,7 +60,7 @@ public static string CliOptionType(DataType? dataType) => DataType.BoolType => "InteropFalseAsChar", DataType.IntegerType => "0", DataType.DoubleType => "0.0", - DataType.PauliType => "PauliId::PauliId_I", + DataType.PauliType => "0", DataType.RangeType => null, DataType.ResultType => "InteropResultZeroAsChar", DataType.StringType => null, diff --git a/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs b/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs index aadfa0a4577..b65500b1c03 100644 --- a/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs +++ b/src/Qir/Tools/Driver/QirFullStateDriverGenerator.cs @@ -11,8 +11,8 @@ public class QirFullStateDriverGenerator: IQirDriverGenerator { private readonly QirCppDriverGenerator DriverGenerator; - public QirFullStateDriverGenerator(bool debug) => - DriverGenerator = new QirCppDriverGenerator(new QirFullStateSimulatorInitializer(debug)); + public QirFullStateDriverGenerator() => + DriverGenerator = new QirCppDriverGenerator(new QirFullStateSimulatorInitializer()); public async Task GenerateAsync(EntryPointOperation entryPoint, Stream stream) => await DriverGenerator.GenerateAsync(entryPoint, stream); diff --git a/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs b/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs index ca5af5d863a..03256773197 100644 --- a/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs +++ b/src/Qir/Tools/Driver/QirFullStateSimulatorInitializer.cs @@ -7,15 +7,11 @@ namespace Microsoft.Quantum.Qir.Runtime.Tools.Driver { public class QirFullStateSimulatorInitializer : IQirRuntimeInitializer { - private readonly bool debug; + internal QirFullStateSimulatorInitializer() {} - internal QirFullStateSimulatorInitializer(bool debug) => this.debug = debug; + public string Generate() => ""; - public string Generate() => new QirCppFullStateSimulatorInitializer(this.debug).TransformText(); - - public IEnumerable Headers => new [] { - "SimFactory.hpp" - }; + public IEnumerable Headers => new string[0]; public IEnumerable LinkLibraries => new string[0]; } diff --git a/src/Qir/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Tools/Executable/QirFullStateExecutable.cs index c626427e6d4..3ed680eacb9 100644 --- a/src/Qir/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Tools/Executable/QirFullStateExecutable.cs @@ -21,17 +21,15 @@ public class QirFullStateExecutable : QirExecutable public override string DriverFileExtension => "cpp"; public override IList LinkLibraries => new List { - "Microsoft.Quantum.Qir.Runtime", - "Microsoft.Quantum.Qir.QSharp.Foundation", - "Microsoft.Quantum.Qir.QSharp.Core" + "Microsoft.Quantum.Simulator.Runtime" }; public override IList HeaderDirectories { get; } = new List(); public override IList LibraryDirectories { get; } = new List(); - public QirFullStateExecutable(FileInfo executableFile, byte[] qirBitcode, bool debug, ILogger? logger = null) - : base(executableFile, qirBitcode, new QirFullStateDriverGenerator(debug), logger) + public QirFullStateExecutable(FileInfo executableFile, byte[] qirBitcode, ILogger? logger = null) + : base(executableFile, qirBitcode, new QirFullStateDriverGenerator(), logger) { var thisModulePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrWhiteSpace(thisModulePath)) diff --git a/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj index a6f34c8ebd1..9de96cf173e 100644 --- a/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj +++ b/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj @@ -44,10 +44,6 @@ TextTemplatingFilePreprocessor QirCppDriver.cs - - TextTemplatingFilePreprocessor - QirCppFullStateSimulatorInitializer.cs - @@ -60,11 +56,6 @@ True QirCppDriver.tt - - True - True - QirCppFullStateSimulatorInitializer.tt - diff --git a/src/Qir/Tools/QirTools.cs b/src/Qir/Tools/QirTools.cs index a159d75f072..f26fc6bfaa1 100644 --- a/src/Qir/Tools/QirTools.cs +++ b/src/Qir/Tools/QirTools.cs @@ -23,7 +23,7 @@ public static class QirTools /// Directory where the libraries to link to are located. /// Directory where the headers needed for compilation are located. /// Directory where the created executables are placed. - /// Enable additional debugging checks at runtime. + /// Ignored. public static async Task BuildFromQSharpDll( FileInfo qsharpDll, IList libraryDirectories, @@ -44,7 +44,7 @@ public static async Task BuildFromQSharpDll( foreach (var entryPointOp in EntryPointLoader.LoadEntryPointOperations(qsharpDll)) { var exeFileInfo = new FileInfo(Path.Combine(executablesDirectory.FullName, $"{entryPointOp.Name}.exe")); - var exe = new QirFullStateExecutable(exeFileInfo, qirContentStream.ToArray(), debug); + var exe = new QirFullStateExecutable(exeFileInfo, qirContentStream.ToArray()); await exe.BuildAsync(entryPointOp, libraryDirectories, includeDirectories); } From 9276d2d545a7eedf7c19f506c6139cda9259032c Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 17:37:01 +0000 Subject: [PATCH 25/55] Use whole-archive linking on non-Windows --- src/Simulation/Native/src/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index e9fef2afd53..5b379f6852d 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -38,11 +38,11 @@ target_include_directories(Microsoft.Quantum.Simulator.Runtime PRIVATE "${PROJEC target_link_libraries(Microsoft.Quantum.Simulator.Runtime ${SPECTRE_LIBS} "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" - -lqir_stdlib ) if (WIN32) target_link_libraries(Microsoft.Quantum.Simulator.Runtime + qir_stdlib Ws2_32 Bcrypt Userenv @@ -50,7 +50,11 @@ if (WIN32) target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") else() target_link_libraries(Microsoft.Quantum.Simulator.Runtime + "-Wl,--whole-archive" + qir_stdlib + "-Wl,--no-whole-archive" pthread + dl ) endif() From 4fdb5d186509e2f145bd2cd71de019bdc9dde474 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 18:29:05 +0000 Subject: [PATCH 26/55] Use thread_local for simulator init --- src/Simulation/Native/src/simulator/qir.cpp | 61 ++++++++++++--------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/Simulation/Native/src/simulator/qir.cpp b/src/Simulation/Native/src/simulator/qir.cpp index ea3ea8d7b61..23c13891f07 100644 --- a/src/Simulation/Native/src/simulator/qir.cpp +++ b/src/Simulation/Native/src/simulator/qir.cpp @@ -11,11 +11,18 @@ #include #include #include +#include #include "capi.hpp" #include "qir.hpp" -static const unsigned GLOBAL_SIM = init(); +unsigned SimId() +{ + // Using `thread_local` ensures each thread gets a unique simulator instance. This matches + // expected QIR usage where multithreaded program execution is not supported. + thread_local unsigned _sim = init(); + return _sim; +} // Pauli consts are {i2} in QIR, likely stored as {i8} in arrays, but we are using the standard C++ enum type based on // {i32} so cannot pass through the buffer and have to allocate a new one instead and copy. @@ -123,7 +130,7 @@ TDumpToLocationCallback const dumpToLocationCallback = void DumpMachineImpl(std::ostream& outStream) { outStream << "# wave function for qubits (least to most significant qubit ids):" << std::endl; - DumpToLocation(GLOBAL_SIM, dumpToLocationCallback, (TDumpLocation)&outStream); + DumpToLocation(SimId(), dumpToLocationCallback, (TDumpLocation)&outStream); outStream.flush(); } @@ -144,7 +151,7 @@ void DumpRegisterImpl(std::ostream& outStream, QirArray* qubits) } outStream << ':' << std::endl; - if (!DumpQubitsToLocation(GLOBAL_SIM, ids.size(), ids.data(), dumpToLocationCallback, (TDumpLocation)&outStream)) + if (!DumpQubitsToLocation(SimId(), ids.size(), ids.data(), dumpToLocationCallback, (TDumpLocation)&outStream)) { outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" << std::endl; @@ -174,7 +181,7 @@ extern "C" { QUBIT* __quantum__rt__qubit_allocate() { - return reinterpret_cast(allocate(GLOBAL_SIM)); + return reinterpret_cast(allocate(SimId())); } QirArray* __quantum__rt__qubit_allocate_array(int64_t count) @@ -190,7 +197,7 @@ extern "C" void __quantum__rt__qubit_release(QUBIT* qubit) { - release(GLOBAL_SIM, reinterpret_cast(qubit)); + release(SimId(), reinterpret_cast(qubit)); } void __quantum__rt__qubit_release_array(QirArray* array) @@ -255,7 +262,7 @@ extern "C" auto targets = reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0)); std::vector ids = GetQubitIds(numTargets, targets); std::vector convertedBases = GetBases(numTargets, pauliIds.data()); - Exp(GLOBAL_SIM, (unsigned)numTargets, convertedBases.data(), angle, ids.data()); + Exp(SimId(), (unsigned)numTargets, convertedBases.data(), angle, ids.data()); } void __quantum__qis__exp__adj(QirArray* paulis, double angle, QirArray* qubits) @@ -276,7 +283,7 @@ extern "C" std::vector idsControls = GetQubitIds(numControls, controls); std::vector convertedBases = GetBases(numTargets, pauliIds.data()); MCExp( - GLOBAL_SIM, (unsigned)numTargets, convertedBases.data(), args->angle, (unsigned)numControls, idsControls.data(), + SimId(), (unsigned)numTargets, convertedBases.data(), args->angle, (unsigned)numControls, idsControls.data(), idsTargets.data()); } @@ -291,7 +298,7 @@ extern "C" void __quantum__qis__h__body(QUBIT* qubit) { - H(GLOBAL_SIM, reinterpret_cast(qubit)); + H(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__h__ctl(QirArray* ctls, QUBIT* qubit) @@ -299,7 +306,7 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCH(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCH(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } RESULT* __quantum__qis__measure__body(QirArray* paulis, QirArray* qubits) @@ -311,13 +318,13 @@ extern "C" std::vector convertedBases = GetBases(count, pauliIds.data()); std::vector ids = GetQubitIds(count, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qubits, 0))); - return Measure(GLOBAL_SIM, count, convertedBases.data(), ids.data()) ? __quantum__rt__result_get_one() + return Measure(SimId(), count, convertedBases.data(), ids.data()) ? __quantum__rt__result_get_one() : __quantum__rt__result_get_zero(); } void __quantum__qis__r__body(PauliId axis, double angle, QUBIT* qubit) { - R(GLOBAL_SIM, static_cast(axis), angle, reinterpret_cast(qubit)); + R(SimId(), static_cast(axis), angle, reinterpret_cast(qubit)); } void __quantum__qis__r__adj(PauliId axis, double angle, QUBIT* qubit) @@ -330,7 +337,7 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector controls = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCR(GLOBAL_SIM, static_cast(args->pauli), args->angle, numControls, controls.data(), + MCR(SimId(), static_cast(args->pauli), args->angle, numControls, controls.data(), reinterpret_cast(args->target)); } @@ -342,12 +349,12 @@ extern "C" void __quantum__qis__s__body(QUBIT* qubit) { - S(GLOBAL_SIM, reinterpret_cast(qubit)); + S(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__s__adj(QUBIT* qubit) { - AdjS(GLOBAL_SIM, reinterpret_cast(qubit)); + AdjS(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__s__ctl(QirArray* ctls, QUBIT* qubit) @@ -355,7 +362,7 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCS(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCS(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__s__ctladj(QirArray* ctls, QUBIT* qubit) @@ -363,17 +370,17 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCAdjS(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCAdjS(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__t__body(QUBIT* qubit) { - T(GLOBAL_SIM, reinterpret_cast(qubit)); + T(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__t__adj(QUBIT* qubit) { - AdjT(GLOBAL_SIM, reinterpret_cast(qubit)); + AdjT(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__t__ctl(QirArray* ctls, QUBIT* qubit) @@ -381,7 +388,7 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCT(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCT(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__t__ctladj(QirArray* ctls, QUBIT* qubit) @@ -389,12 +396,12 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCAdjT(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCAdjT(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__x__body(QUBIT* qubit) { - X(GLOBAL_SIM, reinterpret_cast(qubit)); + X(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__x__ctl(QirArray* ctls, QUBIT* qubit) @@ -402,12 +409,12 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCX(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCX(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__y__body(QUBIT* qubit) { - Y(GLOBAL_SIM, reinterpret_cast(qubit)); + Y(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__y__ctl(QirArray* ctls, QUBIT* qubit) @@ -415,12 +422,12 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCY(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCY(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__z__body(QUBIT* qubit) { - Z(GLOBAL_SIM, reinterpret_cast(qubit)); + Z(SimId(), reinterpret_cast(qubit)); } void __quantum__qis__z__ctl(QirArray* ctls, QUBIT* qubit) @@ -428,7 +435,7 @@ extern "C" auto numControls = __quantum__rt__array_get_size_1d(ctls); std::vector ids = GetQubitIds(numControls, reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(ctls, 0))); - MCZ(GLOBAL_SIM, numControls, ids.data(), reinterpret_cast(qubit)); + MCZ(SimId(), numControls, ids.data(), reinterpret_cast(qubit)); } void __quantum__qis__dumpmachine__body(uint8_t* location) @@ -477,7 +484,7 @@ extern "C" std::vector convertedBases = GetBases(paulis.size(), paulis.data()); double actualProbability = 1.0 - JointEnsembleProbability( - GLOBAL_SIM, (unsigned)paulis.size(), reinterpret_cast(convertedBases.data()), ids.data()); + SimId(), (unsigned)paulis.size(), reinterpret_cast(convertedBases.data()), ids.data()); if (!(std::abs(actualProbability - prob) < 1e-10)) { From 6abef66ec33fe604742a44b040a379903fa54959 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 18:34:39 +0000 Subject: [PATCH 27/55] Use all_load on MacOS --- src/Simulation/Native/src/CMakeLists.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 5b379f6852d..d8c454a8755 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -48,11 +48,19 @@ if (WIN32) Userenv ) target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") +elseif (APPLE) + target_link_libraries(Microsoft.Quantum.Simulator.Runtime + "-Wl,-all_load" + qir_stdlib + "-Wl,-noall_load" + pthread + dl + ) else() target_link_libraries(Microsoft.Quantum.Simulator.Runtime - "-Wl,--whole-archive" - qir_stdlib - "-Wl,--no-whole-archive" + "-Wl,--whole-archive" + qir_stdlib + "-Wl,--no-whole-archive" pthread dl ) From 2bc0e7a2d2f2bbf5b8569d230543017cccf606a4 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 11:40:02 -0700 Subject: [PATCH 28/55] Skip QIR libs,include for runtime tool --- .../Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj index 9de96cf173e..e7945b3d8df 100644 --- a/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj +++ b/src/Qir/Tools/Microsoft.Quantum.Qir.Runtime.Tools.csproj @@ -22,16 +22,6 @@ true PreserveNewest - - runtimes\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - - - runtimes\any\native\include\%(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - false - From 2ee9938129c8f68b4205cbde4b92ba6d7c15c802 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 12:32:10 -0700 Subject: [PATCH 29/55] Use force_load on MacOs --- src/Simulation/Native/src/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index d8c454a8755..18f0b8c5908 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -50,9 +50,8 @@ if (WIN32) target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") elseif (APPLE) target_link_libraries(Microsoft.Quantum.Simulator.Runtime - "-Wl,-all_load" + "-Wl,-force_load" qir_stdlib - "-Wl,-noall_load" pthread dl ) From e4a86d743715df959b71e6c5b60103048e132ffb Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 13:20:38 -0700 Subject: [PATCH 30/55] Try alternate MacOS linking strategy --- src/Simulation/Native/src/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 18f0b8c5908..13ab57f747a 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -50,8 +50,7 @@ if (WIN32) target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") elseif (APPLE) target_link_libraries(Microsoft.Quantum.Simulator.Runtime - "-Wl,-force_load" - qir_stdlib + "-Wl,-force_load,qir_stdlib" pthread dl ) From 9d11f51e233b53f50cf95f4fddac51d8db971885 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 21:11:05 -0700 Subject: [PATCH 31/55] Use full path in MacOs linking --- src/Simulation/Native/src/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 13ab57f747a..a0503a3e649 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -36,12 +36,12 @@ endif(BUILD_SHARED_LIBS) target_include_directories(Microsoft.Quantum.Simulator.Runtime PRIVATE "${PROJECT_BINARY_DIR}/../../../Qir/drops/include") target_link_libraries(Microsoft.Quantum.Simulator.Runtime - ${SPECTRE_LIBS} - "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" ) if (WIN32) target_link_libraries(Microsoft.Quantum.Simulator.Runtime + ${SPECTRE_LIBS} + "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" qir_stdlib Ws2_32 Bcrypt @@ -50,12 +50,13 @@ if (WIN32) target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") elseif (APPLE) target_link_libraries(Microsoft.Quantum.Simulator.Runtime - "-Wl,-force_load,qir_stdlib" + "-Wl,-force_load,${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native/qir_stdlib" pthread dl ) else() target_link_libraries(Microsoft.Quantum.Simulator.Runtime + "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" "-Wl,--whole-archive" qir_stdlib "-Wl,--no-whole-archive" From c5920ba5397aac13072ec65f1a407b67206b0cc8 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 21:48:24 -0700 Subject: [PATCH 32/55] Try explicit lib name --- src/Simulation/Native/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index a0503a3e649..64ff9a02c30 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -50,7 +50,7 @@ if (WIN32) target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") elseif (APPLE) target_link_libraries(Microsoft.Quantum.Simulator.Runtime - "-Wl,-force_load,${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native/qir_stdlib" + "-Wl,-force_load,${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native/libqir_stdlib.a" pthread dl ) From 997e1d35e78151bbc1410671e2c0f153b627ccf6 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 6 Sep 2022 23:07:42 -0700 Subject: [PATCH 33/55] Clean up and comments --- src/Simulation/Native/src/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 64ff9a02c30..8cc793fdbad 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -35,8 +35,6 @@ else(BUILD_SHARED_LIBS) endif(BUILD_SHARED_LIBS) target_include_directories(Microsoft.Quantum.Simulator.Runtime PRIVATE "${PROJECT_BINARY_DIR}/../../../Qir/drops/include") -target_link_libraries(Microsoft.Quantum.Simulator.Runtime -) if (WIN32) target_link_libraries(Microsoft.Quantum.Simulator.Runtime @@ -47,14 +45,17 @@ if (WIN32) Bcrypt Userenv ) + # On Windows, use a .def file to force export stdlib functions from the static library. target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") elseif (APPLE) + # On MacOS, use -force_load and explicit path to pull in entire contents of stdlib static archive. target_link_libraries(Microsoft.Quantum.Simulator.Runtime "-Wl,-force_load,${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native/libqir_stdlib.a" pthread dl ) else() + # On Unix systems, use --whole-archive to to pull in entire contents of stdlib static archive. target_link_libraries(Microsoft.Quantum.Simulator.Runtime "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" "-Wl,--whole-archive" From 78aced67069c0d3a9352659a2cc1ea8301c767cd Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 7 Sep 2022 01:08:25 -0700 Subject: [PATCH 34/55] Remove QIR C++ runtime, rename folders --- Cargo.toml | 4 +- README.md | 2 - bootstrap.ps1 | 2 +- build/build.ps1 | 14 +- build/manifest.ps1 | 6 +- build/steps-codecheck.yml | 4 +- build/test.ps1 | 16 +- src/Qir/Common/Include/SimulatorStub.hpp | 137 --- .../Include/qsharp__foundation_internal.hpp | 25 - src/Qir/Runtime/CMakeLists.txt | 33 - src/Qir/Runtime/README.md | 197 --- src/Qir/{riqir => Runtime}/backend/Cargo.toml | 0 .../backend/src/common_matrices.rs | 0 src/Qir/{riqir => Runtime}/backend/src/lib.rs | 0 .../backend/src/nearly_zero.rs | 0 .../backend/src/result_bool.rs | 0 .../backend/src/simulator.rs | 0 src/Qir/Runtime/build-qir-runtime.ps1 | 27 - .../{riqir => Runtime}/build-qir-stdlib.ps1 | 0 src/Qir/Runtime/lib/CMakeLists.txt | 5 - .../Runtime/lib/QIR/BasicRuntimeDriver.cpp | 75 -- src/Qir/Runtime/lib/QIR/CMakeLists.txt | 60 - src/Qir/Runtime/lib/QIR/Output.cpp | 26 - src/Qir/Runtime/lib/QIR/OutputStream.cpp | 46 - src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp | 87 -- src/Qir/Runtime/lib/QIR/QubitManager.cpp | 483 -------- src/Qir/Runtime/lib/QIR/README.md | 59 - .../Runtime/lib/QIR/allocationsTracker.cpp | 83 -- .../Runtime/lib/QIR/allocationsTracker.hpp | 28 - src/Qir/Runtime/lib/QIR/arrays.cpp | 683 ----------- src/Qir/Runtime/lib/QIR/bridge-rt.ll | 43 - src/Qir/Runtime/lib/QIR/callables.cpp | 471 -------- src/Qir/Runtime/lib/QIR/context.cpp | 131 -- src/Qir/Runtime/lib/QIR/delegated.cpp | 157 --- src/Qir/Runtime/lib/QIR/strings.cpp | 187 --- src/Qir/Runtime/lib/QIR/utils.cpp | 36 - src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt | 44 - src/Qir/Runtime/lib/QSharpCore/README.md | 26 - src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp | 192 --- .../Runtime/lib/QSharpCore/intrinsicsDump.cpp | 23 - .../lib/QSharpCore/qsharp__core__qis.hpp | 63 - .../QSharpFoundation/AssertMeasurement.cpp | 60 - .../lib/QSharpFoundation/CMakeLists.txt | 49 - .../Runtime/lib/QSharpFoundation/README.md | 37 - .../lib/QSharpFoundation/conditionals.cpp | 44 - .../lib/QSharpFoundation/intrinsicsMath.cpp | 185 --- .../qsharp__foundation__qis.hpp | 65 - src/Qir/Runtime/lib/README.md | 62 - src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 21 - .../lib/Simulators/FullstateSimulator.cpp | 654 ---------- src/Qir/Runtime/lib/Simulators/README.md | 51 - .../lib/Simulators/ToffoliSimulator.cpp | 257 ---- src/Qir/Runtime/lib/Tracer/CMakeLists.txt | 41 - src/Qir/Runtime/lib/Tracer/README.md | 218 ---- src/Qir/Runtime/lib/Tracer/TracerInternal.hpp | 18 - .../Runtime/lib/Tracer/layering_example.png | Bin 26209 -> 0 bytes src/Qir/Runtime/lib/Tracer/tracer-qis.cpp | 72 -- src/Qir/Runtime/lib/Tracer/tracer-qis.hpp | 31 - src/Qir/Runtime/lib/Tracer/tracer.cpp | 382 ------ src/Qir/Runtime/lib/Tracer/tracer.hpp | 220 ---- src/Qir/Runtime/prerequisites.ps1 | 26 +- .../public/BasicRuntimeDriverFactory.h | 21 - src/Qir/Runtime/public/CoreDefines.h | 14 - src/Qir/Runtime/public/CoreTypes.hpp | 49 - src/Qir/Runtime/public/OutputStream.hpp | 41 - src/Qir/Runtime/public/QSharpSimApi_I.hpp | 92 -- src/Qir/Runtime/public/QirContext.h | 18 - src/Qir/Runtime/public/QirContext.hpp | 69 -- src/Qir/Runtime/public/QirOutputHandling.hpp | 88 -- src/Qir/Runtime/public/QirRuntime.hpp | 317 ----- src/Qir/Runtime/public/QirRuntimeApi_I.hpp | 59 - src/Qir/Runtime/public/QirTypes.hpp | 204 ---- src/Qir/Runtime/public/QubitManager.hpp | 255 ---- src/Qir/Runtime/public/README.md | 57 - src/Qir/Runtime/public/SimFactory.h | 21 - src/Qir/Runtime/public/SimFactory.hpp | 23 - src/Qir/Runtime/public/TracerTypes.hpp | 20 - src/Qir/Runtime/qir.png | Bin 37446 -> 0 bytes src/Qir/{riqir => Runtime}/stdlib/Cargo.toml | 0 src/Qir/{riqir => Runtime}/stdlib/build.rs | 0 .../stdlib/include/qir_stdlib.def | 0 .../stdlib/include/qir_stdlib.h | 4 +- .../{riqir => Runtime}/stdlib/src/arrays.rs | 0 .../{riqir => Runtime}/stdlib/src/bigints.rs | 0 .../stdlib/src/bridge-rt.ll | 0 .../stdlib/src/callables.rs | 0 src/Qir/{riqir => Runtime}/stdlib/src/lib.rs | 0 src/Qir/{riqir => Runtime}/stdlib/src/math.rs | 0 .../stdlib/src/output_recording.rs | 0 .../stdlib/src/range_support.rs | 0 .../{riqir => Runtime}/stdlib/src/strings.rs | 0 .../{riqir => Runtime}/stdlib/src/tuples.rs | 0 src/Qir/Runtime/test-qir-runtime.ps1 | 13 - .../{riqir => Runtime}/test-qir-stdlib.ps1 | 0 src/Qir/Runtime/unittests/CMakeLists.txt | 32 - .../unittests/QirOutputHandlingTests.cpp | 238 ---- src/Qir/Runtime/unittests/QirRuntimeTests.cpp | 1075 ----------------- .../Runtime/unittests/QubitManagerTests.cpp | 341 ------ src/Qir/Runtime/unittests/ToffoliTests.cpp | 130 -- src/Qir/Runtime/unittests/TracerTests.cpp | 482 -------- src/Qir/Runtime/unittests/driver.cpp | 7 - .../StandaloneInputReference/CMakeLists.txt | 7 +- src/Qir/Tests/.clang-tidy | 3 - src/Qir/Tests/CMakeLists.txt | 15 +- .../Tests/FullstateSimulator/CMakeLists.txt | 10 +- .../FullstateSimulatorTests.cpp | 430 +------ .../qsharp/qir-test-simulator.qs | 34 +- src/Qir/Tests/QIR-dynamic/CMakeLists.txt | 15 +- src/Qir/Tests/QIR-dynamic/qir-driver.cpp | 157 --- src/Qir/Tests/QIR-static/CMakeLists.txt | 10 +- .../QIR-static}/FloatUtils.hpp | 0 src/Qir/Tests/QIR-static/qir-driver.cpp | 283 +---- src/Qir/Tests/QIR-static/qir-test-math.cpp | 162 --- src/Qir/Tests/QIR-static/qir-test-noqsharp.ll | 48 - src/Qir/Tests/QIR-static/qir-test-other.cpp | 16 +- src/Qir/Tests/QIR-static/qir-test-ouput.cpp | 29 - src/Qir/Tests/QIR-tracer/CMakeLists.txt | 30 - src/Qir/Tests/QIR-tracer/generate.py | 46 - .../Tests/QIR-tracer/qir-tracer-driver.cpp | 49 - .../QIR-tracer/qsharp/tracer-conditionals.qs | 26 - .../Tests/QIR-tracer/qsharp/tracer-core.qs | 30 - .../QIR-tracer/qsharp/tracer-intrinsics.qs | 64 - .../Tests/QIR-tracer/qsharp/tracer-qir.csproj | 11 - .../Tests/QIR-tracer/qsharp/tracer-target.qs | 280 ----- src/Qir/Tests/QIR-tracer/tracer-config.cpp | 18 - src/Qir/Tests/QIR-tracer/tracer-config.hpp | 20 - src/Qir/Tests/TestUtils.cpp | 15 - src/Qir/Tests/TestUtils.hpp | 11 - .../Tests/Tools/QirDriverGeneratorTests.cs | 230 ---- .../Tools/QirExecutableGeneratorTests.cs | 105 -- src/Qir/Tests/Tools/QirExecutableTests.cs | 114 -- .../FullStateDriverGenerator/UseBoolArg.cpp | 59 - .../UseBoolArrayArg.cpp | 84 -- .../FullStateDriverGenerator/UseDoubleArg.cpp | 48 - .../UseDoubleArrayArg.cpp | 73 -- .../UseIntegerArg.cpp | 48 - .../UseIntegerArrayArg.cpp | 73 -- .../FullStateDriverGenerator/UseMiscArgs.cpp | 146 --- .../FullStateDriverGenerator/UseNoArgs.cpp | 37 - .../FullStateDriverGenerator/UsePauliArg.cpp | 57 - .../UsePauliArrayArg.cpp | 82 -- .../FullStateDriverGenerator/UseRangeArg.cpp | 78 -- .../UseRangeArrayArg.cpp | 116 -- .../FullStateDriverGenerator/UseResultArg.cpp | 59 - .../UseResultArrayArg.cpp | 84 -- .../FullStateDriverGenerator/UseStringArg.cpp | 53 - ...Microsoft.Quantum.Qir.Runtime.Tools.csproj | 69 -- src/Qir/Tests/Tools/Util.cs | 53 - src/Qir/Tests/build-qir-tests.ps1 | 1 - src/Qir/build_all.ps1 | 1 - src/Qir/riqir-test/CMakeLists.txt | 37 - .../FullstateSimulator/CMakeLists.txt | 19 - src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt | 29 - src/Qir/riqir-test/QIR-static/CMakeLists.txt | 33 - src/Qir/riqir-test/build-qir-tests.ps1 | 24 - src/Qir/riqir-test/test-qir-tests.ps1 | 38 - src/Qir/test_all.ps1 | 3 - 157 files changed, 81 insertions(+), 13229 deletions(-) delete mode 100644 src/Qir/Common/Include/SimulatorStub.hpp delete mode 100644 src/Qir/Common/Include/qsharp__foundation_internal.hpp delete mode 100644 src/Qir/Runtime/CMakeLists.txt delete mode 100644 src/Qir/Runtime/README.md rename src/Qir/{riqir => Runtime}/backend/Cargo.toml (100%) rename src/Qir/{riqir => Runtime}/backend/src/common_matrices.rs (100%) rename src/Qir/{riqir => Runtime}/backend/src/lib.rs (100%) rename src/Qir/{riqir => Runtime}/backend/src/nearly_zero.rs (100%) rename src/Qir/{riqir => Runtime}/backend/src/result_bool.rs (100%) rename src/Qir/{riqir => Runtime}/backend/src/simulator.rs (100%) delete mode 100644 src/Qir/Runtime/build-qir-runtime.ps1 rename src/Qir/{riqir => Runtime}/build-qir-stdlib.ps1 (100%) delete mode 100644 src/Qir/Runtime/lib/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QIR/Output.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/OutputStream.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/QubitManager.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/README.md delete mode 100644 src/Qir/Runtime/lib/QIR/allocationsTracker.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/allocationsTracker.hpp delete mode 100644 src/Qir/Runtime/lib/QIR/arrays.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/bridge-rt.ll delete mode 100644 src/Qir/Runtime/lib/QIR/callables.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/context.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/delegated.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/strings.cpp delete mode 100644 src/Qir/Runtime/lib/QIR/utils.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QSharpCore/README.md delete mode 100644 src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/README.md delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp delete mode 100644 src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp delete mode 100644 src/Qir/Runtime/lib/README.md delete mode 100644 src/Qir/Runtime/lib/Simulators/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp delete mode 100644 src/Qir/Runtime/lib/Simulators/README.md delete mode 100644 src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp delete mode 100644 src/Qir/Runtime/lib/Tracer/CMakeLists.txt delete mode 100644 src/Qir/Runtime/lib/Tracer/README.md delete mode 100644 src/Qir/Runtime/lib/Tracer/TracerInternal.hpp delete mode 100644 src/Qir/Runtime/lib/Tracer/layering_example.png delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer-qis.cpp delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer-qis.hpp delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer.cpp delete mode 100644 src/Qir/Runtime/lib/Tracer/tracer.hpp delete mode 100644 src/Qir/Runtime/public/BasicRuntimeDriverFactory.h delete mode 100644 src/Qir/Runtime/public/CoreDefines.h delete mode 100644 src/Qir/Runtime/public/CoreTypes.hpp delete mode 100644 src/Qir/Runtime/public/OutputStream.hpp delete mode 100644 src/Qir/Runtime/public/QSharpSimApi_I.hpp delete mode 100644 src/Qir/Runtime/public/QirContext.h delete mode 100644 src/Qir/Runtime/public/QirContext.hpp delete mode 100644 src/Qir/Runtime/public/QirOutputHandling.hpp delete mode 100644 src/Qir/Runtime/public/QirRuntime.hpp delete mode 100644 src/Qir/Runtime/public/QirRuntimeApi_I.hpp delete mode 100644 src/Qir/Runtime/public/QirTypes.hpp delete mode 100644 src/Qir/Runtime/public/QubitManager.hpp delete mode 100644 src/Qir/Runtime/public/README.md delete mode 100644 src/Qir/Runtime/public/SimFactory.h delete mode 100644 src/Qir/Runtime/public/SimFactory.hpp delete mode 100644 src/Qir/Runtime/public/TracerTypes.hpp delete mode 100644 src/Qir/Runtime/qir.png rename src/Qir/{riqir => Runtime}/stdlib/Cargo.toml (100%) rename src/Qir/{riqir => Runtime}/stdlib/build.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/include/qir_stdlib.def (100%) rename src/Qir/{riqir => Runtime}/stdlib/include/qir_stdlib.h (98%) rename src/Qir/{riqir => Runtime}/stdlib/src/arrays.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/bigints.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/bridge-rt.ll (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/callables.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/lib.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/math.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/output_recording.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/range_support.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/strings.rs (100%) rename src/Qir/{riqir => Runtime}/stdlib/src/tuples.rs (100%) delete mode 100644 src/Qir/Runtime/test-qir-runtime.ps1 rename src/Qir/{riqir => Runtime}/test-qir-stdlib.ps1 (100%) delete mode 100644 src/Qir/Runtime/unittests/CMakeLists.txt delete mode 100644 src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp delete mode 100644 src/Qir/Runtime/unittests/QirRuntimeTests.cpp delete mode 100644 src/Qir/Runtime/unittests/QubitManagerTests.cpp delete mode 100644 src/Qir/Runtime/unittests/ToffoliTests.cpp delete mode 100644 src/Qir/Runtime/unittests/TracerTests.cpp delete mode 100644 src/Qir/Runtime/unittests/driver.cpp delete mode 100644 src/Qir/Tests/.clang-tidy rename src/Qir/{Common/Include => Tests/QIR-static}/FloatUtils.hpp (100%) delete mode 100644 src/Qir/Tests/QIR-static/qir-test-noqsharp.ll delete mode 100644 src/Qir/Tests/QIR-static/qir-test-ouput.cpp delete mode 100644 src/Qir/Tests/QIR-tracer/CMakeLists.txt delete mode 100644 src/Qir/Tests/QIR-tracer/generate.py delete mode 100644 src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj delete mode 100644 src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs delete mode 100644 src/Qir/Tests/QIR-tracer/tracer-config.cpp delete mode 100644 src/Qir/Tests/QIR-tracer/tracer-config.hpp delete mode 100644 src/Qir/Tests/TestUtils.cpp delete mode 100644 src/Qir/Tests/TestUtils.hpp delete mode 100644 src/Qir/Tests/Tools/QirDriverGeneratorTests.cs delete mode 100644 src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs delete mode 100644 src/Qir/Tests/Tools/QirExecutableTests.cs delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp delete mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp delete mode 100644 src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj delete mode 100644 src/Qir/Tests/Tools/Util.cs delete mode 100644 src/Qir/build_all.ps1 delete mode 100644 src/Qir/riqir-test/CMakeLists.txt delete mode 100644 src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt delete mode 100644 src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt delete mode 100644 src/Qir/riqir-test/QIR-static/CMakeLists.txt delete mode 100644 src/Qir/riqir-test/build-qir-tests.ps1 delete mode 100644 src/Qir/riqir-test/test-qir-tests.ps1 delete mode 100644 src/Qir/test_all.ps1 diff --git a/Cargo.toml b/Cargo.toml index b96c13d014b..697c8c46c0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ members = [ "src/Simulation/qdk_sim_rs", - "src/Qir/riqir/stdlib", - "src/Qir/riqir/backend", + "src/Qir/Runtime/stdlib", + "src/Qir/Runtime/backend", ] [profile.release] diff --git a/README.md b/README.md index 3978cceb071..2ca038490c5 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ You may also visit our [Quantum](https://github.com/microsoft/quantum) repositor Note that when building from source, this repository is configured so that .NET will automatically look at the [Quantum Development Kit prerelease feed](https://dev.azure.com/ms-quantum-public/Microsoft%20Quantum%20(public)/_packaging?_a=feed&feed=alpha) in addition to any other feeds you may have configured. -Building **QIR Runtime** isn't enabled by default yet. Please see [its readme](./src/Qir/Runtime/README.md) for details. - ### All platforms ### 1. Install the pre-reqs: diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 86063d483be..ac8a020a427 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -16,7 +16,7 @@ if (-not (Test-Path Env:/AGENT_OS)) { # If no if ($Env:ENABLE_QIRRUNTIME -ne "false") { Write-Host "Build release flavor of the QIR standard library" $Env:BUILD_CONFIGURATION = "Release" - Push-Location (Join-Path $PSScriptRoot "src/Qir/riqir") + Push-Location (Join-Path $PSScriptRoot "src/Qir/Runtime") .\build-qir-stdlib.ps1 Pop-Location $Env:BUILD_CONFIGURATION = $null diff --git a/build/build.ps1 b/build/build.ps1 index 5477434cd88..41f19d8cdbf 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -7,13 +7,8 @@ $ErrorActionPreference = 'Stop' $all_ok = $True if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") - & "$qirRuntime/build-qir-stdlib.ps1" - if ($LastExitCode -ne 0) { - $script:all_ok = $False - } - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirRuntime/build-qir-runtime.ps1" + $qirstdlib = (Join-Path $PSScriptRoot "../src/Qir/Runtime") + & "$qirstdlib/build-qir-stdlib.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } @@ -93,11 +88,6 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { if ($LastExitCode -ne 0) { $script:all_ok = $False } - $qirTests = (Join-Path $PSScriptRoot "../src/Qir/riqir-test") - & "$qirTests/build-qir-tests.ps1" -SkipQSharpBuild - if ($LastExitCode -ne 0) { - $script:all_ok = $False - } $qirSamples = (Join-Path $PSScriptRoot "../src/Qir/Samples") & "$qirSamples/build-qir-samples.ps1" -SkipQSharpBuild if ($LastExitCode -ne 0) { diff --git a/build/manifest.ps1 b/build/manifest.ps1 index c36ac237180..8e1b368e8f0 100644 --- a/build/manifest.ps1 +++ b/build/manifest.ps1 @@ -54,11 +54,7 @@ $artifacts = @{ Native = @( ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.Simulator.Runtime.dll", - ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.SparseSimulator.Runtime.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.QSharp.Core.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.QSharp.Foundation.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.Runtime.dll", - ".\src\Qir\Runtime\bin\$Env:BUILD_CONFIGURATION\bin\Microsoft.Quantum.Qir.Tracer.dll" + ".\src\Simulation\Simulators\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.SparseSimulator.Runtime.dll" ) | ForEach-Object { Join-Path $PSScriptRoot (Join-Path ".." $_) }; } diff --git a/build/steps-codecheck.yml b/build/steps-codecheck.yml index d1bc37cc4f7..65c02f6c9ab 100644 --- a/build/steps-codecheck.yml +++ b/build/steps-codecheck.yml @@ -19,7 +19,7 @@ steps: displayName: "Install QIR Runtime Prerequisites" workingDirectory: $(System.DefaultWorkingDirectory) -- pwsh: src/Qir/Runtime/build-qir-runtime.ps1 +- pwsh: src/Qir/Runtime/build-qir-stdlib.ps1 displayName: "Build QIR Runtime" workingDirectory: $(System.DefaultWorkingDirectory) @@ -34,7 +34,7 @@ steps: workingDirectory: $(System.DefaultWorkingDirectory)/src/Simulation/Native # QIR Runtime Tests: -- pwsh: src/Qir/Runtime/test-qir-runtime.ps1 +- pwsh: src/Qir/Runtime/test-qir-stdlib.ps1 displayName: "Test QIR Runtime" workingDirectory: $(System.DefaultWorkingDirectory) diff --git a/build/test.ps1 b/build/test.ps1 index b1398aca751..80734c97d7d 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -43,14 +43,8 @@ function Test-One { Test-One '../Simulation.sln' if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirRuntime/test-qir-runtime.ps1" - if ($LastExitCode -ne 0) { - $script:all_ok = $False - } - - $qirRuntime = (Join-Path $PSScriptRoot "../src/Qir/riqir") - & "$qirRuntime/test-qir-stdlib.ps1" + $qirstdlib = (Join-Path $PSScriptRoot "../src/Qir/Runtime") + & "$qirstdlib/test-qir-stdlib.ps1" if ($LastExitCode -ne 0) { $script:all_ok = $False } @@ -61,12 +55,6 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { $script:all_ok = $False } - $qirTests = (Join-Path $PSScriptRoot "../src/Qir/riqir-test") - & "$qirTests/test-qir-tests.ps1" - if ($LastExitCode -ne 0) { - $script:all_ok = $False - } - $qirSamples = (Join-Path $PSScriptRoot "../src/Qir/Samples") & "$qirSamples/test-qir-samples.ps1" if ($LastExitCode -ne 0) { diff --git a/src/Qir/Common/Include/SimulatorStub.hpp b/src/Qir/Common/Include/SimulatorStub.hpp deleted file mode 100644 index b6f705345ab..00000000000 --- a/src/Qir/Common/Include/SimulatorStub.hpp +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct SimulatorStub - : public IRuntimeDriver - , public IQuantumGateSet - { - QubitIdType AllocateQubit() override - { - throw std::logic_error("not_implemented: AllocateQubit"); - } - void ReleaseQubit(QubitIdType /* qubit */) override - { - throw std::logic_error("not_implemented: ReleaseQubit"); - } - virtual std::string QubitToString(QubitIdType /* qubit */) override - { - throw std::logic_error("not_implemented: QubitToString"); - } - void X(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: X"); - } - void Y(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: Y"); - } - void Z(QubitIdType /* target */) override - { - throw std::logic_error("not_implemented: Z"); - } - void H(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: H"); - } - void S(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: S"); - } - void T(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: T"); - } - void R(PauliId /* axis */, QubitIdType /* target */, double /* theta */) override - { - throw std::logic_error("not_implemented: R"); - } - void Exp(long /* numTargets */, PauliId* /* paulis */, QubitIdType* /* targets */, double /* theta */) override - { - throw std::logic_error("not_implemented: Exp"); - } - void ControlledX(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledX"); - } - void ControlledY(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledY"); - } - void ControlledZ(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledZ"); - } - void ControlledH(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledH"); - } - void ControlledS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledS"); - } - void ControlledT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledT"); - } - void ControlledR(long /*numControls*/, QubitIdType* /*controls*/, PauliId /*axis*/, QubitIdType /*target*/, - double /*theta*/) override - { - throw std::logic_error("not_implemented: ControlledR"); - } - void ControlledExp(long /*numControls*/, QubitIdType* /*controls*/, long /*numTargets*/, PauliId* /*paulis*/, - QubitIdType* /*targets*/, double /*theta*/) override - { - throw std::logic_error("not_implemented: ControlledExp"); - } - void AdjointS(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: AdjointS"); - } - void AdjointT(QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: AdjointT"); - } - void ControlledAdjointS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledAdjointS"); - } - void ControlledAdjointT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("not_implemented: ControlledAdjointT"); - } - Result Measure(long /*numBases*/, PauliId* /*bases*/, long /*numTargets*/, QubitIdType* /*targets*/) override - { - throw std::logic_error("not_implemented: Measure"); - } - void ReleaseResult(Result /*result*/) override - { - throw std::logic_error("not_implemented: ReleaseResult"); - } - bool AreEqualResults(Result /*r1*/, Result /*r2*/) override - { - throw std::logic_error("not_implemented: AreEqualResults"); - } - ResultValue GetResultValue(Result /*result*/) override - { - throw std::logic_error("not_implemented: GetResultValue"); - } - Result UseZero() override - { - throw std::logic_error("not_implemented: UseZero"); - } - Result UseOne() override - { - throw std::logic_error("not_implemented: UseOne"); - } - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Common/Include/qsharp__foundation_internal.hpp b/src/Qir/Common/Include/qsharp__foundation_internal.hpp deleted file mode 100644 index df2e844e5a1..00000000000 --- a/src/Qir/Common/Include/qsharp__foundation_internal.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// To be included by the QIS implementation and QIS tests only. -// Not to be included by parties outside QIS. - -#include "CoreTypes.hpp" - -// For test purposes only: -namespace Quantum // Replace with `namespace Quantum::Qis::Internal` after migration to C++17. -{ -namespace Qis -{ - namespace Internal - { - QIR_SHARED_API extern char const excStrDrawRandomVal[]; - - QIR_SHARED_API void RandomizeSeed(bool randomize); - QIR_SHARED_API int64_t GetLastGeneratedRandomI64(); - QIR_SHARED_API double GetLastGeneratedRandomDouble(); - } // namespace Internal -} // namespace Qis -} // namespace Quantum diff --git a/src/Qir/Runtime/CMakeLists.txt b/src/Qir/Runtime/CMakeLists.txt deleted file mode 100644 index d5379352dd3..00000000000 --- a/src/Qir/Runtime/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -cmake_minimum_required(VERSION 3.20 FATAL_ERROR) - -message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") - -# Load common utils and configure cmake policies -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../Common/cmake") -include(secure_dependencies) -set_msvc_static_runtime_policy() - -# set the project name and version -project(qirruntime) - -# specify the C++ standard, compiler and other tools -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -locate_win32_spectre_static_runtime() -configure_security_flags() - -# feel free to customize these flags for your local builds (don't check in) -set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-inline") - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") - -set(public_includes "${PROJECT_SOURCE_DIR}/public") -set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") - -include(qir_cmake_include) - -add_subdirectory(lib) -add_subdirectory(unittests) diff --git a/src/Qir/Runtime/README.md b/src/Qir/Runtime/README.md deleted file mode 100644 index 93b36db8d79..00000000000 --- a/src/Qir/Runtime/README.md +++ /dev/null @@ -1,197 +0,0 @@ -# The Native QIR Runtime - -This folder contains the Quantum Intermediate Representation (QIR) Runtime project. The QIR is a subset of the [LLVM](https://llvm.org/) Intermediate Representation. -The QIR runtime includes an implementation of the - [QIR specification](https://github.com/qir-alliance/qir-spec) and the bridge to - run QIR against the native full state simulator. - -- `public` folder contains the public headers -- `lib` folder contains the implementation of the runtime and the simulators. -- `unittests` folder contains tests for the runtime -- `Externals` folder contains external dependencies. We'll strive to keep those minimal. - -## Build - -### Prerequisites - -The QirRuntime project is using CMake (3.17) + Ninja(1.10.0) + Clang++(14). Other versions of the tools might work - but haven't been tested. Only x64 architecture is supported. -For running the PowerShell scripts below use -[PowerShell Core or PowerShell 7+ (`pwsh`)](https://github.com/PowerShell/PowerShell), not the inbox PowerShell. - -To install prerequisite tools for building the QIR runtime, you can set the `ENABLE_QIRRUNTIME` environment variable to the string `"true"` -and run [`prerequisites.ps1`](prerequisites.ps1), or manually install pre-reqs with the steps listed below. -Note that on Windows, this script relies on the [Chocolatey package manager](https://chocolatey.org/), -while on macOS, `prerequisites.ps1` relies on the [`brew` package manager](https://brew.sh). - -#### Windows pre-reqs - -1. Install Clang 14, Ninja and CMake from the public distros. -1. Add all three to your/system `%PATH%`. -1. Install VS 2019 and enable "Desktop development with C++" component (Clang uses MSVC's standard library on Windows). -1. Install clang-tidy and clang-format if your Clang/LLVM packages didn't include the tools. -1. Install the same version of dotnet as specified by qsharp-runtime [README](../../../README.md) - -*Building from Visual Studio and VS Code is **not** supported. -Running cmake from the editors will likely default to MSVC or clang-cl and fail.* - -#### Linux via WSL pre-reqs - -1. On the host Windows machine [enable WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10) and install - Ubuntu 20.04 LTS. -1. In the Ubuntu's terminal: - 1. `$ sudo apt install cmake` (`$ cmake --version` should return 3.16.3) - 1. `$ sudo apt-get install ninja-build` (`$ ninja --version` should return 1.10.0) - 1. `$ sudo apt install clang-14` (`$ clang++-14 --version` should return 14.0.0 or newer) - 1. Set Clang as the preferred C/C++ compiler: - - $ export CC=/usr/bin/clang-14 - - $ export CXX=/usr/bin/clang++-14 - 1. `$ sudo apt install clang-tidy-14` (`$ clang-tidy-14 --version` should return 'LLVM version 14.0.0' or newer) - 1. Install the same version of dotnet as specified by qsharp-runtime [README](../../../README.md) - -See [https://code.visualstudio.com/docs/remote/wsl] on how to use VS Code with WSL. - -#### Other Prerequisites - -The build depends on `Microsoft.Quantum.Simulator.Runtime` dynamic library built at a higher level of the directory tree. -To build that library follow the instructions in [`qsharp-runtime/README.md`](../../../README.md#building-from-source) -(up to and including the step `Simulation.sln`). - - -### Build Commands - -To build QirRuntime you can run [`build-qir-runtime.ps1`](build-qir-runtime.ps1) script from QirRuntime folder: -```batch -pwsh build-qir-runtime.ps1 -``` - -The script will create the `build/{Debug|Release}` folder and place the build artifacts in it. The configuration `Debug|Release` -is specified with the `BUILD_CONFIGURATION` environment variable. -If the variable is not set then the default is specified in [`set-env.ps1`](../../../build/set-env.ps1). - -## Tests - -The tests in the `unittests` folder are those that are compiled directly against the object libraries -from the runtime, and verify behavior of the runtime using more than the public API. For tests that -verify behavior of the public API surface using compiled QIR from Q# projects, see the `src/Qir/Tests` folder. - -### Running All Tests - -```powershell -# Navigate to QirRuntime folder. - -pwsh test-qir-runtime.ps1 -``` - -### Running Test Binaries Individually - -` -help` provides details on how to run a subset of the tests and other options. For example, you can - filter tests from the "[skip]" category out by ` ~[skip]`. - -For tests that depend on the native simulator and qdk shared libraries, you might need to modify the corresponding - dynamic libraries lookup path environment variable: - -- (Windows) PATH -- (Unix) LD_LIBRARY_PATH -- (Darwin) DYLD_LIBRARY_PATH - -## QIR Bridge and Runtime - -This project contains an implementation of the QIR runtime per the - [QIR specifications](https://github.com/qir-alliance/qir-spec) and the translation - layer between the QIR and the IR, generated by Clang from the native code. Translation layer is called the "QIR Bridge". - -![QIR Bridge architecture diagram](qir.png?raw=true "QIR Bridge architecture diagram") - -This project also provides an implementation of the quantum instruction set, used by Q# for simulation against the full -state simulator: - -```llvm -operation Exp (paulis : Pauli[], theta : Double, qubits : Qubit[]) : Unit is Adj + Ctl -void @__quantum__qis__exp__body(%Array*, double, %Array*) -void @__quantum__qis__exp__adj(%Array*, double, %Array*) -void @__quantum__qis__exp__ctl(%Array*, { %Array*, double, %Array* }*) -void @__quantum__qis__exp__ctladj(%Array*, { %Array*, double, %Array* }*) -void @__quantum__qis__h__body(%Qubit*) -void @__quantum__qis__h__ctl(%Array*, %Qubit*) -%Result* @__quantum__qis__measure__body(%Array*, %Array*) -void @__quantum__qis__r__body(i2, double, %Qubit*) -void @__quantum__qis__r__adj(i2, double, %Qubit*) -void @__quantum__qis__r__ctl(%Array*, { i2, double, %Qubit* }*) -void @__quantum__qis__r__ctladj(%Array*, { i2, double, %Qubit* }*) -void @__quantum__qis__s__body(%Qubit*) -void @__quantum__qis__s__adj(%Qubit*) -void @__quantum__qis__s__ctl(%Array*, %Qubit*) -void @__quantum__qis__s__ctladj(%Array*, %Qubit*) -void @__quantum__qis__t__body(%Qubit*) -void @__quantum__qis__t__adj(%Qubit*) -void @__quantum__qis__t__ctl(%Array*, %Qubit*) -void @__quantum__qis__t__ctladj(%Array*, %Qubit*) -void @__quantum__qis__x__body(%Qubit*) -void @__quantum__qis__x__ctl(%Array*, %Qubit*) -void @__quantum__qis__y__body(%Qubit*) -void @__quantum__qis__y__ctl(%Array*, %Qubit*) -void @__quantum__qis__z__body(%Qubit*) -void @__quantum__qis__z__ctl(%Array*, %Qubit*) -``` - -There are two ways to compile and run the QIR files against the runtime. - -1. Link against the runtime libraries *statically*. For the example of this approach see `test/QIR-static` tests. It - allows the client to access the target simulator directly, if so desired. -1. Link against the *shared qdk* library. The example of this approach can be found in `test/QIR-dynamic` folder. In the - future we'll provide fully self-contained packages of the runtime to enable this workflow completely outside of the - current project. For now, this way of consuming QIR only supports running against the native full state simulator. - -QIR's architecture assumes a single target, whether that be hardware or a particular simulator. As a result, there is no - provision in the QIR specifications to choose a target dynamically. To connect QIR to the simulators from this runtime, - we provide `QirExecutionContext::Init()` (earlier `InitializeQirContext`) - and `QirExecutionContext::Deinit()` (earlier `ReleaseQirContext`) methods. - Switching contexts while executing QIR isn't supported and would yield undefined behavior. - -### Building from IR files - -CMake doesn't support using LLVM's IR files as input so instead we invoke Clang directly from custom commands to create - utility libs that can be linked into other targets using their absolute paths. - -*NB*: Compiling from IR has fewer checks than compiling from C++. For example, IR doesn't support overloading so - declarations and definitions of functions are matched by name, without taking into account the arguments. This means - that a build might succeed with mismatched signatures between caller/callee which will likely lead to crashes and other - bugs at runtime. - -**The QIR runtime is work in progress. Current known limitations are as follows:** - -1. All functionality related to BigInt type (including `__quantum__rt__bigint_to_string`) NYI. -1. QIR is assumed to be __single threaded__. No effort was made to make the bridge and runtime thread safe. -1. Strings are implemented as a thin wrapper over std::string with virtually no optimizations. -1. Variadic functions (e.g. `__quantum__rt__array_create`) require platform specific bridges. The currently implemented - bridge is for Windows. -1. Qubit borrowing NYI (needs both bridge and simulator's support). - -## Coding style and conventions - -If during compilation you see an error like this - -``` -##vso[task.logissue type=error;]Formatting check failed. The following files need to be formatted before compiling: -``` - -then this means that the edits you made violate the -[coding style](https://github.com/microsoft/qsharp-runtime/blob/main/src/Qir/.clang-format) -enforced by clang-format utility. To format the file install the -Clang-Format extension to your editor ([example for VSCode](https://clang.llvm.org/docs/ClangFormat.html#visual-studio-code-integration)), -open the file, press the corresponding formatting hot keys (for VSCode it is \), and save the file. -See more links in [.clang-format](https://github.com/microsoft/qsharp-runtime/blob/main/src/Qir/.clang-format) file. - -Most of our coding style and conventions are enforced via clang-tidy. The project is currently set up to treat - clang-tidy warnings as build errors and we'd like to keep it this way. If you absolutely need to violate the style, - mark the problematic line with `// NOLINT`. To suppress style checks in a whole folder, add .clang-tidy file into the - folder with checks reduced to `Checks: '-*,bugprone-*'`. - -Clang-tidy checks reference: [https://clang.llvm.org/extra/clang-tidy/checks/list.html] - -Conventions not covered by .clang-format and .clang-tidy: - -- fields of a class/struct must be placed at the top of the class/struct definition; -- must use `this` to access class and struct members: `this->fooBar`; -- Interface declarations should be placed in separate header files with "_I" suffix.: `FooBar_I.hpp`. diff --git a/src/Qir/riqir/backend/Cargo.toml b/src/Qir/Runtime/backend/Cargo.toml similarity index 100% rename from src/Qir/riqir/backend/Cargo.toml rename to src/Qir/Runtime/backend/Cargo.toml diff --git a/src/Qir/riqir/backend/src/common_matrices.rs b/src/Qir/Runtime/backend/src/common_matrices.rs similarity index 100% rename from src/Qir/riqir/backend/src/common_matrices.rs rename to src/Qir/Runtime/backend/src/common_matrices.rs diff --git a/src/Qir/riqir/backend/src/lib.rs b/src/Qir/Runtime/backend/src/lib.rs similarity index 100% rename from src/Qir/riqir/backend/src/lib.rs rename to src/Qir/Runtime/backend/src/lib.rs diff --git a/src/Qir/riqir/backend/src/nearly_zero.rs b/src/Qir/Runtime/backend/src/nearly_zero.rs similarity index 100% rename from src/Qir/riqir/backend/src/nearly_zero.rs rename to src/Qir/Runtime/backend/src/nearly_zero.rs diff --git a/src/Qir/riqir/backend/src/result_bool.rs b/src/Qir/Runtime/backend/src/result_bool.rs similarity index 100% rename from src/Qir/riqir/backend/src/result_bool.rs rename to src/Qir/Runtime/backend/src/result_bool.rs diff --git a/src/Qir/riqir/backend/src/simulator.rs b/src/Qir/Runtime/backend/src/simulator.rs similarity index 100% rename from src/Qir/riqir/backend/src/simulator.rs rename to src/Qir/Runtime/backend/src/simulator.rs diff --git a/src/Qir/Runtime/build-qir-runtime.ps1 b/src/Qir/Runtime/build-qir-runtime.ps1 deleted file mode 100644 index 02041c353b1..00000000000 --- a/src/Qir/Runtime/build-qir-runtime.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -. (Join-Path $PSScriptRoot .. qir-utils.ps1) - -Write-Host "##[info]Compile QIR Runtime" - -& (Join-Path $PSScriptRoot ".." check-sources-formatted.ps1) -Path $PSScriptRoot -& (Join-Path $PSScriptRoot ".." check-sources-formatted.ps1) -Path (Join-Path $PSScriptRoot ".." Common) - -if (-not (Build-CMakeProject $PSScriptRoot "QIR Runtime")) { - throw "At least one project failed to compile. Check the logs." -} - -# Copy the results of runtime compilation and the corresponding headers to the QIR drops folder so -# they can be included in pipeline artifacts. -$qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) -$qirDropsInclude = (Join-Path $Env:QIR_DROPS include) -if (-not (Test-Path $Env:QIR_DROPS)) { - New-Item -Path $Env:QIR_DROPS -ItemType "directory" - New-Item -Path $qirDropsBin -ItemType "directory" - New-Item -Path $qirDropsInclude -ItemType "directory" -} -$qirBinaries = (Join-Path $PSScriptRoot bin $Env:BUILD_CONFIGURATION bin *) -$qirIncludes = (Join-Path $PSScriptRoot public *) -Copy-Item $qirBinaries $qirDropsBin -Exclude "*unittests*","*.Tracer.*" -Copy-Item $qirIncludes $qirDropsInclude -Exclude "*.md","Tracer*" \ No newline at end of file diff --git a/src/Qir/riqir/build-qir-stdlib.ps1 b/src/Qir/Runtime/build-qir-stdlib.ps1 similarity index 100% rename from src/Qir/riqir/build-qir-stdlib.ps1 rename to src/Qir/Runtime/build-qir-stdlib.ps1 diff --git a/src/Qir/Runtime/lib/CMakeLists.txt b/src/Qir/Runtime/lib/CMakeLists.txt deleted file mode 100644 index 343d7162ebc..00000000000 --- a/src/Qir/Runtime/lib/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_subdirectory(QIR) -add_subdirectory(QSharpFoundation) -add_subdirectory(QSharpCore) -add_subdirectory(Simulators) -add_subdirectory(Tracer) diff --git a/src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp b/src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp deleted file mode 100644 index f84399ae30c..00000000000 --- a/src/Qir/Runtime/lib/QIR/BasicRuntimeDriver.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include "QirRuntimeApi_I.hpp" -#include "QubitManager.hpp" -#include "BasicRuntimeDriverFactory.h" - -namespace Microsoft -{ -namespace Quantum -{ - class CBasicRuntimeDriver : public IRuntimeDriver - { - std::unique_ptr qubitManager; - - public: - CBasicRuntimeDriver() - { - qubitManager = std::make_unique(); - } - - ~CBasicRuntimeDriver() override - { - } - - std::string QubitToString(QubitIdType q) override - { - return std::to_string(q); - } - - QubitIdType AllocateQubit() override - { - return qubitManager->Allocate(); - } - - void ReleaseQubit(QubitIdType q) override - { - qubitManager->Release(q); - } - - void ReleaseResult(Result /* result */) override - { - } - - bool AreEqualResults(Result r1, Result r2) override - { - return r1 == r2; - } - - ResultValue GetResultValue(Result r) override - { - return (r == UseZero()) ? Result_Zero : Result_One; - } - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } - }; - - extern "C" void* CreateBasicRuntimeDriver() - { - return (IRuntimeDriver*)new CBasicRuntimeDriver(); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/CMakeLists.txt b/src/Qir/Runtime/lib/QIR/CMakeLists.txt deleted file mode 100644 index 4dd5b41054e..00000000000 --- a/src/Qir/Runtime/lib/QIR/CMakeLists.txt +++ /dev/null @@ -1,60 +0,0 @@ -# The downstream consumers but must pick up both the native support lib and the utility -# lib, produced from ll bridge files when linking against either qir-rt or qir-qis. - -#+++++++++++++++++++++++++++++++++++++ -# qir-rt -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# create qir-rt-support lib from the C++ sources -# -set(rt_sup_source_files - QirOutputHandling.cpp - OutputStream.cpp - Output.cpp - allocationsTracker.cpp - arrays.cpp - callables.cpp - context.cpp - delegated.cpp - strings.cpp - utils.cpp - QubitManager.cpp - BasicRuntimeDriver.cpp -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(qir-rt-support-obj OBJECT ${rt_sup_source_files}) -target_source_from_qir(qir-rt-support-obj bridge-rt.ll) -target_include_directories(qir-rt-support-obj PUBLIC - ${public_includes} - ${common_includes} -) -set_property(TARGET qir-rt-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(qir-rt-support-obj PRIVATE EXPORT_QIR_API) - -#+++++++++++++++++++++++++++++++++++++ -# qir-qis -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.Runtime dynamic library -# -add_library(Microsoft.Quantum.Qir.Runtime SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.Runtime - ${CMAKE_DL_LIBS} - qir-rt-support-obj - ${SPECTRE_LIBS} -) - -target_include_directories(Microsoft.Quantum.Qir.Runtime PUBLIC ${public_includes}) -target_compile_definitions(Microsoft.Quantum.Qir.Runtime PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.Runtime PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.Runtime - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) diff --git a/src/Qir/Runtime/lib/QIR/Output.cpp b/src/Qir/Runtime/lib/QIR/Output.cpp deleted file mode 100644 index b07cd3da325..00000000000 --- a/src/Qir/Runtime/lib/QIR/Output.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" // QIR_SHARED_API for quantum__rt__message. -#include "OutputStream.hpp" -#include "QirOutputHandling.hpp" - -void WriteToCurrentStream(QirString* qstr); - -// Public API: -extern "C" -{ - void __quantum__rt__message(QirString* qstr) // NOLINT - { - WriteToCurrentStream(qstr); - } -} // extern "C" - - -void WriteToCurrentStream(QirString* qstr) -{ - std::ostream& currentOutputStream = Microsoft::Quantum::OutputStream::Get(); - currentOutputStream << qstr->str << QOH_REC_DELIMITER; - currentOutputStream.flush(); -} diff --git a/src/Qir/Runtime/lib/QIR/OutputStream.cpp b/src/Qir/Runtime/lib/QIR/OutputStream.cpp deleted file mode 100644 index f2d91f190c6..00000000000 --- a/src/Qir/Runtime/lib/QIR/OutputStream.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// https://stackoverflow.com/a/5419388/6362941 redirect std::cout to a string -// Discussion/history and some more info about the output redirection: -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574170031 -// https://github.com/microsoft/qsharp-runtime/pull/511#discussion_r574194191 - -#include -#include "QirRuntime.hpp" -#include "OutputStream.hpp" - -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - std::ostream* OutputStream::currentOutputStream = &std::cout; // Output to std::cout by default. - - std::ostream& OutputStream::Get() - { - return *currentOutputStream; - } - - std::ostream& OutputStream::Set(std::ostream& newOStream) - { - std::ostream& oldOStream = *currentOutputStream; - currentOutputStream = &newOStream; - return oldOStream; - } - - OutputStream::ScopedRedirector::ScopedRedirector(std::ostream& newOstream) : old(OutputStream::Set(newOstream)) - { - } - - OutputStream::ScopedRedirector::~ScopedRedirector() - { - OutputStream::Set(old); - } - - std::ostream& SetOutputStream(std::ostream& newOStream) - { - return OutputStream::Set(newOStream); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp b/src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp deleted file mode 100644 index d1d695643e3..00000000000 --- a/src/Qir/Runtime/lib/QIR/QirOutputHandling.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include "QirOutputHandling.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -extern void WriteToCurrentStream(QirString*); - -static void PrintCStr(const char* cStr) -{ - QirString msg(cStr); - WriteToCurrentStream(&msg); -} - - -extern "C" -{ - // Tuple Type Records - - void __quantum__rt__tuple_start_record_output() // NOLINT - { - PrintCStr(QOH_REC_TUPLE_START); - } - - void __quantum__rt__tuple_end_record_output() // NOLINT - { - PrintCStr(QOH_REC_TUPLE_END); - } - - - // Array Type Records - - void __quantum__rt__array_start_record_output() // NOLINT - { - PrintCStr(QOH_REC_ARRAY_START); - } - - void __quantum__rt__array_end_record_output() // NOLINT - { - PrintCStr(QOH_REC_ARRAY_END); - } - - - // Primitive Result Records - - void __quantum__rt__result_record_output(Result res) // NOLINT - { - if (__quantum__rt__result_equal(res, __quantum__rt__result_get_zero())) - { - PrintCStr(QOH_REC_RESULT_ZERO); - } - else - { - PrintCStr(QOH_REC_RESULT_ONE); - } - } - - void __quantum__rt__bool_record_output(bool isTrue) // NOLINT - { - if (!isTrue) - { - PrintCStr(QOH_REC_FALSE); - } - else - { - PrintCStr(QOH_REC_TRUE); - } - } - - void __quantum__rt__integer_record_output(int64_t i64Val) // NOLINT - { - std::stringstream strStream; - strStream << QOH_REC_PREFIX << i64Val; - PrintCStr(strStream.str().c_str()); - } - - void __quantum__rt__double_record_output(double doubleVal) // NOLINT - { - std::stringstream strStream; - strStream << QOH_REC_PREFIX << doubleVal; - PrintCStr(strStream.str().c_str()); - } - -} // extern "C" diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp deleted file mode 100644 index 8a506827e79..00000000000 --- a/src/Qir/Runtime/lib/QIR/QubitManager.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QubitManager.hpp" -#include "QirRuntime.hpp" // For quantum__rt__fail_cstr -#include // For memcpy - -namespace Microsoft -{ -namespace Quantum -{ - - // - // Failing in case of errors - // - - [[noreturn]] static void FailNow(const char* message) - { - __quantum__rt__fail_cstr(message); - } - - static void FailIf(bool condition, const char* message) - { - if (condition) - { - __quantum__rt__fail_cstr(message); - } - } - - // - // QubitListInSharedArray - // - - CQubitManager::QubitListInSharedArray::QubitListInSharedArray(QubitIdType startId, QubitIdType endId, - QubitIdType* sharedQbitStatusArray) - : firstElement(startId) - , lastElement(endId) - { - FailIf(startId > endId || startId < 0 || endId == MaximumQubitCapacity, - "Incorrect boundaries in the linked list initialization."); - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - - for (QubitIdType i = startId; i < endId; i++) - { - sharedQbitStatusArray[i] = i + 1; // Current element points to the next element. - } - sharedQbitStatusArray[endId] = NoneMarker; // Last element ends the chain. - } - - bool CQubitManager::QubitListInSharedArray::IsEmpty() const - { - return firstElement == NoneMarker; - } - - void CQubitManager::QubitListInSharedArray::AddQubit(QubitIdType id, QubitIdType* sharedQbitStatusArray) - { - FailIf(id == NoneMarker, "Incorrect qubit id, cannot add it to the list."); - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - - // If the list is empty, we initialize it with the new element. - if (IsEmpty()) - { - firstElement = id; - lastElement = id; - sharedQbitStatusArray[id] = NoneMarker; // List with a single elemenet in the chain. - return; - } - - sharedQbitStatusArray[id] = firstElement; // The new element will point to the former first element. - firstElement = id; // The new element is now the first in the chain. - } - - QubitIdType CQubitManager::QubitListInSharedArray::TakeQubitFromFront(QubitIdType* sharedQbitStatusArray) - { - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - - // First element will be returned. It is 'NoneMarker' if the list is empty. - QubitIdType result = firstElement; - - // Advance list start to the next element if list is not empty. - if (!IsEmpty()) - { - firstElement = sharedQbitStatusArray[firstElement]; // The second element will be the first. - } - - // Drop pointer to the last element if list becomes empty. - if (IsEmpty()) - { - lastElement = NoneMarker; - } - - if (result != NoneMarker) - { - sharedQbitStatusArray[result] = AllocatedMarker; - } - - return result; - } - - void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, - QubitIdType* sharedQbitStatusArray) - { - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - - // No need to do anthing if source is empty. - if (source.IsEmpty()) - { - return; - } - - if (this->IsEmpty()) - { - // If this list is empty, we'll just set it to the source list. - lastElement = source.lastElement; - } - else - { - // Attach source at the beginning of the list if both lists aren't empty. - sharedQbitStatusArray[source.lastElement] = firstElement; // The last element of the source chain will point - // to the first element of this chain. - } - firstElement = source.firstElement; // The first element from the source chain will be - // the first element of this chain. - - // Remove all elements from source. - source.firstElement = NoneMarker; - source.lastElement = NoneMarker; - } - - - // - // RestrictedReuseArea - // - - CQubitManager::RestrictedReuseArea::RestrictedReuseArea(QubitListInSharedArray freeQubits) - : FreeQubitsReuseProhibited() // Default costructor - , FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. - { - } - - - // - // CRestrictedReuseAreaStack - // - - void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) - { - FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); - this->insert(this->end(), area); - } - - CQubitManager::RestrictedReuseArea CQubitManager::CRestrictedReuseAreaStack::PopFromBack() - { - FailIf(this->empty(), "Cannot remove restricted reuse area from an empty set."); - RestrictedReuseArea result = this->back(); - this->pop_back(); - return result; - } - - CQubitManager::RestrictedReuseArea& CQubitManager::CRestrictedReuseAreaStack::PeekBack() - { - return this->back(); - } - - int32_t CQubitManager::CRestrictedReuseAreaStack::Count() const - { - // The size should never exceed int32_t. - return static_cast(this->size()); - } - - // - // CQubitManager - // - - CQubitManager::CQubitManager(QubitIdType initialQubitCapacity, bool mayExtendCap) - : mayExtendCapacity(mayExtendCap) - , qubitCapacity(initialQubitCapacity) - { - FailIf(qubitCapacity <= 0, "Qubit capacity must be positive."); - sharedQubitStatusArray = new QubitIdType[(size_t)qubitCapacity]; - - // These objects are passed by value (copies are created) - QubitListInSharedArray freeQubitsFresh(0, qubitCapacity - 1, sharedQubitStatusArray); - RestrictedReuseArea outermostArea(freeQubitsFresh); - freeQubitsInAreas.PushToBack(outermostArea); - - freeQubitCount = qubitCapacity; - } - - CQubitManager::~CQubitManager() - { - if (sharedQubitStatusArray != nullptr) - { - delete[] sharedQubitStatusArray; - sharedQubitStatusArray = nullptr; - } - // freeQubitsInAreas - direct member of the class, no need to delete. - } - - // Although it is not necessary to pass area IDs to these functions, such support may be added for extra checks. - void CQubitManager::StartRestrictedReuseArea() - { - FailIf(freeQubitsInAreas.Count() <= 0, "Internal error! No reuse areas."); - RestrictedReuseArea areaAboutToBegin; - RestrictedReuseArea& currentArea = freeQubitsInAreas.PeekBack(); - if (currentArea.FreeQubitsReuseAllowed.IsEmpty()) - { - areaAboutToBegin.prevAreaWithFreeQubits = currentArea.prevAreaWithFreeQubits; - } - else - { - areaAboutToBegin.prevAreaWithFreeQubits = freeQubitsInAreas.Count() - 1; - } - freeQubitsInAreas.PushToBack(areaAboutToBegin); - } - - void CQubitManager::NextRestrictedReuseSegment() - { - FailIf(freeQubitsInAreas.Count() <= 0, "Internal error! No reuse areas."); - FailIf(freeQubitsInAreas.Count() == 1, "NextRestrictedReuseSegment() without an active area."); - RestrictedReuseArea& currentArea = freeQubitsInAreas.PeekBack(); - // When new segment starts, reuse of all free qubits in the current area becomes prohibited. - currentArea.FreeQubitsReuseProhibited.MoveAllQubitsFrom(currentArea.FreeQubitsReuseAllowed, - sharedQubitStatusArray); - } - - void CQubitManager::EndRestrictedReuseArea() - { - FailIf(freeQubitsInAreas.Count() < 2, "EndRestrictedReuseArea() without an active area."); - RestrictedReuseArea areaAboutToEnd = freeQubitsInAreas.PopFromBack(); - RestrictedReuseArea& containingArea = freeQubitsInAreas.PeekBack(); - if (areaAboutToEnd.prevAreaWithFreeQubits < containingArea.prevAreaWithFreeQubits) - { - containingArea.prevAreaWithFreeQubits = areaAboutToEnd.prevAreaWithFreeQubits; - } - // When area ends, reuse of all free qubits from this area becomes allowed. - containingArea.FreeQubitsReuseAllowed.MoveAllQubitsFrom(areaAboutToEnd.FreeQubitsReuseProhibited, - sharedQubitStatusArray); - containingArea.FreeQubitsReuseAllowed.MoveAllQubitsFrom(areaAboutToEnd.FreeQubitsReuseAllowed, - sharedQubitStatusArray); - } - - QubitIdType CQubitManager::Allocate() - { - QubitIdType newQubitId = AllocateQubitId(); - FailIf(newQubitId == NoneMarker, "Not enough qubits."); - return newQubitId; - } - - void CQubitManager::Allocate(QubitIdType* qubitsToAllocate, int32_t qubitCountToAllocate) - { - if (qubitCountToAllocate == 0) - { - return; - } - FailIf(qubitCountToAllocate < 0, "Cannot allocate negative number of qubits."); - FailIf(qubitsToAllocate == nullptr, "No array provided for qubits to be allocated."); - - // Consider optimization for initial allocation of a large array at once - for (int32_t i = 0; i < qubitCountToAllocate; i++) - { - QubitIdType newQubitId = AllocateQubitId(); - if (newQubitId == NoneMarker) - { - for (QubitIdType k = 0; k < i; k++) - { - Release(qubitsToAllocate[k]); - } - FailNow("Not enough qubits."); - } - qubitsToAllocate[i] = newQubitId; - } - } - - void CQubitManager::Release(QubitIdType qubit) - { - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - ReleaseQubitId(qubit); - } - - void CQubitManager::Release(QubitIdType* qubitsToRelease, int32_t qubitCountToRelease) - { - if (qubitCountToRelease == 0) - { - return; - } - FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); - - for (int32_t i = 0; i < qubitCountToRelease; i++) - { - Release(qubitsToRelease[i]); - qubitsToRelease[i] = NoneMarker; - } - } - - QubitIdType CQubitManager::Borrow() - { - // We don't support true borrowing/returning at the moment. - return Allocate(); - } - - void CQubitManager::Borrow(QubitIdType* qubitsToBorrow, int32_t qubitCountToBorrow) - { - // We don't support true borrowing/returning at the moment. - return Allocate(qubitsToBorrow, qubitCountToBorrow); - } - - void CQubitManager::Return(QubitIdType qubit) - { - // We don't support true borrowing/returning at the moment. - Release(qubit); - } - - void CQubitManager::Return(QubitIdType* qubitsToReturn, int32_t qubitCountToReturn) - { - // We don't support true borrowing/returning at the moment. - Release(qubitsToReturn, qubitCountToReturn); - } - - void CQubitManager::Disable(QubitIdType qubit) - { - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - - // We can only disable explicitly allocated qubits that were not borrowed. - FailIf(!IsExplicitlyAllocatedId(qubit), "Cannot disable qubit that is not explicitly allocated."); - sharedQubitStatusArray[qubit] = DisabledMarker; - - disabledQubitCount++; - FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); - } - - void CQubitManager::Disable(QubitIdType* qubitsToDisable, int32_t qubitCountToDisable) - { - if (qubitCountToDisable == 0) - { - return; - } - FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); - - for (int32_t i = 0; i < qubitCountToDisable; i++) - { - Disable(qubitsToDisable[i]); - } - } - - bool CQubitManager::IsValidId(QubitIdType id) const - { - return (id >= 0) && (id < qubitCapacity); - } - - bool CQubitManager::IsDisabledId(QubitIdType id) const - { - return sharedQubitStatusArray[id] == DisabledMarker; - } - - bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const - { - return sharedQubitStatusArray[id] == AllocatedMarker; - } - - bool CQubitManager::IsFreeId(QubitIdType id) const - { - return sharedQubitStatusArray[id] >= 0; - } - - - bool CQubitManager::IsValidQubit(QubitIdType qubit) const - { - return IsValidId(qubit); - } - - bool CQubitManager::IsDisabledQubit(QubitIdType qubit) const - { - return IsValidQubit(qubit) && IsDisabledId(qubit); - } - - bool CQubitManager::IsExplicitlyAllocatedQubit(QubitIdType qubit) const - { - return IsValidQubit(qubit) && IsExplicitlyAllocatedId(qubit); - } - - bool CQubitManager::IsFreeQubitId(QubitIdType id) const - { - return IsValidId(id) && IsFreeId(id); - } - - void CQubitManager::EnsureCapacity(QubitIdType requestedCapacity) - { - FailIf(requestedCapacity <= 0, "Requested qubit capacity must be positive."); - if (requestedCapacity <= qubitCapacity) - { - return; - } - // We need to reallocate shared status array, but there's no need to adjust - // existing values (NonMarker or indexes in the array). - - // Prepare new shared status array - auto* newStatusArray = new QubitIdType[(size_t)requestedCapacity]; - memcpy(newStatusArray, sharedQubitStatusArray, (size_t)qubitCapacity * sizeof(newStatusArray[0])); - QubitListInSharedArray newFreeQubits(qubitCapacity, requestedCapacity - 1, newStatusArray); - - // Set new data. All fresh new qubits are added to the free qubits in the outermost area. - freeQubitCount += requestedCapacity - qubitCapacity; - FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); - delete[] sharedQubitStatusArray; - sharedQubitStatusArray = newStatusArray; - qubitCapacity = requestedCapacity; - freeQubitsInAreas[0].FreeQubitsReuseAllowed.MoveAllQubitsFrom(newFreeQubits, sharedQubitStatusArray); - } - - QubitIdType CQubitManager::TakeFreeQubitId() - { - // First we try to take qubit from the current (innermost) area. - QubitIdType id = freeQubitsInAreas.PeekBack().FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); - - // Then, if no free qubits available for reuse, we scan outer areas (if they exist). - if (id == NoneMarker && freeQubitsInAreas.Count() >= 2) - { - int32_t areaIndex = freeQubitsInAreas.Count() - 1; - do - { - areaIndex = freeQubitsInAreas[(size_t)areaIndex].prevAreaWithFreeQubits; - id = freeQubitsInAreas[(size_t)areaIndex].FreeQubitsReuseAllowed.TakeQubitFromFront( - sharedQubitStatusArray); - } while ((areaIndex != 0) && (id == NoneMarker)); - - // We remember previous area where a free qubit was found or 0 if none were found. - freeQubitsInAreas.PeekBack().prevAreaWithFreeQubits = areaIndex; - } - - if (id != NoneMarker) - { - FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Allocated invalid qubit."); - allocatedQubitCount++; - FailIf(allocatedQubitCount <= 0, "Incorrect allocated qubit count."); - freeQubitCount--; - FailIf(freeQubitCount < 0, "Incorrect free qubit count."); - } - - return id; - } - - QubitIdType CQubitManager::AllocateQubitId() - { - QubitIdType newQubitId = TakeFreeQubitId(); - if (newQubitId == NoneMarker && mayExtendCapacity) - { - QubitIdType newQubitCapacity = qubitCapacity * 2; - FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); - EnsureCapacity(newQubitCapacity); - newQubitId = TakeFreeQubitId(); - } - return newQubitId; - } - - void CQubitManager::ReleaseQubitId(QubitIdType id) - { - FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Cannot release an invalid qubit."); - if (IsDisabledId(id)) - { - // Nothing to do. Qubit will stay disabled. - return; - } - - FailIf(!IsExplicitlyAllocatedId(id), "Attempt to free qubit that has not been allocated."); - - // Released qubits are added to reuse area/segment in which they were released - // (rather than area/segment where they are allocated). - // Although counterintuitive, this makes code simple. - // This is reasonable because qubits should be allocated and released in the same segment. (This is not - // enforced) - freeQubitsInAreas.PeekBack().FreeQubitsReuseAllowed.AddQubit(id, sharedQubitStatusArray); - - freeQubitCount++; - FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/README.md b/src/Qir/Runtime/lib/QIR/README.md deleted file mode 100644 index 600d8c22d1c..00000000000 --- a/src/Qir/Runtime/lib/QIR/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Directory - -**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**public\QirRuntime.hpp** Declares `__quantum__rt__*()`. Depends on the listed earlier ones. - - -## Level 1 - -**allocationsTracker.hpp** Defines `Microsoft::Quantum::AllocationsTracker` that tracks the allocations and detects the mem leaks. - Does not depend on anything of our code. - -**utils.cpp** Implements `__quantum__rt__fail()`, `__quantum__rt__memory_allocate()`, `__quantum__rt__heap_{alloc,free}()`. - `__quantum__rt__heap_alloc()` calls **strings.cpp**'s `__quantum__rt__string_create()` - cyclical dependency. - -**strings.cpp** Implements `QirString`, `__quantum__rt__string_*()`, `__quantum__rt___to_string()` (except `qubit_to_string` and `result_to_string`). - Depends on **utils.cpp**'s `__quantum__rt__fail()` - cyclical dependency. - - -## Level 2 - -**allocationsTracker.cpp** Implements the internals of `Microsoft::Quantum::AllocationsTracker`. - Depends on `__quantum__rt__fail()`, `__quantum__rt__string_create()` - -**context.cpp** Implements the internals of `Microsoft::Quantum::QirExecutionContext`, - Depends on **allocationsTracker.hpp**'s `Microsoft::Quantum::AllocationsTracker`. - Gets/returns `IRuntimeDriver *`. - -## Level 3 - -**delegated.cpp** Implements `__quantum__rt__result_*()`, `__quantum__rt__qubit_{allocate,release,to_string}()`. - Each API depends on `Microsoft::Quantum::GlobalContext()[->GetDriver()]`, - `__quantum__rt__qubit_to_string()` also depends on strings.cpp's `__quantum__rt__string_create()`. - `__quantum__rt__result_to_string()` also depends on strings.cpp's `__quantum__rt__string_create()`. - -**arrays.cpp** Implements {the internals of `QirArray`} and `__quantum__rt__*array*`. - Depends on `Microsoft::Quantum::GlobalContext()`, `__quantum__rt__fail()`, `__quantum__rt__string_create()`, - **delegated.cpp**'s `__quantum__rt__qubit_allocate()` - -## Level 4 - -**callables.cpp** Defines the {internals of `QirTupleHeader`, `QirCallable`}, `__quantum__rt__tuple_*()`, `__quantum__rt__callable_*()` - Depends on `QirArray`, `Microsoft::Quantum::GlobalContext()`, `__quantum__rt__fail()`, `__quantum__rt__string_create()`, `TupleWithControls`, - Consider breaking up into **Tuples.cpp** and **Callables.cpp**. diff --git a/src/Qir/Runtime/lib/QIR/allocationsTracker.cpp b/src/Qir/Runtime/lib/QIR/allocationsTracker.cpp deleted file mode 100644 index e9b516c2646..00000000000 --- a/src/Qir/Runtime/lib/QIR/allocationsTracker.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "allocationsTracker.hpp" - -#include "QirRuntime.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - void AllocationsTracker::OnAllocate(void* object) - { - auto inserted = this->allocatedObjects.insert(std::make_pair(object, 1)); - if (inserted.second) - { - // first time we are allocating an object at this address, nothing to do - } - else - { - if (inserted.first->second > 0) - { - __quantum__rt__fail_cstr("Allocating an object over an existing object!"); - } - else - { - inserted.first->second = 1; - } - } - } - - void AllocationsTracker::OnAddRef(void* object) - { - auto tracked = this->allocatedObjects.find(object); - if (tracked == this->allocatedObjects.end()) - { - __quantum__rt__fail_cstr("Attempting to addref an object that isn't tracked!"); - } - else - { - if (tracked->second <= 0) - { - __quantum__rt__fail_cstr("Attempting to ressurect a previously released object!"); - } - else - { - tracked->second += 1; - } - } - } - - void AllocationsTracker::OnRelease(void* object) - { - auto tracked = this->allocatedObjects.find(object); - if (tracked == this->allocatedObjects.end()) - { - __quantum__rt__fail_cstr("Attempting to release an object that isn't tracked!"); - } - else - { - if (tracked->second <= 0) - { - __quantum__rt__fail_cstr("Attempting to release a previously released object!"); - } - else - { - tracked->second -= 1; - } - } - } - - void AllocationsTracker::CheckForLeaks() const - { - for (auto& tracked : this->allocatedObjects) - { - if (tracked.second > 0) - { - __quantum__rt__fail_cstr("Found a potentially leaked object!"); - } - } - } -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp b/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp deleted file mode 100644 index a76d772e75f..00000000000 --- a/src/Qir/Runtime/lib/QIR/allocationsTracker.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace Microsoft -{ -namespace Quantum -{ - // The tracker keeps a list of pointers to all qir objects that have been allocated during the lifetime of an - // execution context and their reference counts, which allows us to check for double-releases and leaks when the - // actual objects have been released. - struct AllocationsTracker - { - void OnAllocate(void* object); - void OnAddRef(void* object); - void OnRelease(void* object); - - void CheckForLeaks() const; - - private: - std::unordered_map allocatedObjects; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/arrays.cpp b/src/Qir/Runtime/lib/QIR/arrays.cpp deleted file mode 100644 index 5c980a9fe26..00000000000 --- a/src/Qir/Runtime/lib/QIR/arrays.cpp +++ /dev/null @@ -1,683 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include // for memcpy -#include -#include -#include -#include - -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -using namespace Microsoft::Quantum; - -int QirArray::AddRef() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAddRef(this); - } - - assert(this->refCount != 0 && "Cannot resurrect released array!"); - return ++this->refCount; -} - -// NB: Release doesn't trigger destruction of the QirArray itself to allow for it -// being used both on the stack and on the heap. The creator of the array -// should delete it, if allocated from the heap. -int QirArray::Release() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnRelease(this); - } - - assert(this->refCount != 0 && "Cannot release already released array!"); - const int rc = --this->refCount; - if (rc == 0) - { - if (ownsQubits) - { - delete[](reinterpret_cast(this->buffer)); - } - else - { - delete[] this->buffer; - } - this->buffer = nullptr; - } - return rc; -} - -QirArray::QirArray(TItemCount qubitsCount) - : count(qubitsCount) - , itemSizeInBytes((TItemSize)sizeof(void*)) - , ownsQubits(true) - , refCount(1) -{ - if (this->count > 0) - { - QubitIdType* qbuffer = new QubitIdType[count]; - for (TItemCount i = 0; i < count; i++) - { - qbuffer[i] = reinterpret_cast(__quantum__rt__qubit_allocate()); - } - this->buffer = reinterpret_cast(qbuffer); - } - else - { - this->buffer = nullptr; - } - this->dimensionSizes.push_back(this->count); - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } -} - -QirArray::QirArray(TItemCount countItems, TItemSize itemSizeBytes, TDimCount dimCount, TDimContainer&& dimSizes) - : count(countItems) - - // Each array item needs to be properly aligned. Let's align them by correcting the `itemSizeInBytes`. - , itemSizeInBytes( - ((itemSizeBytes == 1) || (itemSizeBytes == 2) || (itemSizeBytes == 4) || - ((itemSizeBytes % sizeof(size_t)) == 0) // For built-in types or multiples of architecture alignment - ) - ? itemSizeBytes // leave their natural alignment. - // Other types align on the architecture boundary `sizeof(size_t)`: - // 4 bytes on 32-bit arch, 8 on 64-bit arch. - : itemSizeBytes + sizeof(size_t) - (itemSizeBytes % sizeof(size_t))) - - , dimensions(dimCount) - , dimensionSizes(std::move(dimSizes)) - , ownsQubits(false) - , refCount(1) -{ - assert(itemSizeBytes != 0); - assert(dimCount > 0); - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } - - if (dimCount == 1) - { - assert(this->dimensionSizes.empty() || this->dimensionSizes[0] == this->count); - if (this->dimensionSizes.empty()) - { - this->dimensionSizes.push_back(countItems); - } - } - - assert(this->count * (TBufSize)itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize bufferSize = (TBufSize)this->count * (TBufSize)itemSizeInBytes; - if (bufferSize > 0) - { - this->buffer = new char[bufferSize]; - assert(bufferSize <= std::numeric_limits::max()); - memset(this->buffer, 0, (size_t)bufferSize); - } - else - { - this->buffer = nullptr; - } -} - -QirArray::QirArray(const QirArray& other) - : count(other.count) - , itemSizeInBytes(other.itemSizeInBytes) - , dimensions(other.dimensions) - , dimensionSizes(other.dimensionSizes) - , ownsQubits(false) - , refCount(1) -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } - - assert((TBufSize)(this->count) * this->itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize size = (TBufSize)this->count * (TBufSize)this->itemSizeInBytes; - if (this->count > 0) - { - this->buffer = new char[size]; - assert(size <= std::numeric_limits::max()); - memcpy(this->buffer, other.buffer, (size_t)size); - } - else - { - this->buffer = nullptr; - } -} - -QirArray::~QirArray() -{ - assert(this->buffer == nullptr); -} - -char* QirArray::GetItemPointer(TItemCount index) const -{ - assert(index < this->count); - return &this->buffer[static_cast(index * this->itemSizeInBytes)]; -} - -void QirArray::Append(const QirArray* other) -{ - assert(!this->ownsQubits); // cannot take ownership of the appended qubits, as they might be owned by somebody else - assert(this->itemSizeInBytes == other->itemSizeInBytes); - assert(this->dimensions == 1 && other->dimensions == 1); - - assert((TBufSize)(other->count) * other->itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize otherSize = (TBufSize)other->count * (TBufSize)other->itemSizeInBytes; - - if (otherSize == 0) - { - return; - } - - assert((TBufSize)(this->count) * this->itemSizeInBytes < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize thisSize = (TBufSize)this->count * (TBufSize)this->itemSizeInBytes; - - char* newBuffer = new char[thisSize + otherSize]; - if (thisSize) - { - memcpy(newBuffer, this->buffer, thisSize); - } - memcpy(&newBuffer[thisSize], other->buffer, otherSize); - - delete[] this->buffer; - this->buffer = newBuffer; - this->count += other->count; - this->dimensionSizes[0] = this->count; -} - -// We are using "row-major" layout so the linearized index into the multi-dimensional array where indexes into -// each dimension (dim_0, ... , dim_(n-1)) are i_0, i_1, ..., i_(n-1) as: -// i_0*dim_1*...*dim_(n-1) + i_1*dim_2*...*dim_(n-1) + ... + i_(n-2)*dim_(n-1) + i_(n-1) -// -// For example, 3D array with dims [5, 3, 4]: -// 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] -// 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] -// ... [24 - 35] -// ... [36 - 47] -// 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] -// index[112] ~ linear index 18 = 1*3*4 + 1*4 + 2 -static QirArray::TItemCount GetLinearIndex(const QirArray::TDimContainer& dimensionSizes, - const QirArray::TDimContainer& indexes) -{ - const size_t dimensions = dimensionSizes.size(); - QirArray::TItemCount linearIndex = 0; - QirArray::TItemCount layerSize = 1; - for (size_t i = dimensions; i > 0;) - { - --i; - linearIndex += indexes[i] * layerSize; - layerSize *= dimensionSizes[i]; - } - return linearIndex; -} - -// Calculate the length of the linear run, where the index in given `dimension` doesn't change. -// It's equal to the product of the dimension sizes in higher dimensions. -static QirArray::TItemCount RunCount(const QirArray::TDimContainer& dimensionSizes, QirArray::TDimCount dimension) -{ - assert((0 <= dimension) && ((size_t)dimension < dimensionSizes.size())); - return std::accumulate(dimensionSizes.begin() + dimension + 1, dimensionSizes.end(), (QirArray::TDimCount)1, - std::multiplies()); -} - -/*============================================================================== - Implementation of __quantum__rt__* methods for arrays -==============================================================================*/ -extern "C" -{ - QirArray* __quantum__rt__qubit_allocate_array(int64_t count) // TODO: Use `QirArray::TItemCount count` - { // (breaking change). - return new QirArray((QirArray::TItemCount)count); - } - - QirArray* __quantum__rt__qubit_borrow_array(int64_t count) - { - // Currently we implement borrowing as allocation. - return __quantum__rt__qubit_allocate_array(count); - } - - void __quantum__rt__qubit_release_array(QirArray* qa) - { - if (qa == nullptr) - { - return; - } - - assert(qa->ownsQubits); - if (qa->ownsQubits) - { - QubitIdType* qubits = reinterpret_cast(qa->buffer); - for (QirArray::TItemCount i = 0; i < qa->count; i++) - { - __quantum__rt__qubit_release(reinterpret_cast(qubits[i])); - } - } - - __quantum__rt__array_update_reference_count(qa, -1); - } - - void __quantum__rt__qubit_return_array(QirArray* qa) - { - // Currently we implement borrowing as allocation. - __quantum__rt__qubit_release_array(qa); - } - - QirArray* __quantum__rt__array_create_1d(int32_t itemSizeInBytes, int64_t countItems) - { - assert(itemSizeInBytes > 0); - return new QirArray((QirArray::TItemCount)countItems, (QirArray::TItemSize)itemSizeInBytes); - } - - // Bucketing of addref/release is non-standard so for now we'll keep the more traditional addref/release semantics - // in the native types. Should reconsider, if the perf of the loops becomes an issue. - void __quantum__rt__array_update_reference_count(QirArray* array, int32_t increment) - { - if (array == nullptr || increment == 0) - { - return; - } - else if (increment > 0) - { - for (int i = 0; i < increment; i++) - { - array->AddRef(); - } - } - else - { - for (int i = increment; i < 0; i++) - { - const long refCount = array->Release(); - if (refCount == 0) - { - delete array; - assert(i == -1 && "Attempting to decrement reference count below zero!"); - break; - } - } - } - } - - void __quantum__rt__array_update_alias_count(QirArray* array, int32_t increment) - { - if (array == nullptr || increment == 0) - { - return; - } - array->aliasCount += increment; - if (array->aliasCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Alias count cannot be negative!")); - } - } - - // TODO: Use `QirArray::TItemCount index` (breaking change): - char* __quantum__rt__array_get_element_ptr_1d(QirArray* array, int64_t index) - { - assert(array != nullptr); - return array->GetItemPointer((QirArray::TItemCount)index); - } - - // Returns the number of dimensions in the array. - int32_t __quantum__rt__array_get_dim(QirArray* array) // TODO: Return `QirArray::TDimCount` (breaking change). - { - assert(array != nullptr); - return array->dimensions; - } - - // TODO: Use `QirArray::TDimCount dim`, return `QirArray::TItemCount` (breaking change): - int64_t __quantum__rt__array_get_size(QirArray* array, int32_t dim) - { - assert(array != nullptr); - assert(dim < array->dimensions); - - return array->dimensionSizes[(size_t)dim]; - } - - int64_t __quantum__rt__array_get_size_1d(QirArray* array) - { - return __quantum__rt__array_get_size(array, 0); - } - - QirArray* __quantum__rt__array_copy(QirArray* array, bool forceNewInstance) - { - if (array == nullptr) - { - return nullptr; - } - if (forceNewInstance || array->aliasCount > 0) - { - return new QirArray(*array); - } - (void)array->AddRef(); - return array; - } - - QirArray* __quantum__rt__array_concatenate(QirArray* head, QirArray* tail) - { - assert(head != nullptr && tail != nullptr); - assert(head->dimensions == 1 && tail->dimensions == 1); - - QirArray* concatenated = new QirArray(*head); - concatenated->Append(tail); - return concatenated; - } - - // Creates a new array. The first int is the size of each element in bytes. The second int is the dimension count. - // The variable arguments should be a sequence of int64_ts contains the length of each dimension. The bytes of the - // new array should be set to zero. - // TODO: Use unsigned types (breaking change): - QirArray* __quantum__rt__array_create_nonvariadic(int itemSizeInBytes, int countDimensions, va_list dims) - { - QirArray::TDimContainer dimSizes; - dimSizes.reserve((size_t)countDimensions); - QirArray::TItemCount totalCount = 1; - - for (int i = 0; i < countDimensions; i++) - { - const QirArray::TItemCount dimSize = (QirArray::TItemCount)va_arg(dims, int64_t); - // TODO: Use `va_arg(dims, QirArray::TItemCount)`. - dimSizes.push_back(dimSize); - totalCount *= dimSize; - } - - assert(countDimensions < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler in case `countDimensions` becomes - // `QirArray::TDimCount`. - return new QirArray(totalCount, (QirArray::TItemSize)itemSizeInBytes, (QirArray::TDimCount)countDimensions, - std::move(dimSizes)); - } - - QirArray* __quantum__rt__array_create(int itemSizeInBytes, int countDimensions, ...) // NOLINT - { - va_list args; - va_start(args, countDimensions); - QirArray* array = __quantum__rt__array_create_nonvariadic(itemSizeInBytes, countDimensions, args); - va_end(args); - - return array; - } - - char* __quantum__rt__array_get_element_ptr_nonvariadic(QirArray* array, va_list args) // NOLINT - { - assert(array != nullptr); - - QirArray::TDimContainer indexes; - indexes.reserve(array->dimensions); - - for (QirArray::TDimCount i = 0; i < array->dimensions; i++) - { - indexes.push_back((QirArray::TItemCount)va_arg(args, int64_t)); - // TODO: Use `va_arg(args, QirArray::TItemCount)`. - assert(indexes.back() < array->dimensionSizes[i]); - } - - const QirArray::TItemCount linearIndex = GetLinearIndex(array->dimensionSizes, indexes); - return array->GetItemPointer(linearIndex); - } - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wvarargs" - // Returns a pointer to the indicated element of the array. The variable arguments should be a sequence of int64_ts - // that are the indices for each dimension. - char* __quantum__rt__array_get_element_ptr(QirArray* array, ...) // NOLINT - { - assert(array != nullptr); - - va_list args; - va_start(args, array->dimensions); // TODO: (Bug or hack?) Replace `array->dimensions` with `array`. - char* ptr = __quantum__rt__array_get_element_ptr_nonvariadic(array, args); - va_end(args); - - return ptr; - } -#pragma GCC diagnostic pop - - struct CheckedRange - { - int64_t start; // inclusive - int64_t step; // cannot be zero - int64_t end; // EXclusive (as opposed to `QirRange`) - int64_t width; // number of items in the range - - CheckedRange(const QirRange& r, int64_t upperBound /*exclusive*/) // lower bound assumed to be 0 (inclusive) - { - this->start = r.start; - this->step = r.step; - - if (r.step == 0) - { - throw std::runtime_error("invalid range"); - } - else if ((r.step > 0 && r.end < r.start) // Positive step and negative range. - || (r.step < 0 && r.start < r.end)) // Negative step and positive range. - { - // the QirRange generates empty sequence, normalize it - this->start = 0; - this->step = 1; - this->end = 0; - this->width = 0; - } - else if (r.step > 0) // Positive step and non-negative range. - { - this->width = (r.end - r.start + 1) / r.step // Number of full periods. - + ((r.end - r.start + 1) % r.step != 0 ? 1 : 0); // Every item is in the beginning of its - // period (also true for the last item in - // incomplete period at the end). - assert(this->width > 0); - - const int64_t lastSequenceItem = r.start + (this->width - 1) * r.step; - if (lastSequenceItem >= upperBound || r.start < 0) - { - throw std::runtime_error("range out of bounds"); - } - - this->end = lastSequenceItem + r.step; // `this->end` is EXclusive (as opposed to `QirRange`). - // `this->end` can also be `lastSequenceItem + 1`. - } - else // Negative step and non-positive range. - { // Range{10, -3, 1} == { 10, 7, 4, 1 } - // (B) Range{1, -5 , 0} = { 1 } - // (C) Range{4, -2, 0} = {4, 2, 0} - this->width = (r.end - r.start - 1) / r.step // (1 - 10 - 1) / (-3) == (-10) / (-3) == 3. - // (B) (0 - 1 - 1) / (-5) == -2 / -5 == 0. - + ((r.end - r.start - 1) % r.step != 0 ? 1 : 0); // (-10) % (-3) == -1; (-1) ? 1 : 0 == 1. - // (B) -2 % -5 = -2; -2 ? 1 : 0 == 1. - // Total: 4. - // (B) Total: 1. - assert(this->width > 0); - - const int64_t lastSequenceItem = - r.start + (this->width - 1) * r.step; // 10 + (4 - 1) * (-3) = 1. - // (B) 1 + (1 - 1)*(-5) = 1 + 0*5 = 1. - if (lastSequenceItem < 0 || r.start >= upperBound) - { - throw std::runtime_error("range out of bounds"); - } - - this->end = lastSequenceItem + r.step; // (B) 1 + (-5) = -4. - // `this->end` is EXclusive (as opposed to `QirRange`). - // `this->end` can also be `lastSequenceItem - 1`. - } - - // normalize the range of width 1, as the step doesn't matter for it - if (this->width == 1) - { - this->step = 1; - this->end = this->start + 1; - } - } - - bool IsEmpty() const - { - return this->width == 0; - } - }; - - // Creates and returns an array that is a slice of an existing array. The int indicates which dimension the slice is - // on. The %Range specifies the slice. Both ends of the range are inclusive. Negative step means the the order of - // elements should be reversed. - // TODO: Use `QirArray::TDimCount dim` (breaking change): - QirArray* quantum__rt__array_slice( // NOLINT - QirArray* array, int32_t dim, const QirRange& qirRange, - bool /*ignored: forceNewInstance*/) // https://github.com/microsoft/qsharp-language/issues/102 - // https://github.com/microsoft/qsharp-runtime/pull/830#issuecomment-925435170 - { - assert(array != nullptr); - assert(dim >= 0 && dim < array->dimensions); - - const QirArray::TItemSize itemSizeInBytes = array->itemSizeInBytes; - const QirArray::TDimCount dimensions = array->dimensions; - - const CheckedRange range(qirRange, array->dimensionSizes[(size_t)dim]); - - // If the range is empty, return an empty array of the same type but 0 items in dim dimension. - if (range.IsEmpty()) - { - QirArray::TDimContainer dims = array->dimensionSizes; - dims[(size_t)dim] = 0; - return new QirArray(0, itemSizeInBytes, dimensions, std::move(dims)); - } - - // When range covers the whole dimension, can return a copy of the array without doing any math. - if (range.step == 1 && range.start == 0 && range.end == array->dimensionSizes[(size_t)dim]) - { - return __quantum__rt__array_copy(array, true /*force*/); - } - - // Create slice array of appropriate size. - QirArray::TDimContainer sliceDims = array->dimensionSizes; - sliceDims[(size_t)dim] = (QirArray::TItemCount)(range.width); - const QirArray::TItemCount sliceItemsCount = std::accumulate( - sliceDims.begin(), sliceDims.end(), (QirArray::TItemCount)1, std::multiplies()); - QirArray* slice = new QirArray(sliceItemsCount, itemSizeInBytes, dimensions, std::move(sliceDims)); - if (nullptr == slice->buffer) - { - return slice; - } - const QirArray::TItemCount singleIndexRunCount = RunCount(array->dimensionSizes, (QirArray::TDimCount)dim); - const QirArray::TItemCount rowCount = singleIndexRunCount * array->dimensionSizes[(size_t)dim]; - - // When range is continuous, can copy data in larger chunks. For example, if the slice is on dim = 0, - // we will copy exactly once. - if (range.step == 1) - { - const QirArray::TItemCount rangeRunCount = - (QirArray::TItemCount)(singleIndexRunCount * (range.end - range.start)); - - assert((QirArray::TBufSize)rangeRunCount * itemSizeInBytes < - std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const QirArray::TBufSize rangeChunkSize = - (QirArray::TBufSize)rangeRunCount * (QirArray::TBufSize)itemSizeInBytes; - - QirArray::TItemCount dst = 0; - QirArray::TItemCount src = (QirArray::TItemCount)(singleIndexRunCount * range.start); - while (src < array->count) - { - assert(dst < slice->count); - memcpy(&slice->buffer[static_cast(dst * itemSizeInBytes)], - &array->buffer[static_cast(src * itemSizeInBytes)], rangeChunkSize); - src += rowCount; - dst += rangeRunCount; - } - return slice; - } - - // In case of disconnected or reversed range have to copy the data one run at a time. - assert((QirArray::TBufSize)singleIndexRunCount * itemSizeInBytes < - std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const QirArray::TBufSize chunkSize = - (QirArray::TBufSize)singleIndexRunCount * (QirArray::TBufSize)itemSizeInBytes; - QirArray::TItemCount dst = 0; - QirArray::TItemCount src = (QirArray::TItemCount)(singleIndexRunCount * range.start); - while (src < array->count) - { - assert(dst < slice->count); - - int64_t srcInner = src; // The `srcInner` can go negative in the end of the last iteration. - for (int64_t index = range.start; index != range.end; index += range.step) - { - assert(((QirArray::TItemSize)dst * itemSizeInBytes + (QirArray::TItemSize)chunkSize) <= - (QirArray::TItemSize)slice->count * slice->itemSizeInBytes); - assert((srcInner * (int64_t)itemSizeInBytes + (int64_t)chunkSize) <= - ((int64_t)array->count * (int64_t)array->itemSizeInBytes)); - assert(srcInner >= 0); - - memcpy(&slice->buffer[static_cast(dst * itemSizeInBytes)], - &array->buffer[static_cast(srcInner * itemSizeInBytes)], chunkSize); - srcInner += (singleIndexRunCount * range.step); - dst += singleIndexRunCount; - } - src += rowCount; - } - return slice; - } - - // Creates and returns an array that is a projection of an existing array. The int indicates which dimension the - // projection is on, and the int64_t specifies the specific index value to project. The returned Array* will have - // one fewer dimension than the existing array. - // TODO: Use `QirArray::TDimCount dim, QirArray::TItemCount index` (breaking change): - QirArray* __quantum__rt__array_project(QirArray* array, int dim, int64_t index) // NOLINT - { - assert(array != nullptr); - assert(dim >= 0 && dim < array->dimensions); - assert(array->dimensions > 1); // cannot project from 1D array into an array - assert(index >= 0 && index < array->dimensionSizes[(size_t)dim]); - - const QirArray::TItemSize itemSizeInBytes = array->itemSizeInBytes; - const QirArray::TDimCount dimensions = array->dimensions; - - // Create projected array of appropriate size. - QirArray::TDimContainer projectDims = array->dimensionSizes; - projectDims.erase(projectDims.begin() + dim); - - const QirArray::TItemCount projectItemsCount = std::accumulate( - projectDims.begin(), projectDims.end(), (QirArray::TItemCount)1, std::multiplies()); - QirArray* project = new QirArray(projectItemsCount, itemSizeInBytes, dimensions - 1, std::move(projectDims)); - if (nullptr == project->buffer) - { - return project; - } - - const QirArray::TItemCount singleIndexRunCount = RunCount(array->dimensionSizes, (QirArray::TDimCount)dim); - const QirArray::TItemCount rowCount = singleIndexRunCount * array->dimensionSizes[(size_t)dim]; - - assert((QirArray::TBufSize)singleIndexRunCount * itemSizeInBytes < - std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - - const QirArray::TBufSize chunkSize = - (QirArray::TBufSize)singleIndexRunCount * (QirArray::TBufSize)itemSizeInBytes; - - QirArray::TItemCount dst = 0; - QirArray::TItemCount src = (QirArray::TItemCount)(singleIndexRunCount * index); - while (src < array->count) - { - assert(dst < project->count); - memcpy(&project->buffer[static_cast(dst * itemSizeInBytes)], - &array->buffer[static_cast(src * itemSizeInBytes)], chunkSize); - src += rowCount; - dst += singleIndexRunCount; - } - return project; - } -} diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/lib/QIR/bridge-rt.ll deleted file mode 100644 index 8ccd51d87bf..00000000000 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ /dev/null @@ -1,43 +0,0 @@ -; Copyright (c) Microsoft Corporation. -; Licensed under the MIT License. - -%Array = type opaque -%Range = type { i64, i64, i64 } -%String = type opaque - -%"struct.QirArray" = type opaque -%"struct.QirRange" = type { i64, i64, i64 } -%"struct.QirString" = type opaque - -declare %"struct.QirArray"* @quantum__rt__array_slice(%"struct.QirArray"*, i32, %"struct.QirRange"* dereferenceable(24), - i1 %forceNewInstance) -declare %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) - -; NOTE: These three functions can be converted to extern C once the spec and compiler are updated to pass %Range by -; pointer instead of by value (see https://github.com/microsoft/qsharp-language/issues/108). Once that -; happens, this file can be removed. - -define dllexport %Array* @__quantum__rt__array_slice(%Array* %.ar, i32 %dim, %Range %.range, i1 %forceNewInstance) { - %ar = bitcast %Array* %.ar to %"struct.QirArray"* - %.prange = alloca %Range - store %Range %.range, %Range* %.prange - %range = bitcast %Range* %.prange to %"struct.QirRange"* - %slice = call %"struct.QirArray"* @quantum__rt__array_slice( - %"struct.QirArray"* %ar, i32 %dim, %"struct.QirRange"* dereferenceable(24) %range, i1 %forceNewInstance) - %.slice = bitcast %"struct.QirArray"* %slice to %Array* - ret %Array* %.slice -} - -define dllexport %Array* @__quantum__rt__array_slice_1d(%Array* %.ar, %Range %.range, i1 %forceNewInstance) { - %.slice = call %Array* @__quantum__rt__array_slice(%Array* %.ar, i32 0, %Range %.range, i1 %forceNewInstance) - ret %Array* %.slice -} - -define dllexport %String* @__quantum__rt__range_to_string(%Range %.range) { - %.prange = alloca %Range - store %Range %.range, %Range* %.prange - %range = bitcast %Range* %.prange to %"struct.QirRange"* - %str = call %"struct.QirString"* @quantum__rt__range_to_string(%"struct.QirRange"* dereferenceable(24) %range) - %.str = bitcast %"struct.QirString"* %str to %String* - ret %String* %.str -} \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QIR/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp deleted file mode 100644 index 2108d574e54..00000000000 --- a/src/Qir/Runtime/lib/QIR/callables.cpp +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include // for memcpy -#include -#include -#include -#include - -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -// Exposed to tests only: -QirTupleHeader* FlattenControlArrays(QirTupleHeader* tuple, int depth); - -using namespace Microsoft::Quantum; - -/*============================================================================== - Implementation of __quantum__rt__tuple_* and __quantum__rt__callable_* -==============================================================================*/ -extern "C" -{ - PTuple __quantum__rt__tuple_create(int64_t size) // TODO: Use unsigned integer type for param (breaking change). - { - assert((uint64_t)size < std::numeric_limits::max()); - // Using `<` rather than `<=` to calm down the compiler on 64-bit arch. - return QirTupleHeader::Create(static_cast(size))->AsTuple(); - } - - void __quantum__rt__tuple_update_reference_count(PTuple tuple, int32_t increment) - { - if (tuple == nullptr || increment == 0) - { - return; - } - - QirTupleHeader* th = QirTupleHeader::GetHeader(tuple); - if (increment > 0) - { - for (int i = 0; i < increment; i++) - { - (void)th->AddRef(); - } - } - else - { - for (int i = increment; i < 0; i++) - { - (void)th->Release(); - } - } - } - - void __quantum__rt__tuple_update_alias_count(PTuple tuple, int32_t increment) - { - if (tuple == nullptr || increment == 0) - { - return; - } - QirTupleHeader* th = QirTupleHeader::GetHeader(tuple); - th->aliasCount += increment; - - if (th->aliasCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Alias count cannot be negative")); - } - } - - PTuple __quantum__rt__tuple_copy(PTuple tuple, bool forceNewInstance) - { - if (tuple == nullptr) - { - return nullptr; - } - - QirTupleHeader* th = QirTupleHeader::GetHeader(tuple); - if (forceNewInstance || th->aliasCount > 0) - { - return QirTupleHeader::CreateWithCopiedData(th)->AsTuple(); - } - - th->AddRef(); - return tuple; - } - - void __quantum__rt__callable_update_reference_count(QirCallable* callable, int32_t increment) - { - if (callable == nullptr || increment == 0) - { - return; - } - else if (increment > 0) - { - for (int i = 0; i < increment; i++) - { - (void)callable->AddRef(); - } - } - else - { - for (int i = increment; i < 0; i++) - { - if (0 == callable->Release()) - { - assert(-1 == i && "Attempting to decrement reference count below zero!"); - break; - } - } - } - } - - void __quantum__rt__callable_update_alias_count(QirCallable* callable, int32_t increment) - { - if (callable == nullptr || increment == 0) - { - return; - } - callable->UpdateAliasCount(increment); - } - - QirCallable* __quantum__rt__callable_create(t_CallableEntry* entries, t_CaptureCallback* captureCallbacks, - PTuple capture) - { - assert(entries != nullptr); - return new QirCallable(entries, captureCallbacks, capture); - } - - void __quantum__rt__callable_invoke(QirCallable* callable, PTuple args, PTuple result) - { - assert(callable != nullptr); - callable->Invoke(args, result); - } - - QirCallable* __quantum__rt__callable_copy(QirCallable* other, bool forceNewInstance) - { - if (other == nullptr) - { - return nullptr; - } - if (forceNewInstance) - { - return new QirCallable(*other); - } - return other->CloneIfShared(); - } - - void __quantum__rt__callable_make_adjoint(QirCallable* callable) - { - assert(callable != nullptr); - callable->ApplyFunctor(QirCallable::Adjoint); - } - - void __quantum__rt__callable_make_controlled(QirCallable* callable) - { - assert(callable != nullptr); - callable->ApplyFunctor(QirCallable::Controlled); - } - - void __quantum__rt__capture_update_reference_count(QirCallable* callable, int32_t parameter) - { - callable->InvokeCaptureCallback(0, parameter); - } - - void __quantum__rt__capture_update_alias_count(QirCallable* callable, int32_t parameter) - { - callable->InvokeCaptureCallback(1, parameter); - } -} - -/*============================================================================== - Implementation of QirTupleHeader -==============================================================================*/ -int QirTupleHeader::AddRef() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAddRef(this); - } - - assert(this->refCount > 0); - return ++this->refCount; -} - -int QirTupleHeader::Release() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnRelease(this); - } - - - assert(this->refCount > 0); // doesn't guarantee we catch double releases but better than nothing - int retVal = --this->refCount; - if (this->refCount == 0) - { - PTuplePointedType* buffer = reinterpret_cast(this); - delete[] buffer; - } - return retVal; -} - -QirTupleHeader* QirTupleHeader::Create(TBufSize size) -{ - PTuplePointedType* buffer = new PTuplePointedType[sizeof(QirTupleHeader) + size]; - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(buffer); - } - - // at the beginning of the buffer place QirTupleHeader, leave the buffer uninitialized - QirTupleHeader* th = reinterpret_cast(buffer); - th->refCount = 1; - th->aliasCount = 0; - th->tupleSize = size; - - return th; -} - -QirTupleHeader* QirTupleHeader::CreateWithCopiedData(QirTupleHeader* other) -{ - const TBufSize size = other->tupleSize; - PTuplePointedType* buffer = new PTuplePointedType[sizeof(QirTupleHeader) + size]; - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(buffer); - } - - // at the beginning of the buffer place QirTupleHeader - QirTupleHeader* th = reinterpret_cast(buffer); - th->refCount = 1; - th->aliasCount = 0; - th->tupleSize = size; - - // copy the contents of the other tuple - memcpy(th->AsTuple(), other->AsTuple(), size); - - return th; -} - -/*============================================================================== - Implementation of QirCallable -==============================================================================*/ -QirCallable::~QirCallable() -{ - assert(refCount == 0); -} - -QirCallable::QirCallable(const t_CallableEntry* ftEntries, const t_CaptureCallback* callbacks, PTuple capt) - : refCount(1) - , capture(capt) - , appliedFunctor(0) - , controlledDepth(0) -{ - memcpy(this->functionTable, ftEntries, sizeof(this->functionTable)); - assert(this->functionTable[0] != nullptr); // base must be always defined - - if (callbacks != nullptr) - { - memcpy(this->captureCallbacks, callbacks, sizeof(this->captureCallbacks)); - } - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } -} - -QirCallable::QirCallable(const QirCallable& other) - : refCount(1) - , capture(other.capture) - , appliedFunctor(other.appliedFunctor) - , controlledDepth(other.controlledDepth) -{ - memcpy(this->functionTable, other.functionTable, sizeof(this->functionTable)); - memcpy(this->captureCallbacks, other.captureCallbacks, sizeof(this->captureCallbacks)); - - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAllocate(this); - } -} - -QirCallable* QirCallable::CloneIfShared() -{ - if (this->aliasCount == 0) - { - this->AddRef(); - return this; - } - return new QirCallable(*this); -} - -int QirCallable::AddRef() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnAddRef(this); - } - - int rc = ++this->refCount; - assert(rc != 1); // not allowed to resurrect! - return rc; -} - -int QirCallable::Release() -{ - if (GlobalContext() != nullptr) - { - GlobalContext()->OnRelease(this); - } - assert(this->refCount > 0); - - int rc = --this->refCount; - if (rc == 0) - { - delete this; - } - return rc; -} - -void QirCallable::UpdateAliasCount(int increment) -{ - this->aliasCount += increment; - if (this->aliasCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Alias count cannot be negative")); - } -} - -// The function _assumes_ a particular structure of the passed in tuple (see -// https://github.com/microsoft/qsharp-language/blob/main/Specifications/QIR/Callables.md) and recurses into it upto -// the given depth to create a new tuple with a combined array of controls. -// -// For example, `src` tuple might point to a tuple with the following structure (depth = 2): -// { %Array*, { %Array*, { i64, %Qubit* }* }* } -// or it might point to a tuple where the inner type isn't a tuple but a built-in type (depth = 2): -// { %Array*, { %Array*, %Qubit* }* } -// The function will create a new tuple, where the array contains elements of all nested arrays, respectively for the -// two cases will get: -// { %Array*, { i64, %Qubit* }* } -// { %Array*, %Qubit* } -// The caller is responsible for releasing both the returned tuple and the array it contains. -// The order of the elements in the array is unspecified. -QirTupleHeader* FlattenControlArrays(QirTupleHeader* tuple, int depth) -{ - assert(depth > 1); // no need to unpack at depth 1, and should avoid allocating unnecessary tuples - - const QirArray::TItemSize qubitSize = - sizeof(/*Qubit*/ void*); // Compiler complains for `sizeof(Qubit)`: - // warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate - // [bugprone-sizeof-expression]. To be fixed when the `Qubit` is made fixed-size type. - - TupleWithControls* outer = TupleWithControls::FromTupleHeader(tuple); - - // Discover, how many controls there are in total so can allocate a correctly sized array for them. - QirArray::TItemCount cControls = 0; - TupleWithControls* current = outer; - for (int i = 0; i < depth; i++) - { - assert(i == depth - 1 || current->GetHeader()->tupleSize == sizeof(TupleWithControls)); - QirArray* controls = current->controls; - assert(controls->itemSizeInBytes == qubitSize); - cControls += controls->count; - current = current->innerTuple; - } - - // Copy the controls into the new array. This array doesn't own the qubits so must use the generic constructor. - QirArray* combinedControls = new QirArray(cControls, qubitSize); - char* dst = combinedControls->buffer; - [[maybe_unused]] const char* dstEnd = dst + static_cast(qubitSize * cControls); - current = outer; - QirTupleHeader* last = nullptr; - for (int i = 0; i < depth; i++) - { - if (i == depth - 1) - { - last = current->GetHeader(); - } - - QirArray* controls = current->controls; - - const QirArray::TBufSize blockSize = (QirArray::TBufSize)qubitSize * (QirArray::TBufSize)controls->count; - // Make sure we don't overflow `TBufSize` on 32-bit arch: - assert((blockSize >= qubitSize) && (blockSize >= controls->count)); - - assert(dst + blockSize <= dstEnd); - memcpy(dst, controls->buffer, blockSize); - dst += blockSize; - // in the last iteration the innerTuple isn't valid, but we are not going to use it - current = current->innerTuple; - } - - // Create the new tuple with the flattened controls array and args from the `last` tuple. - QirTupleHeader* flatTuple = QirTupleHeader::CreateWithCopiedData(last); - QirArray** arr = reinterpret_cast(flatTuple->AsTuple()); - *arr = combinedControls; - - return flatTuple; -} - -void QirCallable::Invoke(PTuple args, PTuple result) -{ - assert(this->appliedFunctor < QirCallable::TableSize); - if (this->controlledDepth < 2) - { - // For uncontrolled or singly-controlled callables, the `args` tuple is "flat" and can be passed directly to the - // implementing function. - this->functionTable[this->appliedFunctor](capture, args, result); - } - else - { - // Must unpack the `args` tuple into a new tuple with flattened controls. - QirTupleHeader* flat = FlattenControlArrays(QirTupleHeader::GetHeader(args), this->controlledDepth); - this->functionTable[this->appliedFunctor](capture, flat->AsTuple(), result); - - QirArray* controls = *reinterpret_cast(flat->AsTuple()); - __quantum__rt__array_update_reference_count(controls, -1); - flat->Release(); - } -} - -void QirCallable::Invoke() -{ - assert((this->appliedFunctor & QirCallable::Controlled) == 0 && "Cannot invoke controlled callable without args"); - PTuple args = __quantum__rt__tuple_create(0); - this->Invoke(args, nullptr); - __quantum__rt__tuple_update_reference_count(args, -1); -} - -// A + A = I; A + C = C + A = CA; C + C = C; CA + A = C; CA + C = CA -void QirCallable::ApplyFunctor(int functor) -{ - assert(functor == QirCallable::Adjoint || functor == QirCallable::Controlled); - - if (functor == QirCallable::Adjoint) - { - this->appliedFunctor ^= QirCallable::Adjoint; - if (this->functionTable[this->appliedFunctor] == nullptr) - { - this->appliedFunctor ^= QirCallable::Adjoint; - __quantum__rt__fail(__quantum__rt__string_create("The callable doesn't provide adjoint operation")); - } - } - else if (functor == QirCallable::Controlled) - { - this->appliedFunctor |= QirCallable::Controlled; - if (this->functionTable[this->appliedFunctor] == nullptr) - { - if (this->controlledDepth == 0) - { - this->appliedFunctor ^= QirCallable::Controlled; - } - __quantum__rt__fail(__quantum__rt__string_create("The callable doesn't provide controlled operation")); - } - this->controlledDepth++; - } -} - -void QirCallable::InvokeCaptureCallback(int32_t index, int32_t parameter) -{ - assert(index >= 0 && index < QirCallable::CaptureCallbacksTableSize && "Capture callback index out of range"); - - if (this->captureCallbacks[index] != nullptr) - { - this->captureCallbacks[index](this->capture, parameter); - } -} diff --git a/src/Qir/Runtime/lib/QIR/context.cpp b/src/Qir/Runtime/lib/QIR/context.cpp deleted file mode 100644 index d19d886604b..00000000000 --- a/src/Qir/Runtime/lib/QIR/context.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include - -#include "QirContext.hpp" -#include "QirContext.h" - -#include "CoreTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "allocationsTracker.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - std::unique_ptr g_context = nullptr; - std::unique_ptr& GlobalContext() - { - return g_context; - } - - void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects) - { - assert(g_context == nullptr); - g_context = std::make_unique(driver, trackAllocatedObjects); - } - - extern "C" void InitializeQirContext(void* driver, bool trackAllocatedObjects) - { - InitializeQirContext((IRuntimeDriver*)driver, trackAllocatedObjects); - } - - void ReleaseQirContext() - { - assert(g_context != nullptr); - - if (g_context->trackAllocatedObjects) - { - g_context->allocationsTracker->CheckForLeaks(); - } - - g_context.reset(nullptr); - } - - QirExecutionContext::QirExecutionContext(IRuntimeDriver* drv, bool trackAllocatedObj) - : driver(drv) - , trackAllocatedObjects(trackAllocatedObj) - { - if (this->trackAllocatedObjects) - { - this->allocationsTracker = std::make_unique(); - } - } - - // If we just remove this user-declared-and-defined dtor - // then it will be automatically defined together with the class definition, - // which will require the `AllocationsTracker` to be a complete type - // everywhere where `public/QirContext.hpp` is included - // (we'll have to move `allocationsTracker.hpp` to `public/`). - QirExecutionContext::~QirExecutionContext() = default; - - void QirExecutionContext::Init(IRuntimeDriver* driver, bool trackAllocatedObjects /*= false*/) - { - assert(GlobalContext() == nullptr); - GlobalContext() = std::make_unique(driver, trackAllocatedObjects); - } - - void QirExecutionContext::Deinit() - { - assert(GlobalContext() != nullptr); - - if (GlobalContext()->trackAllocatedObjects) - { - GlobalContext()->allocationsTracker->CheckForLeaks(); - } - - GlobalContext().reset(nullptr); - } - - void QirExecutionContext::OnAddRef(void* object) - { - if (trackAllocatedObjects) - { - this->allocationsTracker->OnAddRef(object); - } - } - - void QirExecutionContext::OnRelease(void* object) - { - if (this->trackAllocatedObjects) - { - this->allocationsTracker->OnRelease(object); - } - } - - void QirExecutionContext::OnAllocate(void* object) - { - if (this->trackAllocatedObjects) - { - this->allocationsTracker->OnAllocate(object); - } - } - - IRuntimeDriver* QirExecutionContext::GetDriver() const - { - return this->driver; - } - - QirExecutionContext::Scoped::Scoped(IRuntimeDriver* drv, bool trackAllocatedObj /*= false*/) - { - QirExecutionContext::Init(drv, trackAllocatedObj); - } - - QirExecutionContext::Scoped::~Scoped() - { - QirExecutionContext::Deinit(); - } - - QirContextScope::QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObj /*= false*/) - { - InitializeQirContext(driver, trackAllocatedObj); - } - - QirContextScope::~QirContextScope() - { - ReleaseQirContext(); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/QIR/delegated.cpp b/src/Qir/Runtime/lib/QIR/delegated.cpp deleted file mode 100644 index 30c41fe19f8..00000000000 --- a/src/Qir/Runtime/lib/QIR/delegated.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include "QirRuntime.hpp" - -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" - -/*============================================================================= - Note: QIR assumes a single global execution context! -=============================================================================*/ - -// QIR specification requires the Result type to be reference counted, even though Results are created by the target and -// qubits, created by the same target, aren't reference counted. To minimize the implementation burden on the target, -// the runtime will track the reference counts for results. The trade-off is the performance penalty of such external -// tracking. The design should be evaluated against real user code when we have it. -static std::unordered_map& AllocatedResults() -{ - static std::unordered_map allocatedResults; - return allocatedResults; -} - -static Microsoft::Quantum::IRestrictedAreaManagement* RestrictedAreaManagement() -{ - return dynamic_cast( - Microsoft::Quantum::GlobalContext()->GetDriver()); -} - -extern "C" -{ - Result __quantum__rt__result_get_zero() - { - return Microsoft::Quantum::GlobalContext()->GetDriver()->UseZero(); - } - - Result __quantum__rt__result_get_one() - { - return Microsoft::Quantum::GlobalContext()->GetDriver()->UseOne(); - } - - QUBIT* __quantum__rt__qubit_allocate() - { - return reinterpret_cast(Microsoft::Quantum::GlobalContext()->GetDriver()->AllocateQubit()); - } - - void __quantum__rt__qubit_release(QUBIT* qubit) - { - Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseQubit(reinterpret_cast(qubit)); - } - - QUBIT* __quantum__rt__qubit_borrow() - { - // Currently we implement borrowing as allocation. - return __quantum__rt__qubit_allocate(); - } - - void __quantum__rt__qubit_return(QUBIT* qubit) - { - // Currently we implement borrowing as allocation. - __quantum__rt__qubit_release(qubit); - } - - void __quantum__rt__qubit_restricted_reuse_area_start() - { - RestrictedAreaManagement()->StartArea(); - } - - void __quantum__rt__qubit_restricted_reuse_segment_next() - { - RestrictedAreaManagement()->NextSegment(); - } - - void __quantum__rt__qubit_restricted_reuse_area_end() - { - RestrictedAreaManagement()->EndArea(); - } - - void __quantum__rt__result_update_reference_count(RESULT* r, int32_t increment) - { - if (increment == 0) - { - return; // Inefficient QIR? But no harm. - } - else if (increment > 0) - { - // If we don't have the result in our map, assume it has been allocated by a measurement with refcount = 1, - // and this is the first attempt to share it. - std::unordered_map& trackedResults = AllocatedResults(); - auto rit = trackedResults.find(r); - if (rit == trackedResults.end()) - { - trackedResults[r] = 1 + increment; - } - else - { - rit->second += increment; - } - } - else - { - // If we don't have the result in our map, assume it has been never shared, so it's reference count is 1. - std::unordered_map& trackedResults = AllocatedResults(); - auto rit = trackedResults.find(r); - if (rit == trackedResults.end()) - { - assert(increment == -1); - Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseResult(r); - } - else - { - const int newRefcount = rit->second + increment; - assert(newRefcount >= 0); - if (newRefcount == 0) - { - trackedResults.erase(rit); - Microsoft::Quantum::GlobalContext()->GetDriver()->ReleaseResult(r); - } - else - { - rit->second = newRefcount; - } - } - } - } - - bool __quantum__rt__result_equal(RESULT* r1, RESULT* r2) // NOLINT - { - if (r1 == r2) - { - return true; - } - return Microsoft::Quantum::GlobalContext()->GetDriver()->AreEqualResults(r1, r2); - } - - // Returns a string representation of the result. - QirString* __quantum__rt__result_to_string(RESULT* result) // NOLINT - { - ResultValue rv = Microsoft::Quantum::GlobalContext()->GetDriver()->GetResultValue(result); - assert(rv != Result_Pending); - - return (rv == Result_Zero) ? __quantum__rt__string_create("Zero") : __quantum__rt__string_create("One"); - } - - // Returns a string representation of the qubit. - QirString* __quantum__rt__qubit_to_string(QUBIT* qubit) // NOLINT - { - return __quantum__rt__string_create(Microsoft::Quantum::GlobalContext() - ->GetDriver() - ->QubitToString(reinterpret_cast(qubit)) - .c_str()); - } -} diff --git a/src/Qir/Runtime/lib/QIR/strings.cpp b/src/Qir/Runtime/lib/QIR/strings.cpp deleted file mode 100644 index 7fc034bce7c..00000000000 --- a/src/Qir/Runtime/lib/QIR/strings.cpp +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -// Exposed to tests only: -std::unordered_map& AllocatedStrings(); - - -std::unordered_map& AllocatedStrings() // Cannot be static, is called by tests. -{ - static std::unordered_map allocatedStrings; - return allocatedStrings; -} - -static QirString* CreateOrReuseAlreadyAllocated(std::string&& str) -{ - QirString* qstr = nullptr; - auto alreadyAllocated = AllocatedStrings().find(str); - if (alreadyAllocated != AllocatedStrings().end()) - { - qstr = alreadyAllocated->second; - assert(qstr->str == str); - qstr->refCount++; - } - else - { - qstr = new QirString(std::move(str)); - AllocatedStrings()[qstr->str] = qstr; - } - return qstr; -} - -QirString::QirString(std::string&& otherStr) - // clang-format off - : str(std::move(otherStr)) -// clang-format on -{ -} - -QirString::QirString(const char* cstr) - // clang-format off - : str(cstr) -// clang-format on -{ -} - - -extern "C" -{ - // Creates a string from an array of UTF-8 bytes. - QirString* __quantum__rt__string_create(const char* bytes) // NOLINT - { - return CreateOrReuseAlreadyAllocated(std::string(bytes)); - } - - void __quantum__rt__string_update_reference_count(QirString* qstr, int32_t increment) // NOLINT - { - if (qstr == nullptr || increment == 0) - { - return; - } - - assert(qstr->refCount > 0 && "The string has been already released!"); - qstr->refCount += increment; - - if (qstr->refCount < 0) - { - __quantum__rt__fail(__quantum__rt__string_create("Attempting to decrement reference count below zero!")); - } - else if (qstr->refCount == 0) - { - auto allocated = AllocatedStrings().find(qstr->str); - assert(allocated != AllocatedStrings().end()); - // TODO: consider amortizing map cleanup across multiple iterations - AllocatedStrings().erase(allocated); - delete qstr; - } - } - - // Creates a new string that is the concatenation of the two argument strings. - QirString* __quantum__rt__string_concatenate(QirString* left, QirString* right) // NOLINT - { - return CreateOrReuseAlreadyAllocated(left->str + right->str); - } - - // Returns true if the two strings are equal, false otherwise. - bool __quantum__rt__string_equal(QirString* left, QirString* right) // NOLINT - { - assert((left == right) == (left->str == right->str)); - return left == right; - } - - // Returns a string representation of the integer. - QirString* __quantum__rt__int_to_string(int64_t value) // NOLINT - { - return CreateOrReuseAlreadyAllocated(std::to_string(value)); - } - - // Returns a string representation of the double. - QirString* __quantum__rt__double_to_string(double value) // NOLINT - { - std::ostringstream oss; - oss.precision(std::numeric_limits::max_digits10); - oss << std::fixed << value; - std::string str = oss.str(); - - // Remove padding zeros from the decimal part (relies on the fact that the output for integers always contains - // period). - std::size_t pos1 = str.find_last_not_of('0'); - if (pos1 != std::string::npos) - { - str.erase(pos1 + 1); - } - // For readability don't end with "." -- always have at least one digit in the decimal part. - if (str[str.size() - 1] == '.') - { - str.append("0"); - } - - return CreateOrReuseAlreadyAllocated(std::move(str)); - } - - // Returns a string representation of the Boolean. - QirString* __quantum__rt__bool_to_string(bool value) // NOLINT - { - std::string str = value ? "true" : "false"; - return CreateOrReuseAlreadyAllocated(std::move(str)); - } - - // Returns a string representation of the Pauli. - QirString* __quantum__rt__pauli_to_string(PauliId pauli) // NOLINT - { - switch (pauli) - { - case PauliId_I: - return __quantum__rt__string_create("PauliI"); - case PauliId_X: - return __quantum__rt__string_create("PauliX"); - case PauliId_Y: - return __quantum__rt__string_create("PauliY"); - case PauliId_Z: - return __quantum__rt__string_create("PauliZ"); - default: - break; - } - return __quantum__rt__string_create(""); - } - - // Returns a string representation of the range. - QirString* quantum__rt__range_to_string(const QirRange& range) // NOLINT - { - std::ostringstream oss; - oss << range.start << ".."; - if (range.step != 1) - { - oss << range.step << ".."; - } - oss << range.end; - - return __quantum__rt__string_create(oss.str().c_str()); - } - - const char* __quantum__rt__string_get_data(QirString* str) // NOLINT - { - return str->str.c_str(); - } - - uint32_t __quantum__rt__string_get_length(QirString* str) // NOLINT - { - return (uint32_t)(str->str.size()); - } - - // Implemented in delegated.cpp: - // QirString* __quantum__rt__qubit_to_string(QUBIT* qubit); // NOLINT - - // Returns a string representation of the big integer. - // TODO QirString* __quantum__rt__bigint_to_string(QirBigInt*); // NOLINT -} diff --git a/src/Qir/Runtime/lib/QIR/utils.cpp b/src/Qir/Runtime/lib/QIR/utils.cpp deleted file mode 100644 index 82b18700108..00000000000 --- a/src/Qir/Runtime/lib/QIR/utils.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include -#include - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" -#include "OutputStream.hpp" - - -extern "C" -{ - char* __quantum__rt__memory_allocate(uint64_t size) - { - return (char*)malloc((size_t)size); - } - - // Fail the computation with the given error message. - void __quantum__rt__fail(QirString* msg) // NOLINT - { - __quantum__rt__fail_cstr(msg->str.c_str()); - } - - void __quantum__rt__fail_cstr(const char* cstr) - { - Microsoft::Quantum::OutputStream::Get() << cstr << std::endl; - Microsoft::Quantum::OutputStream::Get().flush(); - throw std::runtime_error(cstr); - } -} diff --git a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt deleted file mode 100644 index 7bd86a74662..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -#+++++++++++++++++++++++++++++++++++++ -# qsharp-core-qis -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# create qsharp-core-qis-support lib from the C++ sources -# -set(qsharp_core_sup_source_files - intrinsicsDump.cpp - intrinsics.cpp -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(qsharp-core-qis-support-obj OBJECT ${qsharp_core_sup_source_files}) -target_include_directories(qsharp-core-qis-support-obj PUBLIC ${public_includes}) -set_property(TARGET qsharp-core-qis-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(qsharp-core-qis-support-obj PUBLIC EXPORT_QIR_API) - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.QSharp.Core dynamic library -# -add_library(Microsoft.Quantum.Qir.QSharp.Core SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.QSharp.Core - ${CMAKE_DL_LIBS} - qsharp-core-qis-support-obj - simulators-obj - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - ${SPECTRE_LIBS} -) -add_dependencies(Microsoft.Quantum.Qir.QSharp.Core Microsoft.Quantum.Qir.Runtime) - -target_include_directories(Microsoft.Quantum.Qir.QSharp.Core PUBLIC ${public_includes}) -target_compile_definitions(Microsoft.Quantum.Qir.QSharp.Core PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.QSharp.Core PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.QSharp.Core - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) - diff --git a/src/Qir/Runtime/lib/QSharpCore/README.md b/src/Qir/Runtime/lib/QSharpCore/README.md deleted file mode 100644 index 1a80606d70a..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Directory - -**public\CoreTypes.hpp** (QUBIT, PauliId, RESULT)[, `public\QirTypes.hpp` (QirArray)]. -**public\QirContext.hpp** Declares `GlobalContext()`. -**public\QSharpSimApi_I.hpp** - Defines `IQuantumGateSet`. - -## Level 1 - -**qsharp__core__qis.hpp** Declares `__quantum__qis__*()` gate set implementations. - Depends on `public\CoreTypes.hpp` (QUBIT, PauliId, RESULT) - Uses `QirArray *` from `public\QirTypes.hpp`. - -**intrinsics.cpp** Defines `__quantum__qis__*()` gate set implementation. - Each API depends on `GlobalContext()`, `IQuantumGateSet`. diff --git a/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp b/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp deleted file mode 100644 index b9fdeb2e6e0..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/intrinsics.cpp +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/*============================================================================= - QIR assumes a single global execution context. - To support the dispatch over the qir-bridge, the clients must register their - Microsoft::Quantum::IRuntimeDriver* first. -=============================================================================*/ -#include -#include - -#include "qsharp__core__qis.hpp" - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" - -// Pauli consts are {i2} in QIR, likely stored as {i8} in arrays, but we are using the standard C++ enum type based on -// {i32} so cannot pass through the buffer and have to allocate a new one instead and copy. -static std::vector ExtractPauliIds(QirArray* paulis) -{ - const QirArray::TItemCount count = paulis->count; - std::vector pauliIds; - pauliIds.reserve(count); - for (QirArray::TItemCount i = 0; i < count; i++) - { - pauliIds.push_back(static_cast(*paulis->GetItemPointer(i))); - } - return pauliIds; -} - -static Microsoft::Quantum::IQuantumGateSet* GateSet() -{ - return dynamic_cast(Microsoft::Quantum::GlobalContext()->GetDriver()); -} - -extern "C" -{ - void __quantum__qis__exp__body(QirArray* paulis, double angle, QirArray* qubits) - { - assert(paulis->count == qubits->count); - - std::vector pauliIds = ExtractPauliIds(paulis); - GateSet()->Exp((long)(paulis->count), reinterpret_cast(pauliIds.data()), - reinterpret_cast(qubits->buffer), angle); - } - - void __quantum__qis__exp__adj(QirArray* paulis, double angle, QirArray* qubits) - { - __quantum__qis__exp__body(paulis, -angle, qubits); - } - - void __quantum__qis__exp__ctl(QirArray* ctls, QirExpTuple* args) - { - assert(args->paulis->count == args->targets->count); - - std::vector pauliIds = ExtractPauliIds(args->paulis); - GateSet()->ControlledExp((long)(ctls->count), reinterpret_cast(ctls->buffer), - (long)(args->paulis->count), reinterpret_cast(pauliIds.data()), - reinterpret_cast(args->targets->buffer), args->angle); - } - - void __quantum__qis__exp__ctladj(QirArray* ctls, QirExpTuple* args) - { - assert(args->paulis->count == args->targets->count); - - QirExpTuple updatedArgs = {args->paulis, -(args->angle), args->targets}; - - __quantum__qis__exp__ctl(ctls, &updatedArgs); - } - - void __quantum__qis__h__body(QUBIT* qubit) - { - GateSet()->H(reinterpret_cast(qubit)); - } - - void __quantum__qis__h__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledH((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - Result __quantum__qis__measure__body(QirArray* paulis, QirArray* qubits) - { - const QirArray::TItemCount count = qubits->count; - assert(count == paulis->count); - - std::vector pauliIds = ExtractPauliIds(paulis); - return GateSet()->Measure((long)(count), reinterpret_cast(pauliIds.data()), (long)(count), - reinterpret_cast(qubits->buffer)); - } - - void __quantum__qis__r__body(PauliId axis, double angle, QUBIT* qubit) - { - GateSet()->R(axis, reinterpret_cast(qubit), angle); - } - - void __quantum__qis__r__adj(PauliId axis, double angle, QUBIT* qubit) - { - __quantum__qis__r__body(axis, -angle, qubit); - } - - void __quantum__qis__r__ctl(QirArray* ctls, QirRTuple* args) - { - GateSet()->ControlledR((long)(ctls->count), reinterpret_cast(ctls->buffer), args->pauli, - reinterpret_cast(args->target), args->angle); - } - - void __quantum__qis__r__ctladj(QirArray* ctls, QirRTuple* args) - { - GateSet()->ControlledR((long)(ctls->count), reinterpret_cast(ctls->buffer), args->pauli, - reinterpret_cast(args->target), -(args->angle)); - } - - void __quantum__qis__s__body(QUBIT* qubit) - { - GateSet()->S(reinterpret_cast(qubit)); - } - - void __quantum__qis__s__adj(QUBIT* qubit) - { - GateSet()->AdjointS(reinterpret_cast(qubit)); - } - - void __quantum__qis__s__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledS((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__s__ctladj(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledAdjointS((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__t__body(QUBIT* qubit) - { - GateSet()->T(reinterpret_cast(qubit)); - } - - void __quantum__qis__t__adj(QUBIT* qubit) - { - GateSet()->AdjointT(reinterpret_cast(qubit)); - } - - void __quantum__qis__t__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledT((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__t__ctladj(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledAdjointT((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__x__body(QUBIT* qubit) - { - GateSet()->X(reinterpret_cast(qubit)); - } - - void __quantum__qis__x__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledX((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__y__body(QUBIT* qubit) - { - GateSet()->Y(reinterpret_cast(qubit)); - } - - void __quantum__qis__y__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledY((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } - - void __quantum__qis__z__body(QUBIT* qubit) - { - GateSet()->Z(reinterpret_cast(qubit)); - } - - void __quantum__qis__z__ctl(QirArray* ctls, QUBIT* qubit) - { - GateSet()->ControlledZ((long)(ctls->count), reinterpret_cast(ctls->buffer), - reinterpret_cast(qubit)); - } -} diff --git a/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp b/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp deleted file mode 100644 index 4d6dd714c5e..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/intrinsicsDump.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "QirContext.hpp" -#include "QSharpSimApi_I.hpp" -#include "QirRuntimeApi_I.hpp" -#include "qsharp__core__qis.hpp" - -static Microsoft::Quantum::IDiagnostics* GetDiagnostics() -{ - return dynamic_cast(Microsoft::Quantum::GlobalContext()->GetDriver()); -} - -// Implementation: -extern "C" -{ - void __quantum__qis__dumpmachine__body(uint8_t* location) - { - GetDiagnostics()->DumpMachine(location); - } - - void __quantum__qis__dumpregister__body(uint8_t* location, const QirArray* qubits) - { - GetDiagnostics()->DumpRegister(location, qubits); - } -} // extern "C" diff --git a/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp b/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp deleted file mode 100644 index ebbd1a5987c..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/qsharp__core__qis.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "CoreTypes.hpp" // QUBIT, PauliId, RESULT - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -struct QirArray; - -struct QirRTuple -{ - PauliId pauli; - double angle; - QUBIT* target; -}; - -struct QirExpTuple -{ - QirArray* paulis; - double angle; - QirArray* targets; -}; - -/* - Methods from __quantum__qis namespace are specific to the target. When QIR is generated it might limit or extend - the set of intrinsics, supported by the target (known to QIR generator at compile time). This provides the - implementation of the QSharp.Core intrinsics that redirect to IQuantumGateSet. -*/ -extern "C" -{ - // Q# Gate Set - QIR_SHARED_API void __quantum__qis__exp__body(QirArray*, double, QirArray*); // NOLINT - QIR_SHARED_API void __quantum__qis__exp__adj(QirArray*, double, QirArray*); // NOLINT - QIR_SHARED_API void __quantum__qis__exp__ctl(QirArray*, QirExpTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__exp__ctladj(QirArray*, QirExpTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__h__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__h__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API RESULT* __quantum__qis__measure__body(QirArray*, QirArray*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__body(PauliId, double, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__adj(PauliId, double, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__ctl(QirArray*, QirRTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__r__ctladj(QirArray*, QirRTuple*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__adj(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__s__ctladj(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__adj(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__t__ctladj(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__x__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__x__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__y__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__y__ctl(QirArray*, QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__z__body(QUBIT*); // NOLINT - QIR_SHARED_API void __quantum__qis__z__ctl(QirArray*, QUBIT*); // NOLINT - - // Q# Dump: - // Note: The param `location` must be `const void*`, - // but it is called from .ll, where `const void*` is not supported. - QIR_SHARED_API void __quantum__qis__dumpmachine__body(uint8_t* location); // NOLINT - QIR_SHARED_API void __quantum__qis__dumpregister__body(uint8_t* location, const QirArray* qubits); // NOLINT -} diff --git a/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp b/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp deleted file mode 100644 index e82b578e80e..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "QirRuntime.hpp" -#include "QSharpSimApi_I.hpp" -#include "QirRuntimeApi_I.hpp" -#include "QirContext.hpp" - -#include "qsharp__foundation__qis.hpp" - -using namespace Microsoft::Quantum; - -static IDiagnostics* GetDiagnostics() -{ - return dynamic_cast(GlobalContext()->GetDriver()); -} - -// Implementation: -extern "C" -{ - void __quantum__qis__assertmeasurementprobability__body(QirArray* bases, QirArray* qubits, RESULT* result, - double prob, QirString* msg, double tol) - { - if (bases->count != qubits->count) - { - __quantum__rt__fail_cstr("Both input arrays - bases, qubits - for AssertMeasurementProbability(), " - "must be of same size."); - } - - IRuntimeDriver* driver = GlobalContext()->GetDriver(); - if (driver->AreEqualResults(result, driver->UseOne())) - { - prob = 1.0 - prob; - } - - // Convert paulis from sequence of bytes to sequence of PauliId: - std::vector paulis(bases->count); - for (QirArray::TItemCount i = 0; i < bases->count; ++i) - { - paulis[i] = (PauliId) * (bases->GetItemPointer(i)); - } - - if (!GetDiagnostics()->AssertProbability((long)qubits->count, paulis.data(), - reinterpret_cast(qubits->GetItemPointer(0)), prob, tol, - nullptr)) - { - __quantum__rt__fail(msg); - } - } - - void __quantum__qis__assertmeasurementprobability__ctl(QirArray* /* ctls */, - QirAssertMeasurementProbabilityTuple* args) - { - // Controlled AssertMeasurementProbability ignores control bits. See the discussion on - // https://github.com/microsoft/qsharp-runtime/pull/450 for more details. - __quantum__qis__assertmeasurementprobability__body(args->bases, args->qubits, args->result, args->prob, - args->msg, args->tol); - } - -} // extern "C" diff --git a/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt b/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt deleted file mode 100644 index f8ca4ebe1c0..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -#+++++++++++++++++++++++++++++++++++++ -# qsharp-foundation-qis -#+++++++++++++++++++++++++++++++++++++ - -#=============================================================================== -# create qsharp-foundation-qis-support lib from the C++ sources -# -set(qsharp_foundation_sup_source_files - AssertMeasurement.cpp - intrinsicsMath.cpp - conditionals.cpp -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(qsharp-foundation-qis-support-obj OBJECT ${qsharp_foundation_sup_source_files}) -target_include_directories(qsharp-foundation-qis-support-obj PUBLIC - ${public_includes} - ${common_includes} -) -set_property(TARGET qsharp-foundation-qis-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(qsharp-foundation-qis-support-obj PUBLIC EXPORT_QIR_API) - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.QSharp.Foundation dynamic library -# -add_library(Microsoft.Quantum.Qir.QSharp.Foundation SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.QSharp.Foundation - ${CMAKE_DL_LIBS} - qsharp-foundation-qis-support-obj - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - ${SPECTRE_LIBS} -) -add_dependencies(Microsoft.Quantum.Qir.QSharp.Foundation Microsoft.Quantum.Qir.Runtime) - -target_include_directories(Microsoft.Quantum.Qir.QSharp.Foundation PUBLIC - ${public_includes} - ${common_includes} -) -target_compile_definitions(Microsoft.Quantum.Qir.QSharp.Foundation PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.QSharp.Foundation PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.QSharp.Foundation - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) diff --git a/src/Qir/Runtime/lib/QSharpFoundation/README.md b/src/Qir/Runtime/lib/QSharpFoundation/README.md deleted file mode 100644 index bf373827db2..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Directory - -**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**public\QirRuntime.hpp** Declares `__quantum__rt__*()`. Depends on the listed earlier ones. - - -## Level 1 - -**conditionals.cpp** Defines `__quantum__qis__apply*__body()`. - Depends on QIR's `quantum__rt__result_{equal,get_zero}()`, declared in **public\QirRuntime.hpp**. - -**intrinsicsMath.cpp** Defines `__quantum__qis__*` math funcs implementations, - `Quantum::Qis::Internal::{excStrDrawRandomVal,RandomizeSeed,GetLastGeneratedRandomI64,GetLastGeneratedRandomDouble}`. - Depends on `quantum__rt__fail()`, `quantum__rt__string_create()`, declared in **public\QirRuntime.hpp**. - - -## Level 2 - -**qsharp__foundation__qis.hpp** - Declares `__quantum__qis__*()` math funcs and ApplyIf. - Depends on **public\CoreTypes.hpp**, **public\QirTypes.hpp**. diff --git a/src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp b/src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp deleted file mode 100644 index c7df89fbd6c..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/conditionals.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#include "qsharp__foundation__qis.hpp" - -#include "QirTypes.hpp" -#include "QirRuntime.hpp" - -static bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2) -{ - assert(rs1 != nullptr && rs2 != nullptr && rs1->count == rs2->count); - assert(rs1->itemSizeInBytes == sizeof(void*)); // the array should contain pointers to RESULT - assert(rs2->itemSizeInBytes == sizeof(void*)); // the array should contain pointers to RESULT - - RESULT** results1 = reinterpret_cast(rs1->buffer); - RESULT** results2 = reinterpret_cast(rs2->buffer); - for (QirArray::TItemCount i = 0; i < rs1->count; i++) - { - if (!__quantum__rt__result_equal(results1[i], results2[i])) - { - return false; - } - } - return true; -} - -extern "C" -{ - void __quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne) - { - QirCallable* clb = __quantum__rt__result_equal(r, __quantum__rt__result_get_zero()) ? clbOnZero : clbOnOne; - clb->Invoke(); - } - - void __quantum__qis__applyconditionallyintrinsic__body(QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, - QirCallable* clbOnSomeDifferent) - { - QirCallable* clb = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent; - clb->Invoke(); - } -} diff --git a/src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp b/src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp deleted file mode 100644 index 3a748101f35..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/intrinsicsMath.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include "qsharp__foundation__qis.hpp" -#include "qsharp__foundation_internal.hpp" -#include "QirRuntime.hpp" - -// Forward declarations: -namespace // Visible in this translation unit only. -{ -extern thread_local bool randomizeSeed; -extern int64_t lastGeneratedRndI64; -extern double lastGeneratedRndDouble; -} // namespace - -// Implementation: -extern "C" -{ - - // Implementations: - double __quantum__qis__nan__body() - { - return std::sqrt(-1.0); // sqrt() -> NaN - } - - bool __quantum__qis__isnan__body(double d) - { - return std::isnan(d); // https://en.cppreference.com/w/cpp/numeric/math/isnan - } - - double __quantum__qis__infinity__body() - { - return (double)INFINITY; // https://en.cppreference.com/w/c/numeric/math/INFINITY - } - - bool __quantum__qis__isinf__body(double d) - { - return std::isinf(d) && d > 0.0; // https://en.cppreference.com/w/cpp/numeric/math/isinf - } - - bool __quantum__qis__isnegativeinfinity__body(double d) - { - return std::isinf(d) && d < 0.0; // https://en.cppreference.com/w/cpp/numeric/math/isinf - } - - double __quantum__qis__sin__body(double d) - { - return std::sin(d); - } - - double __quantum__qis__cos__body(double d) - { - return std::cos(d); - } - - double __quantum__qis__tan__body(double d) - { - return std::tan(d); - } - - double __quantum__qis__arctan2__body(double y, double x) - { - return std::atan2(y, x); // https://en.cppreference.com/w/cpp/numeric/math/atan2 - } - - double __quantum__qis__sinh__body(double theta) - { - return std::sinh(theta); - } - - double __quantum__qis__cosh__body(double theta) - { - return std::cosh(theta); - } - - double __quantum__qis__tanh__body(double theta) - { - return std::tanh(theta); - } - - double __quantum__qis__arcsin__body(double theta) - { - return std::asin(theta); // https://en.cppreference.com/w/cpp/numeric/math/asin - } - - double __quantum__qis__arccos__body(double theta) - { - return std::acos(theta); // https://en.cppreference.com/w/cpp/numeric/math/acos - } - - double __quantum__qis__arctan__body(double theta) - { - return std::atan(theta); // https://en.cppreference.com/w/cpp/numeric/math/atan - } - - double __quantum__qis__sqrt__body(double d) - { - return std::sqrt(d); - } - - double __quantum__qis__log__body(double d) - { - return std::log(d); - } - - double __quantum__qis__ieeeremainder__body(double x, double y) - { - return std::remainder(x, y); // https://en.cppreference.com/w/cpp/numeric/math/remainder - } - - int64_t __quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum) - { - if (minimum > maximum) - { - __quantum__rt__fail_cstr(Quantum::Qis::Internal::excStrDrawRandomVal); - } - - // https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution - // https://en.cppreference.com/w/cpp/numeric/random - thread_local static std::mt19937_64 gen(randomizeSeed ? std::random_device()() : // Default - 0); // For test purposes only. - - lastGeneratedRndI64 = std::uniform_int_distribution(minimum, maximum)(gen); - return lastGeneratedRndI64; - } - - double __quantum__qis__drawrandomdouble__body(double minimum, double maximum) - { - if (minimum > maximum) - { - __quantum__rt__fail_cstr(Quantum::Qis::Internal::excStrDrawRandomVal); - } - - // For testing purposes we need separate generators for Int and Double: - // https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution - // https://en.cppreference.com/w/cpp/numeric/random - thread_local static std::mt19937_64 gen(randomizeSeed ? std::random_device()() : // Default - 0); // For test purposes only. - - // https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution - lastGeneratedRndDouble = std::uniform_real_distribution( - minimum, std::nextafter(maximum, std::numeric_limits::max())) // "Notes" section. - (gen); - return lastGeneratedRndDouble; - } - -} // extern "C" - -namespace // Visible in this translation unit only. -{ -thread_local bool randomizeSeed = true; -int64_t lastGeneratedRndI64 = 0; -double lastGeneratedRndDouble = 0.0; -} // namespace - -// For test purposes only: -namespace Quantum -{ -namespace Qis -{ - namespace Internal - { - char const excStrDrawRandomVal[] = "Invalid Argument: minimum > maximum"; - - void RandomizeSeed(bool randomize) - { - randomizeSeed = randomize; - } - - int64_t GetLastGeneratedRandomI64() - { - return lastGeneratedRndI64; - } - - double GetLastGeneratedRandomDouble() - { - return lastGeneratedRndDouble; - } - - } // namespace Internal -} // namespace Qis -} // namespace Quantum diff --git a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp b/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp deleted file mode 100644 index 9148ff5222d..00000000000 --- a/src/Qir/Runtime/lib/QSharpFoundation/qsharp__foundation__qis.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -struct QirArray; -struct QirCallable; - -struct QirAssertMeasurementProbabilityTuple -{ - QirArray* bases; - QirArray* qubits; - RESULT* result; - double prob; - QirString* msg; - double tol; -}; - -/* - Methods from __quantum__qis namespace are specific to the target. When QIR is generated it might limit or extend - the set of intrinsics, supported by the target (known to QIR generator at compile time). As part of the runtime - we provide _optional_ implementation of the common intrinsics that redirects to IQuantumGateSet. -*/ -extern "C" -{ - // Q# Math: - QIR_SHARED_API double __quantum__qis__nan__body(); // NOLINT - QIR_SHARED_API bool __quantum__qis__isnan__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__infinity__body(); // NOLINT - QIR_SHARED_API bool __quantum__qis__isinf__body(double d); // NOLINT - QIR_SHARED_API bool __quantum__qis__isnegativeinfinity__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__sin__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__cos__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__tan__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__arctan2__body(double y, double x); // NOLINT - QIR_SHARED_API double __quantum__qis__sinh__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__cosh__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__tanh__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__arcsin__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__arccos__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__arctan__body(double theta); // NOLINT - QIR_SHARED_API double __quantum__qis__sqrt__body(double d); // NOLINT - QIR_SHARED_API double __quantum__qis__log__body(double d); // NOLINT - - QIR_SHARED_API double __quantum__qis__ieeeremainder__body(double x, double y); // NOLINT - QIR_SHARED_API int64_t __quantum__qis__drawrandomint__body(int64_t minimum, int64_t maximum); // NOLINT - QIR_SHARED_API double __quantum__qis__drawrandomdouble__body(double minimum, double maximum); // NOLINT - - // Q# ApplyIf: - QIR_SHARED_API void __quantum__qis__applyifelseintrinsic__body(RESULT*, QirCallable*, QirCallable*); // NOLINT - QIR_SHARED_API void __quantum__qis__applyconditionallyintrinsic__body( // NOLINT - QirArray*, QirArray*, QirCallable*, QirCallable*); - - // Q# Assert Measurement: - QIR_SHARED_API void __quantum__qis__assertmeasurementprobability__body( // NOLINT - QirArray* bases, QirArray* qubits, RESULT* result, double prob, QirString* msg, double tol); - QIR_SHARED_API void __quantum__qis__assertmeasurementprobability__ctl( // NOLINT - QirArray* ctls, QirAssertMeasurementProbabilityTuple* args); - -} // extern "C" diff --git a/src/Qir/Runtime/lib/README.md b/src/Qir/Runtime/lib/README.md deleted file mode 100644 index c29f64860de..00000000000 --- a/src/Qir/Runtime/lib/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Directory Structure - -[../../../Simulation/README.md](../../../Simulation/README.md) - -This directory structure mirrors how the Q# language runtime itself is implemented, -namely that the QSharpFoundation is code that is specific to the concepts and patterns inherent in Q# -while QSharpCore is the default quantum instruction target package that is part of the QDK. -This was introduced as part of the target package feature work, specifically the -[PR #476](https://github.com/microsoft/qsharp-runtime/pull/476). - - -## QSharpFoundation -Is a project defined [here](../../../Simulation/QSharpFoundation). - -## QSharpCore -Is a project defined [here](../../../Simulation/QSharpCore). - -## QIR -Anything that is required by the [QIR specs](https://github.com/qir-alliance/qir-spec), -which in particular includes the ["methods that delegate to the simulators"](QIR/bridge-rt.ll#46), should live in the QIR folder. -They require support from the backend, but are not language-specific. -Both the Q# Core and the Q# Foundation are Q#-specific in that these are the target instructions that the Q# libraries are built on. - -Qubit allocation (`@quantum__rt__qubit_allocate()`, `@quantum__rt__qubit_release()`), as a specific example, -is not part of one target package or another, but rather part of the QSharp Foundation package, -which is why it is defined there in the QIR as well. - - -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Solution - -**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** - Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. - -## Level 1 - -**Simulators** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`, `FullstateSimulator`, `CreateFullstateSimulator()`. - `FullstateSimulator` depends on the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** - -## Level 2 - -**QIR** Defines the `@__quantum__rt__*` entry points (to be called by the `.ll` files generated from users' `.qs` files). - Provides the access to simulators (through the `IRuntimeDriver *` returned by `GlobalContext()->GetDriver()`). - -## Level 3 - -**QSharpFoundation** Defines `@__quantum__qis__*()` math funcs and ApplyIf entry points. - Depends on QIR (`quantum__rt__result_{equal,get_zero}()`, `quantum__rt__fail()`, `quantum__rt__string_create()`). - -**QSharpCore** Defines `@__quantum__qis__*()` quantum gate set entry points. - Each API depends on `GlobalContext()`, `IQuantumGateSet`. - Uses `QirArray *` from `public\QirTypes.hpp`. diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt deleted file mode 100644 index 4277958afbb..00000000000 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(source_files - "FullstateSimulator.cpp" - "ToffoliSimulator.cpp" -) - -set(includes - "${public_includes}" - "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src" - "${PROJECT_SOURCE_DIR}/../../Simulation/Native/src/simulator" -) - -#=============================================================================== -# Produce object lib we'll use to create a shared lib (so/dll) later on - -add_library(simulators-obj OBJECT ${source_files}) -target_include_directories(simulators-obj PUBLIC - ${includes} - ${common_includes} -) -set_property(TARGET simulators-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(simulators-obj PRIVATE EXPORT_QIR_API) diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp deleted file mode 100644 index 2116c534eda..00000000000 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ /dev/null @@ -1,654 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "capi.hpp" - - -#include "FloatUtils.hpp" -#include "QirTypes.hpp" // TODO: Consider removing dependency on this file. -#include "QirRuntime.hpp" -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" -#include "SimFactory.h" -#include "OutputStream.hpp" -#include "QubitManager.hpp" - -#ifdef _WIN32 -#include -typedef HMODULE QUANTUM_SIMULATOR; -#else // not _WIN32 -#include -typedef void* QUANTUM_SIMULATOR; -#endif - -namespace -{ -const char* FULLSTATESIMULATORLIB = "Microsoft.Quantum.Simulator.Runtime.dll"; -#if defined(__APPLE__) -const char* XPLATFULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.dylib"; -#elif !defined(_WIN32) -const char* XPLATFULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.so"; -#endif - -QUANTUM_SIMULATOR LoadQuantumSimulator() -{ - QUANTUM_SIMULATOR handle = nullptr; -#ifdef _WIN32 - handle = ::LoadLibraryA(FULLSTATESIMULATORLIB); - if (handle == nullptr) - { - throw std::runtime_error(std::string("Failed to load ") + FULLSTATESIMULATORLIB + - " (error code: " + std::to_string(GetLastError()) + ")"); - } -#else - handle = ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); - if (handle == nullptr) - { - handle = ::dlopen(XPLATFULLSTATESIMULATORLIB, RTLD_LAZY); - if (handle == nullptr) - { - throw std::runtime_error(std::string("Failed to load ") + XPLATFULLSTATESIMULATORLIB + " (" + ::dlerror() + - ")"); - } - } -#endif - return handle; -} - - -void* LoadProc(QUANTUM_SIMULATOR handle, const char* procName) -{ -#ifdef _WIN32 - return reinterpret_cast(::GetProcAddress(handle, procName)); -#else // not _WIN32 - return ::dlsym(handle, procName); -#endif -} -} // namespace - -namespace Microsoft -{ -namespace Quantum -{ - // TODO: is it OK to load/unload the dll for each simulator instance? - class CFullstateSimulator - : public IRuntimeDriver - , public IRestrictedAreaManagement - , public IQuantumGateSet - , public IDiagnostics - { - typedef void (*TSingleQubitGate)(unsigned /*simulator id*/, unsigned /*qubit id*/); - typedef void (*TSingleQubitControlledGate)(unsigned /*simulator id*/, unsigned /*number of controls*/, - unsigned* /*controls*/, unsigned /*qubit id*/); - - // QuantumSimulator defines paulis as: - // enum Basis - // { - // PauliI = 0, - // PauliX = 1, - // PauliY = 3, - // PauliZ = 2 - // }; - // which (surprise!) matches our definition of PauliId enum - static inline unsigned GetBasis(PauliId pauli) - { - return static_cast(pauli); - } - - std::vector GetBases(long num, PauliId* paulis) - { - std::vector convertedBases; - convertedBases.reserve((size_t)num); - for (auto i = 0; i < num; i++) - { - convertedBases.push_back(GetBasis(paulis[i])); - } - return convertedBases; - } - - const QUANTUM_SIMULATOR handle = nullptr; - - using TSimulatorId = unsigned; // TODO: Use `void*` or a fixed-size integer, - // starting in native simulator (breaking change). - static constexpr TSimulatorId NULL_SIMULATORID = UINT_MAX; - // Should be `= std::numeric_limits::max()` but the Clang 12.0.0 complains. - - TSimulatorId simulatorId = NULL_SIMULATORID; - - // the QuantumSimulator expects contiguous ids, starting from 0 - std::unique_ptr qubitManager; - - unsigned GetQubitId(QubitIdType qubit) const - { - // Qubit manager uses unsigned range of intptr_t for qubit ids. - return static_cast(qubit); - } - - std::vector GetQubitIds(long num, QubitIdType* qubits) const - { - std::vector ids; - ids.reserve((size_t)num); - for (long i = 0; i < num; i++) - { - ids.push_back(GetQubitId(qubits[i])); - } - return ids; - } - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void DumpState() - { - std::cout << "*********************" << std::endl; - this->GetState( - [](const char* idx, double re, double im) - { - if (!Close(re, 0.0) || !Close(im, 0.0)) - { - std::cout << "|" << idx << ">: " << re << "+" << im << "i" << std::endl; - } - return true; - }); - std::cout << "*********************" << std::endl; - } - - void* GetProc(const char* name) - { - void* proc = LoadProc(this->handle, name); - if (proc == nullptr) - { - throw std::runtime_error(std::string("Failed to find '") + name + "' proc in " + FULLSTATESIMULATORLIB); - } - return proc; - } - - void UnmarkAsMeasuredSingleQubit(QubitIdType q) - { - isMeasured[GetQubitId(q)] = false; - } - - void UnmarkAsMeasuredQubitList(long num, QubitIdType* qubit) - { - for (const auto& id : GetQubitIds(num, qubit)) - { - isMeasured[id] = false; - } - } - - public: - CFullstateSimulator(uint32_t userProvidedSeed = 0) : handle(LoadQuantumSimulator()) - { - typedef unsigned (*TInit)(); - static TInit initSimulatorInstance = reinterpret_cast(this->GetProc("init")); - - qubitManager = std::make_unique(); - this->simulatorId = initSimulatorInstance(); - - typedef void (*TSeed)(unsigned, unsigned); - static TSeed setSimulatorSeed = reinterpret_cast(this->GetProc("seed")); - setSimulatorSeed(this->simulatorId, - (userProvidedSeed == 0) - ? (unsigned)std::chrono::system_clock::now().time_since_epoch().count() - : (unsigned)userProvidedSeed); - } - ~CFullstateSimulator() override - { - if (this->simulatorId != NULL_SIMULATORID) - { - typedef void (*TDestroy)(unsigned); - static TDestroy destroySimulatorInstance = - reinterpret_cast(LoadProc(this->handle, "destroy")); - assert(destroySimulatorInstance); - destroySimulatorInstance(this->simulatorId); - - // TODO: It seems that simulator might still be doing something on background threads so attempting to - // unload it might crash. - // UnloadQuantumSimulator(this->handle); - } - } - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void GetState(TGetStateCallback callback) override - { - typedef void (*TDump)(unsigned, TGetStateCallback); - static TDump dump = reinterpret_cast(this->GetProc("Dump")); - dump(this->simulatorId, callback); - } - - virtual std::string QubitToString(QubitIdType q) override - { - return std::to_string(q); - } - - void DumpMachine(const void* location) override; - void DumpRegister(const void* location, const QirArray* qubits) override; - - QubitIdType AllocateQubit() override - { - typedef void (*TAllocateQubit)(unsigned, unsigned); - static TAllocateQubit allocateQubit = reinterpret_cast(this->GetProc("allocateQubit")); - - QubitIdType q = qubitManager->Allocate(); // Allocate qubit in qubit manager. - unsigned id = GetQubitId(q); // Get its id. - allocateQubit(this->simulatorId, id); // Allocate it in the simulator. - if (isMeasured.size() < id + 1) - { - isMeasured.resize(id + 1, false); - } - return q; - } - - void ReleaseQubit(QubitIdType q) override - { - typedef bool (*TReleaseQubit)(unsigned, unsigned); - static TReleaseQubit releaseQubit = reinterpret_cast(this->GetProc("release")); - - // Release qubit in the simulator, checking to make sure that release was valid. - auto id = GetQubitId(q); - if (!releaseQubit(this->simulatorId, id) && !isMeasured[id]) - { - // We reject the release of a qubit that is not in the ground state (releaseQubit returns false), - // and was not recently measured (ie: the last operation was not measurement). This means the - // state is not well known, and therefore the safety of release is not guaranteed. - __quantum__rt__fail_cstr("Released qubit neither measured nor in ground state."); - } - qubitManager->Release(q); // Release it in the qubit manager. - } - - // IRestrictedAreaManagement - - virtual void StartArea() override - { - qubitManager->StartRestrictedReuseArea(); - } - - virtual void NextSegment() override - { - qubitManager->NextRestrictedReuseSegment(); - } - virtual void EndArea() override - { - qubitManager->EndRestrictedReuseArea(); - } - - Result Measure(long numBases, PauliId bases[], long numTargets, QubitIdType targets[]) override - { - assert(numBases == numTargets); - typedef unsigned (*TMeasure)(unsigned, unsigned, unsigned*, unsigned*); - static TMeasure m = reinterpret_cast(this->GetProc("Measure")); - std::vector ids = GetQubitIds(numTargets, targets); - if (ids.size() == 1) - { - // If measuring exactly one qubit, mark it as measured for tracking. - isMeasured[ids[0]] = true; - } - std::vector convertedBases = GetBases(numBases, bases); - - return reinterpret_cast( - m(this->simulatorId, (unsigned)numBases, convertedBases.data(), ids.data())); - } - - void ReleaseResult(Result /*r*/) override - { - } - - ResultValue GetResultValue(Result r) override - { - const unsigned val = static_cast(reinterpret_cast(r)); - assert(val == 0 || val == 1); - return (val == 0) ? Result_Zero : Result_One; - } - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } - - bool AreEqualResults(Result r1, Result r2) override - { - return (r1 == r2); - } - - void X(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("X")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledX(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCX")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void Y(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("Y")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledY(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCY")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void Z(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("Z")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledZ(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCZ")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void H(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("H")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledH(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCH")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void S(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("S")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledS(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCS")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void AdjointS(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("AdjS")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledAdjointS(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = - reinterpret_cast(this->GetProc("MCAdjS")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void T(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("T")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledT(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCT")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void AdjointT(QubitIdType q) override - { - static TSingleQubitGate op = reinterpret_cast(this->GetProc("AdjT")); - op(this->simulatorId, GetQubitId(q)); - UnmarkAsMeasuredSingleQubit(q); - } - - void ControlledAdjointT(long numControls, QubitIdType controls[], QubitIdType target) override - { - static TSingleQubitControlledGate op = - reinterpret_cast(this->GetProc("MCAdjT")); - std::vector ids = GetQubitIds(numControls, controls); - op(this->simulatorId, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void R(PauliId axis, QubitIdType target, double theta) override - { - typedef void (*TR)(unsigned, unsigned, double, unsigned); - static TR r = reinterpret_cast(this->GetProc("R")); - - r(this->simulatorId, GetBasis(axis), theta, GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - } - - void ControlledR(long numControls, QubitIdType controls[], PauliId axis, QubitIdType target, - double theta) override - { - typedef void (*TMCR)(unsigned, unsigned, double, unsigned, unsigned*, unsigned); - static TMCR cr = reinterpret_cast(this->GetProc("MCR")); - - std::vector ids = GetQubitIds(numControls, controls); - cr(this->simulatorId, GetBasis(axis), theta, (unsigned)numControls, ids.data(), GetQubitId(target)); - UnmarkAsMeasuredSingleQubit(target); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - void Exp(long numTargets, PauliId paulis[], QubitIdType targets[], double theta) override - { - typedef void (*TExp)(unsigned, unsigned, unsigned*, double, unsigned*); - static TExp exp = reinterpret_cast(this->GetProc("Exp")); - std::vector ids = GetQubitIds(numTargets, targets); - std::vector convertedBases = GetBases(numTargets, paulis); - exp(this->simulatorId, (unsigned)numTargets, convertedBases.data(), theta, ids.data()); - UnmarkAsMeasuredQubitList(numTargets, targets); - } - - void ControlledExp(long numControls, QubitIdType controls[], long numTargets, PauliId paulis[], - QubitIdType targets[], double theta) override - { - typedef void (*TMCExp)(unsigned, unsigned, unsigned*, double, unsigned, unsigned*, unsigned*); - static TMCExp cexp = reinterpret_cast(this->GetProc("MCExp")); - std::vector idsTargets = GetQubitIds(numTargets, targets); - std::vector idsControls = GetQubitIds(numControls, controls); - std::vector convertedBases = GetBases(numTargets, paulis); - cexp(this->simulatorId, (unsigned)numTargets, convertedBases.data(), theta, (unsigned)numControls, - idsControls.data(), idsTargets.data()); - UnmarkAsMeasuredQubitList(numTargets, targets); - UnmarkAsMeasuredQubitList(numControls, controls); - } - - bool Assert(long numTargets, PauliId* bases, QubitIdType* targets, Result result, - const char* failureMessage) override - { - const double probabilityOfZero = AreEqualResults(result, UseZero()) ? 1.0 : 0.0; - return AssertProbability(numTargets, bases, targets, probabilityOfZero, 1e-10, failureMessage); - } - - bool AssertProbability(long numTargets, PauliId bases[], QubitIdType targets[], double probabilityOfZero, - double precision, const char* /*failureMessage*/) override - { - typedef double (*TOp)(unsigned id, unsigned n, int* b, unsigned* q); - static TOp jointEnsembleProbability = reinterpret_cast(this->GetProc("JointEnsembleProbability")); - - std::vector ids = GetQubitIds(numTargets, targets); - std::vector convertedBases = GetBases(numTargets, bases); - double actualProbability = - 1.0 - jointEnsembleProbability(this->simulatorId, (unsigned)numTargets, - reinterpret_cast(convertedBases.data()), ids.data()); - - return (std::abs(actualProbability - probabilityOfZero) < precision); - } - - private: - std::ostream& GetOutStream(const void* location, std::ofstream& outFileStream); - void DumpMachineImpl(std::ostream& outStream); - void DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits); - void GetStateTo(TDumpLocation location, TDumpToLocationCallback callback); - bool GetRegisterTo(TDumpLocation location, TDumpToLocationCallback callback, const QirArray* qubits); - - // This bit std::vector tracks whether the last operation on a given qubit was Measure. - // Note that `std::vector` is already specialized to use an underlying bitfied to save space. - // See: https://www.cplusplus.com/reference/vector/vector-bool/ - std::vector isMeasured; - - private: - TDumpToLocationCallback const dumpToLocationCallback = [](size_t idx, double re, double im, - TDumpLocation location) -> bool - { - std::ostream& outStream = *reinterpret_cast(location); - - if (!Close(re, 0.0) || !Close(im, 0.0)) - { - outStream << "|" << std::bitset<8>(idx) << ">: " << re << "+" << im << "i" << std::endl; - } - return true; - }; - }; // class CFullstateSimulator - - void CFullstateSimulator::DumpMachineImpl(std::ostream& outStream) - { - outStream << "# wave function for qubits (least to most significant qubit ids):" << std::endl; - this->GetStateTo((TDumpLocation)&outStream, dumpToLocationCallback); - outStream.flush(); - } - - void CFullstateSimulator::DumpRegisterImpl(std::ostream& outStream, const QirArray* qubits) - { - outStream << "# wave function for qubits with ids (least to most significant): "; - for (QirArray::TItemCount idx = 0; idx < qubits->count; ++idx) - { - if (idx != 0) - { - outStream << "; "; - } - outStream << (uintptr_t)((reinterpret_cast(qubits->GetItemPointer(0)))[idx]); - } - outStream << ':' << std::endl; - - if (!this->GetRegisterTo((TDumpLocation)&outStream, dumpToLocationCallback, qubits)) - { - outStream << "## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##" - << std::endl; - } - outStream.flush(); - } - - bool CFullstateSimulator::GetRegisterTo(TDumpLocation location, TDumpToLocationCallback callback, - const QirArray* qubits) - { - std::vector ids = - GetQubitIds((long)(qubits->count), reinterpret_cast(qubits->GetItemPointer(0))); - static TDumpQubitsToLocationAPI dumpQubitsToLocation = - reinterpret_cast(this->GetProc("DumpQubitsToLocation")); - return dumpQubitsToLocation(this->simulatorId, (unsigned)(qubits->count), ids.data(), callback, location); - } - - void CFullstateSimulator::GetStateTo(TDumpLocation location, TDumpToLocationCallback callback) - { - static TDumpToLocationAPI dumpTo = reinterpret_cast(this->GetProc("DumpToLocation")); - - dumpTo(this->simulatorId, callback, location); - } - - std::ostream& CFullstateSimulator::GetOutStream(const void* location, std::ofstream& outFileStream) - { - // If the location is not nullptr and not empty string then dump to a file: - if ((location != nullptr) && (((static_cast(location))->str) != "")) - { - // Open the file for appending: - const std::string& filePath = (static_cast(location))->str; - - bool openException = false; - try - { - outFileStream.open(filePath, std::ofstream::out | std::ofstream::app); - } - catch (const std::ofstream::failure& e) - { - openException = true; - std::cerr << "Exception caught: \"" << e.what() << "\".\n"; - } - - if (((outFileStream.rdstate() & std::ofstream::failbit) != 0) || openException) - { - std::cerr << "Failed to open dump file \"" + filePath + "\".\n"; - return OutputStream::Get(); // Dump to std::cout. - } - - return outFileStream; - } - - // Otherwise dump to std::cout: - return OutputStream::Get(); - } - - void CFullstateSimulator::DumpMachine(const void* location) - { - std::ofstream outFileStream; - std::ostream& outStream = GetOutStream(location, outFileStream); - DumpMachineImpl(outStream); - } - - void CFullstateSimulator::DumpRegister(const void* location, const QirArray* qubits) - { - std::ofstream outFileStream; - std::ostream& outStream = GetOutStream(location, outFileStream); - DumpRegisterImpl(outStream, qubits); - } - - std::unique_ptr CreateFullstateSimulator(uint32_t userProvidedSeed /*= 0*/) - { - return std::make_unique(userProvidedSeed); - } - - extern "C" void* CreateFullstateSimulatorC(uint32_t userProvidedSeed) - { - return (IRuntimeDriver*)new CFullstateSimulator(userProvidedSeed); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/Simulators/README.md b/src/Qir/Runtime/lib/Simulators/README.md deleted file mode 100644 index 6decc7a6b3d..00000000000 --- a/src/Qir/Runtime/lib/Simulators/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -## Level 0. External To This Solution - -**[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** - Full state simulator common for C# Runtime and Q# Runtime. Dynamic library. - -**src\Simulation\Native\src\simulator\capi.hpp** - Declares the APIs exposed by **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}** . - - -## Level 1. External To This Directory - -**public\CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**public\QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**public\QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. - -**public\QirRuntimeApi_I.hpp** - Defines `IRuntimeDriver`. - Depends on **public\CoreTypes.hpp**. - -**public\SimFactory.hpp** Declares `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. - Depends on `IRuntimeDriver`. - -**public\QSharpSimApi_I.hpp** - Defines `IQuantumGateSet`, `IDiagnostics`. - Depends on **public\CoreTypes.hpp**, `QirArray`. - - -## Level 2 - -**ToffoliSimulator.cpp** Defines `CToffoliSimulator`, `CreateToffoliSimulator()`. - Depends on `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`, **public\CoreTypes.hpp** - -**FullstateSimulator.cpp** Defines the `FullstateSimulator` - QIR wrapper around the **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, `CreateFullstateSimulator()`. - Depends on **[lib]Microsoft.Quantum.Simulator.Runtime.{dll|dylib|so}**, **src\Simulation\Native\src\simulator\capi.hpp**, - `IRuntimeDriver`, `IQuantumGateSet`, `IDiagnostics`. - Consider breaking up into **FullstateSimulator.hpp** and **FullstateSimulator.cpp**. diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp deleted file mode 100644 index 0ad70b48973..00000000000 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include -#include -#include - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - /*============================================================================== - CToffoliSimulator - Simulator for reversible classical logic. - ==============================================================================*/ - class CToffoliSimulator final - : public IRuntimeDriver - , public IQuantumGateSet - , public IDiagnostics - { - QubitIdType nextQubitId = 0; - - // State of a qubit is represented by a bit in states indexed by qubit's id, - // bits 0 and 1 correspond to |0> and |1> states respectively. - std::vector states; - - // The clients should never attempt to derefenece the Result, so we'll use fake - // pointers to avoid allocation and deallocation. - Result zero = reinterpret_cast(0xface0000); - Result one = reinterpret_cast(0xface1000); - - public: - CToffoliSimulator() = default; - ~CToffoliSimulator() override = default; - - /// - /// Implementation of IRuntimeDriver - /// - void ReleaseResult(Result /* result */) override - { - } - - bool AreEqualResults(Result r1, Result r2) override - { - return (r1 == r2); - } - - ResultValue GetResultValue(Result result) override - { - return (result == one) ? Result_One : Result_Zero; - } - - Result UseZero() override - { - return zero; - } - Result UseOne() override - { - return one; - } - - QubitIdType AllocateQubit() override - { - QubitIdType retVal = this->nextQubitId; - ++(this->nextQubitId); - assert(this->nextQubitId < std::numeric_limits::max()); // Check aginast the risk of overflow. - this->states.emplace_back(false); - return retVal; - } - - void ReleaseQubit([[maybe_unused]] QubitIdType qubit) override - { - assert((qubit + 1) == this->nextQubitId); - assert(!this->states.at(static_cast(qubit))); - --(this->nextQubitId); - this->states.pop_back(); - } - - std::string QubitToString(QubitIdType qubit) override - { - return std::to_string(qubit) + ":" + (this->states.at(static_cast(qubit)) ? "1" : "0"); - } - - /// - /// Implementation of IDiagnostics - /// - bool Assert(long numTargets, PauliId* bases, QubitIdType* targets, Result result, - const char* /* failureMessage */) override - { - // Measurements in Toffoli simulator don't change the state. - // TODO: log failureMessage? - return AreEqualResults(result, Measure(numTargets, bases, numTargets, targets)); - } - - bool AssertProbability(long numTargets, PauliId bases[], QubitIdType targets[], double probabilityOfZero, - double precision, const char* /* failureMessage */) override - { - assert(precision >= 0); - - // Measurements in Toffoli simulator don't change the state, and the result is deterministic. - const double actualZeroProbability = (Measure(numTargets, bases, numTargets, targets) == zero) ? 1.0 : 0.0; - return std::abs(actualZeroProbability - probabilityOfZero) < precision; - } - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void GetState(TGetStateCallback /* callback */) override - { - throw std::logic_error("operation_not_supported"); - } - - void DumpMachine(const void* /* location */) override - { - std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 - } - - void DumpRegister(const void* /* location */, const QirArray* /* qubits */) override - { - std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 - } - - - /// - /// Implementation of IQuantumGateSet - /// - void X(QubitIdType qubit) override - { - this->states.at(static_cast(qubit)).flip(); - } - - void ControlledX(long numControls, QubitIdType* const controls, QubitIdType qubit) override - { - bool allControlsSet = true; - for (long i = 0; i < numControls; i++) - { - if (!this->states.at(static_cast(controls[i]))) - { - allControlsSet = false; - break; - } - } - - if (allControlsSet) - { - this->states.at(static_cast(qubit)).flip(); - } - } - - - Result Measure(long numBases, PauliId bases[], long /* numTargets */, QubitIdType targets[]) override - { - bool odd = false; - for (long i = 0; i < numBases; i++) - { - if (bases[i] == PauliId_X || bases[i] == PauliId_Y) - { - throw std::runtime_error("Toffoli simulator only supports measurements in Z basis"); - } - if (bases[i] == PauliId_Z) - { - odd ^= (this->states.at(static_cast(targets[i]))); - } - } - return odd ? one : zero; - } - - - // - // The rest of the gate set Toffoli simulator doesn't support - // - void Y(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void Z(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void H(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void S(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void T(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void R(PauliId /* axis */, QubitIdType /*target*/, double /* theta */) override - { - throw std::logic_error("operation_not_supported"); - } - void Exp(long /* numTargets */, PauliId* /* paulis */, QubitIdType* /*targets*/, double /* theta */) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledY(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledZ(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledH(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledR(long /*numControls*/, QubitIdType* /*controls*/, PauliId /*axis*/, QubitIdType /*target*/, - double /*theta*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledExp(long /*numControls*/, QubitIdType* /*controls*/, long /*numTargets*/, PauliId* /*paulis*/, - QubitIdType* /*targets*/, double /* theta */) override - { - throw std::logic_error("operation_not_supported"); - } - void AdjointS(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void AdjointT(QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledAdjointS(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - void ControlledAdjointT(long /*numControls*/, QubitIdType* /*controls*/, QubitIdType /*target*/) override - { - throw std::logic_error("operation_not_supported"); - } - }; - - std::unique_ptr CreateToffoliSimulator() - { - return std::make_unique(); - } - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/Tracer/CMakeLists.txt b/src/Qir/Runtime/lib/Tracer/CMakeLists.txt deleted file mode 100644 index a28bd52a0d3..00000000000 --- a/src/Qir/Runtime/lib/Tracer/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# build the native part of the tracer -set(source_files - "tracer-qis.cpp" - "tracer.cpp" -) - -set(includes - "${public_includes}" - "${PROJECT_SOURCE_DIR}/lib/QIR" -) - -# Produce object lib we'll use to create a shared lib (so/dll) later on -add_library(tracer-obj OBJECT ${source_files}) -target_include_directories(tracer-obj PUBLIC ${includes}) -set_property(TARGET tracer-obj PROPERTY POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(tracer-obj PRIVATE EXPORT_QIR_API) - -#=============================================================================== -# Produce the Microsoft.Quantum.Qir.Tracer dynamic library -# -add_library(Microsoft.Quantum.Qir.Tracer SHARED) - -target_link_libraries(Microsoft.Quantum.Qir.Tracer - ${CMAKE_DL_LIBS} - tracer-obj - "-L${CMAKE_BINARY_DIR}/lib/QIR" - -lMicrosoft.Quantum.Qir.Runtime - ${SPECTRE_LIBS} -) -add_dependencies(Microsoft.Quantum.Qir.QSharp.Foundation Microsoft.Quantum.Qir.Runtime) - -target_include_directories(Microsoft.Quantum.Qir.Tracer PUBLIC ${includes}) -target_compile_definitions(Microsoft.Quantum.Qir.Tracer PRIVATE EXPORT_QIR_API) - -set_property(TARGET Microsoft.Quantum.Qir.Tracer PROPERTY POSITION_INDEPENDENT_CODE ON) - -install(TARGETS Microsoft.Quantum.Qir.Tracer - RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin" - LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/bin" - ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/bin" -) diff --git a/src/Qir/Runtime/lib/Tracer/README.md b/src/Qir/Runtime/lib/Tracer/README.md deleted file mode 100644 index 9e41cab56d6..00000000000 --- a/src/Qir/Runtime/lib/Tracer/README.md +++ /dev/null @@ -1,218 +0,0 @@ -# Resource Tracer Design Document # - -The purpose of the Resource Tracer is to provide efficient and flexible way to estimate resources of a quantum program - in QIR representation. The estimates are calculated by simulating execution of the program (as opposed to the static - analysis). Please see [Resource Estimator](https://docs.microsoft.com/azure/quantum/machines/resources-estimator) - for more background on resource estimation for quantum programs. - -To run against the tracer, the quantum program should comply with the - [QIR specifications](https://github.com/qir-alliance/qir-spec) as well as: - -1. convert _each_ used intrinsic operation into one of the Quantum Instruction Set (_qis_) operations supported by the - tracer (see the last section of this readme); -1. (_optional_) provide callbacks for handling of conditional branches on a measurement (if not provided, the estimates - would cover only one branch of the execution); -1. (_optional_) provide callbacks for start/end of quantum operations (if not provided, all operations will be treated - as inlined as if the whole program consisted of a single operation); -1. (_optional_) provide callbacks for global barriers; -1. (_optional_) provide description of mapping for frame tracking; -1. (_optional_) provide names of operations for output (in the form of `tracer-config.hpp|cpp` files). - -The Resource Tracer will consist of: - -1. the bridge for the `__quantum__qis__*` methods listed below; -2. the native implementation to back the `__quantum__qis__*` methods; -3. the logic for partitioning gates into layers; -4. the logic for frame tracking; -5. output of the collected statistics; -6. (_lower priority_) the scheduling component to optimize depth and/or width of the circuit. - -## Layering ## - -One of the goals of the tracer is to compute which of the quantum operations can be executed in parallel. Further in - this section we provide the defintions of used concepts and the description of how we group the operations into - _layers_, however, we hope that the following example of layering is intuitively clear. - -### Example of layering ### - -The diagram below shows an example of how a sequential program, represented by the left circuit, could be layered. The gates in light gray are of duration zero, the preferrred layer duration is 1, and the barrier, - represented by a vertical squiggle, is set to have duration 0. - -![layering example](layering_example.png?raw=true "Layering example diagram") - -Notice, that gate 9 is dropped because it cannot cross the barrier to be added into L(2,1). - -### Definitions ### - -Each quantum operation in a program can be assigned an integer value, which we'll call its ___start time___. Some - operations might have non-zero duration, so they will also have ___end time___. For each qubit, there are also times - when the qubit is allocated and released. Start time of a gate cannot be less than allocation time of any of the qubits - the gate is using. If two gates or measurements use the same qubit, one of the gates must have start time greater than - or equal to the end time of the other. We'll call a particular assignment of times across a program its ___time function___. - -A sequentially executed quantum program can be assigned a trivial time function, when all quantum operations have - duration of 1 and unique start times, ordered to match the flow of the program. Layering compresses the timeline by - assuming that some operations might be executed simultaneously while allowing for different operations to have various - durations. - -Provided a valid _time_ function for the program a ___layer of duration N at time T, denoted as L(T,N),___ - is a subset of operations in the program such that all of these operations have start time greater or equal _T_ and - finish time less than _T + N_. The program is ___layered___ if all gates in it are partitioned into layers, that don't - overlap in time. The union of all qubits that are involved in operations of a given layer, will be denoted _Qubits(T,N)_. - -A sequential program can be trivially layered such that each layer contains exactly one operation. Notice, that the - definition of layer doesn't require the gates to be executed _in parallel_. For example, all gates in a fully sequential - program can be also placed into a single layer L(0, infinity). Some gates might be considered to be very cheap and take - zero time to execute, those gates can be added to a layer even if they act on the same qubit another gate in this layer - is acting on and have to be executed sequentially within the layer. - -### The Resource Tracer's Layering Algorithm ### - -As the tracer is executing a sequential quantum program, it will compute a time function and corresponding layering - using the _conceptual_ algorithm, described below (aka "tetris algorithm"). The actual implementation of layering might - be done differently, as long as the resulting layering is the same as if running the conceptual algorithm. - -A layer _L(T,N)_ acts as a ___fence___ if it does _not_ accepts any new operations, even if these operations don't - involve qubits from _Qubits(T,N)_. - -__Conditional execution on measurement results__: The Tracer will execute LLVM IR's branching structures "as is", - depending on the values of the corresponding variables at runtime. To enable estimation of branches that depend on a - measurement result, the source Q# program must be authored in such a way that the Q# compiler will translate the - conditionals into corresponding callbacks to the tracer (`__quantum__qis__apply_conditionally`). The tracer will - execute _both branches_ of the conditional statement to compute the upper bound estimate. The conditional callbacks - will mark the layers that contain measurements that produced the results used in conditionals as _fences_ for the - duration of the conditional callback. - -A user can create special layers that act as permanent _fences_ by calling `__quantum__qis__inject_barrier` function. The - user can choose duration of a barrier which would affect start time of the following layers but no operations will be - added to a barrier, independent of its duration. _Terminology note_: 'fence' is a role of layer, which might be assigned - to a layer temporarily or permanently; 'barrier' is a special layer the user can inject that has the role of a permanent - fence and contains no operations. - -__TODO__: figure out which operations should or should _not_ be supported inside conditional callbacks. For example: - -- nested conditional callbacks; -- measurements; -- opening and closing operations of tracked frames (if tracking is set up). - -__Caching__ (lower priority): It might be a huge perf win if the Resource Tracer could cache statistics for repeated - computations. The Tracer will have an option to cache layering results per quantum module if the boundaries of modules - are treated as barriers. - -#### The conceptual algorithm #### - -Note: The tracer assumes that the preferred layer duration is _P_. - -1. The first encountered operation of duration _N_, where either _N > 0_ or the operation involves multiple qubits, is - added into layer _L(0, max(P,N))_. The value of _conditional fence_ variable on the tracer is set to 0. -1. When conditional callback is encountered, the layer _L(t,N)_ of the measurement that produced the result used in the - conditional callback, is looked up and the _conditional fence_ is set to _t + N_. At the end of the conditional callback - _conditional fence_ is reset to 0. (Effectively, no operations, conditioned on the result of a measurement, can happen - before or in the same layer as the measurement, even if they don't involve the measured qubits.) -1. Suppose, there are already layers _L(0,N0), ... , L(k,Nk)_ and the operation being executed is a single-qubit _op_ of - duration __0__ (controlled and multi-qubit operations of duration 0 are treated the same as non-zero operations). - - - Scan from [boundaries included] _L(k,Nk)_ to _L(conditional fence,Nf)_ until find a layer _L(t,Nt)_ - such that _Qubits(t,Nt)_ contains the qubit of _op_. - - Add _op_ into this layer. - - If no such layer is found, add _op_ to the list of pending operations on the qubit. - - At the end of the program still pending operations will be ignored. - -1. Suppose, there are already layers _L(0,N0), ... , L(k,Nk)_ and the operation being executed is _op_ of duration _N > 0_ - or it involves more than one qubit. - - - Scan from [boundaries included] _L(k,Nk)_ to _L(conditional fence,Nf)_ until find a layer _L(w,Nw)_ - such that _Qubits(w,Nw)_ contain some of _op_'s qubits. - - If _L(w,Nw)_ is found and _op_ can be added into it without increasing the layer's duration, add _op_ into - _L(w,Nw)_, otherwise set _L(w,Nw) = L(conditional fence,Nf)_. - - If _op_ hasn't been added to a layer, scan from [boundaries included] _L(w,Nw)_ to _L(k,Nk)_ until find - a layer _L(t,Nt)_ such that _N <= Nt_ (notice, that this layer cannot contain any qubits from _op_). - - If _L(t,Nt)_ is found, add _op_ into this layer. - - If _op_ hasn't been added to a layer, add _op_ into a new layer _L(k+Nk, max(P, N))_. - - Add the pending operations of all _op_'s qubits into the same layer and clear the pending lists of these qubits. - -## Special handling of SWAP ## - -The tracer will provide a way to handle SWAP as, effectively, renaming of the involved qubits. The users will have the - choice of using the special handling versus treating the gate as a standard counted intrinsic. - -## Frame tracking ## - -A user might want to count differently operations that are applied in a different state. For example, if Hadamard gate - is applied to a qubit and then Rz gate, a user might want to count it as if Rz were executed instead. - The frame is closed when the state of the qubit is reset (in Hadamard's case, another Hadamard operator is applied to - the qubit). The user will be able to register the required frame tracking with the tracer via a C++ registration - callback. - -The descriptor of the frame will contain the following information and will be provided to the Tracer when initializing - it in C++. - -- openingOp: the operation id that opens the frame on the qubits this operation is applied to -- closingOp: the operation id that closes the frame on the qubits this operation is applied to -- vector of: { bitmask_ctls, bitmask_targets, operationIdOriginal, operationIdMapped } - -The closing operation will be ignored if the frame on the qubit hasn't been open. The bitmasks define which of the qubits - should be in an open frame to trigger the mapping. For non-controlled operations the first mask will be ignored. To - begin with, the tracer will support frame mapping for up to 8 control/target qubits. - -__TBD__: C++ definitions of the structure above + the interface to register frame tracking with the Tracer. - -## Output format ## - -The tracer will have options to output the estimates into command line or into a file, specified by the user. In both - cases the output will be in the same format: - -- column separator is configurable (the regex expressions below use comma as separator) -- the first column specifies the time _t_ of a layer _L(t, n)_ or of a barrier -- the second column contains the optional name of the layer or the barrier -- the remaining columns contain counts per operation in the layer (all zeros in case of a barrier) - -- The first row is a header row: `layer_id,name(,[0-9a-zA-Z]+)*`. The fragment `(,[0-9a-zA-Z]+)*` lists operation - names or their ids if the names weren't provided by the user. -- The following rows contain statistics per layer: `[0-9]+,[a-zA-Z]*(,([0-9]*))*`. -- The rows are sorted in order of increasing layer time. -- Zero counts for the statistics _can_ be replaced with empty string. - -The map of operation ids to names can be passed to the tracer's constructor as `std::unordered_map`. - The mapping can be partial, ids will be used in the ouput for unnamed operations. - -Example of valid output: - -```csv -layer_id,name,Y,Z,5 -0,,0,1,0 -1,,0,0,1 -2,b,0,0,0 -4,,0,1,0 -8,,1,0,0 -``` - -## Depth vs width optimizations ## - -TBD but lower priority. - -## List of `__quantum__qis__*` methods, supported by the Tracer ## - -| Signature | Description | -| :---------------------------------------------------- | :----------------------------------------------------------- | -| `void __quantum__qis__inject_barrier(i32 %id, i32 %duration)` | Function to insert a barrier. The first argument is the id of the barrier that can be used to map it to a user-friendly name in the output and the second argument specifies the duration of the barrier. See [Layering](#layering) section for details. | -| `void __quantum__qis__on_module_start(i64 %id)` | Function to identify the start of a quantum module. The argument is a unique _id_ of the module. The tracer will have an option to treat module boundaries as barriers between layers and (_lower priority_) option to cache estimates for a module, executed multiple times. For example, a call to the function might be inserted into QIR, generated by the Q# compiler, immediately before the body code of a Q# `operation`. | -| `void __quantum__qis__on_module_end(i64 %id)` | Function to identify the end of a quantum module. The argument is a unique _id_ of the module and must match the _id_ supplied on start of the module. For example, a call to the function might be inserted into QIR, generated by the Q# compiler, immediately after the body code of a Q# `operation`. | -| `void __quantum__qis__single_qubit_op(i32 %id, i32 %duration, %Qubit* %q)` | Function for counting operations that involve a single qubit. The first argument is the id of the operation. Multiple intrinsics can be assigned the same id, in which case they will be counted together. The second argument is duration to be assigned to the particular invocation of the operation. | -| `void __quantum__qis__multi_qubit_op(i32 %id, i32 %duration, %Array* %qs)` | Function for counting operations that involve multiple qubits.| -| `void __quantum__qis__single_qubit_op__ctl(i32 %id, i32 %duration, %Array* %ctls, %Qubit* %q)` | Function for counting controlled operations with single target qubit and `%ctls` array of controls. | -| `void __quantum__qis__multi_qubit_op__ctl(i32 %id, i32 %duration, %Array* %ctls, %Array* %qs)` | Function for counting controlled operations with multiple target qubits and `%ctls` array of controls. | -| `%Result* @__quantum__qis__single_qubit_measure(i32 %id, i32 %duration, %Qubit* %q)` | Function for counting measurements of a single qubit. The user can assign different operation ids for different measurement bases. | -| `%Result* @__quantum__qis__joint_measure(i32 %id, i32 %duration, %Array* %qs)` | Function for counting joint-measurements of qubits. The user can assign different operation ids for different measurement bases. | -| `void __quantum__qis__swap(%Qubit* %q1, %Qubit* %q2)` | See [Special handling of SWAP](#special-handling-of-swap) for details. | -| `void __quantum__qis__apply_conditionally(%Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different)` | The first two arguments contain arrays of results to be compared pairwise. The third argument is a callable that represents the branch that would be executed if all results compared equal and the forth argument is a callable that represents the branch that would be executed if any of the results compared different. The tracer executes _both_ branches.| - -_Note on operation ids_: The user is responsible for using operation ids in a consistent manner. Operations with the - same id will be counted by the tracer as the _same_ operation, even accross invocations with different number of target - qubits or when different functors are applied. - -_Note on mapping Q# intrinsics to the methods above_: Q# compiler will support Tracer as a special target and will let - the user to either choose some default mapping or specify their custom mapping. For example, see QIR-tracer tests in - this project (`tracer-target.qs` specifies the mapping). - -The Resource Tracer will reuse qir-rt library while implementing the qis methods specified above. diff --git a/src/Qir/Runtime/lib/Tracer/TracerInternal.hpp b/src/Qir/Runtime/lib/Tracer/TracerInternal.hpp deleted file mode 100644 index dbd1390c4f0..00000000000 --- a/src/Qir/Runtime/lib/Tracer/TracerInternal.hpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#ifndef TRACERINTERNAL_HPP -#define TRACERINTERNAL_HPP - -#include -#include "tracer.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - extern thread_local std::shared_ptr tracer; - -} // namespace Quantum -} // namespace Microsoft - -#endif // #ifndef TRACERINTERNAL_HPP diff --git a/src/Qir/Runtime/lib/Tracer/layering_example.png b/src/Qir/Runtime/lib/Tracer/layering_example.png deleted file mode 100644 index 5713843d8598169f80720dffc2b9fb714ab61527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26209 zcmbrmcU03^*EWoUfQo>M(n3)YP^nS^p{gjL2&nW9Mx=%kq&F)?2#5%XH0ja_C3GSJ zp@V?506_?$cci!P1ZU>HpXXicTkBo#A7jQ$@;m43v&*%wePx0ksw>l-Vmn1aK|yy< z<<4UY3aXP76vxy~`~}_-VDoJSFUOo8E8nKb>tLS&e>rY>OYIf~1v-p&&zuVU{iLIc zzB2^{M*#VsV_~Ef3knL0jeB=)X}Oy$jJTOGT2Q`NNu|AtxCR*vwEOtz%zMF$ii3fo z=g%H@IyTOEB2eJb!!sWPjOVPv;T? zN$3)6vAc59T8O!a1FTFzu?In%CciGb72hDgejDw=kzXnNlp&Pl*FBE^#}Cl!=h<%; zt2UmMnMiFk-t{nxWuD{=jfg0ys0=?xD$Zc_=Jpu0CCJ%EK&iocqL$@V8}ZVc5*(HM zIvvY@MYU$r^a}JZw(hSW{jqTFnHE~z48uO&qg|PX7Z8gwhDw%xZa3$RRJi$Fzq@H~ zTq!$Pt{LFEFly*&OwdlMPthD=%Kxz$9n1u)6H*-f>}!Et|hrA}GH zION`Y=Xq6GXtht~uBnM{^Vlm7?oPWaX>;1BE!7O+;g5cpRyxSN!;(xZ7hXb{`)X6T zrY_NCFWRC++87J9hJ&qdood})Q1F$U8e)x5s_ss;Dj2-->GdhO-}Ae0KD9&%uSGXn zUzxnD5PLh%e5J)z;oJ(|46#7D-*p9+Y?1uN)eR9EF4l(F$1$^E)#BD`7J{!gQ&or3 z>FkVi9rdi9)(pd5wY`MVD*VXlZ)%`{PjGY>#l`fLTa7_A!(dz!_M}Nvs%Q0eMlW|l zE5l?v!F|s<05U{^+*>M%X_zfG!iypv?i!F;B&0eYqR^-{@fc zD|-%#kSW^g(vhu&*%p4+&Qy8})jmJ-?uuzhpOasA$!k?5*Xyx8cA`@Pn-jW~3)uLm z=TEx1%a>-vcXL_^D$aua4p$I!5{&m&B=Zs!(3bn(Pdd0}>WT{7d}HOfuaR7&w{1V9 z;+dn?n7uZ+cyb}DZ*hC3h4I>yK_kcEOs_%tUu`cvxIAqeJhTt*sAcz*{PCt2OZSGi zO(M-~uO8PuG|NwK72~<<*Lz)~qauX??-zF6=`aJuO?jJZ&rBKL`P4v2a7J7DYT1NX zu+UYG8pAw+Io%|!*yi1CW9zFkKZp5(sXjlL{PkgEDiOVFg@#Z^(UzBX7Csmq*su{I zsHo@Y$_Oy?O0a{7VOS_fe#ch3gLZZ4e!YTF+T7wB zjwN|BAHs{j5E+TZhjmW^CVOXEO74*Z@7tyn$Ekk58EsjqQO31-ESEJ*AzSg_#fxE% znreZmJ-(SAf%w|VUr5S+{>m)-@w`hC@7xk>h0_Exc;(L7%V|Iho+gr9M}yR*r3zg! zc8?TYy;?~zy^ae?NSo?sq;B2zB7J6~dyq&7|`Oo~z37iUehKy83A{$90wn*Y&m@pK2 z(s1153PoeoBcMpyNc6>Q_U3(Uf}7Q$*GLc?+s+wD$W{plPuQtB`4)BD}i znP5-59eL49=EUdn%H}-+v8S#A*}t_SfuG5Y~exMdc~i4%+la~+w>$SH?v5^Ghp0+^=$Y%i6p5G* zMNLdNuoddXHw!rR!$rMqHM@f}W&;AbXq+8E{MuAee`@KS$@;Gi=~}uqSIueg(Mw_JoHPn~Ys{5k$0FYUr+aBlW(Hk5*e+m*xu^P8-5Co5|o)lIPH zo^z|x9Ng&?q0#l^?^z(ai|{*FWxD)x#R|#=^>Q1^Y1}l@7i`@&W4+IhRlhrH2yP zCu6B2OE69kXLKjnQRC3l4}8D8Xkv;;5zBv3##J#zcjD@>Z6=2Hl1B)?wRFT2;Y3*R_HLN8?ukyFGJ7m*W&G}R{%r5e=YQ>f_Ed9p4AjyVw*D?I zoKP|oW3)b7;Jqe0c zd%PFfLa2>|G#_i2>pixPm|sOrJb+{y#OwNU4t((}$`qnyPH;^Nn~gCgab$~}ZT(KmZJVc>rF=AuW|MTbi7=O~;xwvB&K>gQ6C^VKw7=)blQxQ)>)(e=}C$$c{P zwbC8i8-uCD~;|fD1Bhm(YH?x5zpywv$lfkI$Vjg)jSwfs+)b#aBlzJ{)8ozM%QDBAwVPk z2k6Ja!*~~qCC92tJs9& zA(ykzsLJ*J^tggY1=7UMr9JD(-&}5kyXolzONk-HHBNS-hS>fG9a@U(v(MZAiT{uO zuSk2kg0yl6BWf_~3)soGA6`!Wa4oWLVr8w*_f5KEw%Qo9fbDd(Fvy<2eBqU?xBJbG zN7H!!O&jLSopB9T`BRX!p6n3?h4+J4Zl|U9v$Zi;)Iy0-Mfl^MGO(>t+~3AmF z>^pO}>aZ|N>G>Mz49!J~2v{RrEUyKY**vAQo31llb22O{^M+YweAsthJ0G#Eh2Iab zrhL9G*zuQ>M)6Y_dCHWa=2ocg{^PxwMzP3l-t1mhY_YDn;ofJE_ga@@#vw&`9zSAf zGkf2Lt(U?%{r?fR7DR60%)%0pxpZPP*hiRpl zG@tRgN@j1_r}f3_8#QyhGe2J?JX$v~wcb=s66hjw){Ne=GEL~a9st`qaC3cXa>t~( z0n&Vb-_xZy@*t%KWn2@km8S+1v_MNUZqf%8%41+6opr>G48P?!2nX##6R(b^^bXs- zgW_|FgLp0Me-cYV{epuo=I!m0TCe%AM2GqPLLc%Q+(=MQE*(RI!f=#-&r+BAaKd0#Z{rvreNv8{WY9M6p{v9%% z1y|}D2qER(o`;2Qb9b*K9%p;_l~}A8?NEPTGT#*D^>p{0gEDrx$;Z4o<0W;ZuZ2&= zitp;K*)ghwgBw1Z=~3>E-?=>~!67iv^?RvNJ&Oy`YK>)M`MX7F-wX!DgDJ($5p zO4xU9DW5vdE3BGcfBPV6a0nOGlc*3k!j2!$UTYyB*Q6v7R|MxI)^e}tog+598ZC3Z z%e3lC;y9O<|} zC)+&5V@NHSAY*3EH<)*B|j^ z8O6>Vu0NmO>u5Hmp9_Q;_6Uo%znF8Dxl27^B`a?*2>HZ0m5xe9R6oN+zIh|)Ku8o5 z7q~3ymQc05O2YD+)>L^Ql_yUz&S(9XFTNo{{IzYB3_*m}c zG_>jpn@U+MGe%zis}1%}!q=rXJfy$OwE%h{x1)_<yzb&EZH6Hb-LP zCl1EFtI(UIT{MZ1-t>g;jgLeZZplPv6x%AOwNeGo2kY-WPx@An&ZsfF@1NSQSrab) z!;PQSoco$lyiXc(1-KPTMB4X7x2v{%|Ay2&l~P3oxD zV6XGELVdQ)E-*#Dph^X+-&+ELrjVx^kAJ5sBS|@O-eGkSSf8kD=!Y7^W)Vp9nS2l3 zJT*KE_;8+v!Pc6>4ga_e1))Ke9eW;b z#FZt#_R8mfR(@d|M1zBd2<&am7n=^wzKI=@w;HzlEg#>ZuQni7X#W(Q!GzymG{GkA zDs=;MAh|)Mlps@Ko@8T!$b^g6tPJR4v$G!JiU07JDUKOb_!TUvL)#MQ?aFZ)2D9B~5uiAUNFwS3>P2PK}Ek=xV zJZ`O1;qYgsCf}t?x6~o5M|{RKe7ru0H)5uN+4N)2viwv>JS{D4_LTldl;*j(!byMg)g!7Sev{Mf9Cx8=fA!kgE5;-##knt9a3@M9C0HFSh>s(px?hg zgBIn!d|Ak(>gp65)1S4>etmn1nLhVf(m`nId+gr!RN}LuK@6(UveS2U;+_Yk_{@)_ z*9ePKK0Cj|o#}#)*qJy9+dhGq6K>Uq2fM3K_W;cd^%1Y7YUJ9LY@J+uB){%N`$Oyc zz_Um>f`moOd5_o|M|9E)&1W5PtY^+#+`Sm-M(|K3P$n>Oa2RK4T`M<#oM})pdw8&^ z;K6rwV=REHMdH)DLAwN5kHV^eV}HJ_6u`jcJ08UMly#uz>!TDwLrjxb9pe)?Tp0y; z6J4S5d!kphzWzwP7hykA!E^W*J01;JE_&8JQBjoPv%9goxfHz#J%1D?52uF9or9SK zXS-6A&CI@>e)aUyrAx1A7&;>O!s2S?(2bn2a$XxN#75F+z})`s5*7lrW0ChRQ!y=P zU7Q_2!y|arChCpT?nko8xX$S|CN6M$s3Jpf<1Rq<{p^*-YSY>hfQmO*}}E{ic3C%fD(h(;p00y5+mOmY=Hf zzdEplJ@g$YFdy&j#`^h{4+~dcF-aRQ!KbUmhG0z#nj`u9fn^%F1G#YQFl1A?^#y*N z2j@$+2G1(oT6J2g5mne*u4@QpHZ60k#jtQ5F^%6ueD~M;<$ZRv@{9%-r@Z7;>dDIp zMXz?mD+bV)*bP1=OiJ93783lhRJl~MW|j~y5QK~Uf zUrE$-Y)!i!5%(jn3mO!w2&GlV!JN|_Mr&fKODps8QicgAj#Hh9 ze!ClpZO6sCNBmIs{{H4@d#q$M+;ZtXaGMI3xta~Ut-|4+bJS98o+;MkjXwEcNzK?L zj^yNI*Tvy$(cO`ELaMhXV`t<)%u)mYTIfn?j5hU+Aw_=GNR;0PDZp)}yCLSuPu|Oy zpR|3tfrJ@306Qcrkesv6GBIr|*D+vs7b|W1a>BzRPd>u5MhP+wnqqzT+tiC)b8LFE zhDsfx8K#tuny@Ne+)`@E$rC5sf4!vH*;tTyQXl9<#Hl^6z8AsU4s1lI@)*J(dR*1u zkGi}z7vmND4uHD}zEYFV<418R`2PI*Xn%JzUprezMJ3S4E%T0&(&7MGQ7F+iP=u-d zTB2}n^4(zhgDqm6KOIEIHOJ`5l=n&lGdMLlhF3|g!lpF>(Kpl9raFc73nzvgu;6yH z+;49GQ2!)i{=JdQZw~}&F=7aF^W|TwT#0?(UQ&{JD!boZ;GaM6zkNPudH{qE;`ek{ zqR&Q*(P9uIpGJa!N%r`KO0P|Ogr!Vy;`x=o7!@SOEtO?Idk+UOHWUTwrf=!?XKFSs z#%`QFdp1_mAu4QK=#M>q0tXfIO z3OS+D7|KDCR%1GQ)@gp=hDTA}o#mz}6`ijSlV*A{yf!>lO>Q3_+gc&^_{^>TNKz!V zdxASNtPol3&5kbJ_q<(oqGz9%M?heHd8}^rS%1nA!-FFD?jm3})j<}J{CrzVXY@AOrABoyKs$}ZkAA8$?VIY(?th? zwqZPJFI*Rg<4(hL6)vwyKc6=9I&~Ij*%22mc`*{Cd;F@@#fx7oI}@0NO+HFa^If}U zJzVA#9v+TC+(6*PJzkfx$$1td9yNT90WK3a=Lv4`n;#vAv=+PD=F+He#xeJp`uciM zCv-C(lgP!t=u7sq_RrP(8$-av?|Xy_>_?>0RFZZB7c;_ZLCu-#&EA|!^lP#1-x@tU z=+8IBf?CyVy#x-G4ZMCo(eEI<$NmkQl#F>mP6|DMK|ZvyBZ6`*OViMoZz`d)fmR*AR6=e z_AzuaFK~MDw_^iEg!Buqn8&5%8kW+ticUbLLO5h}WSdHC`zK!irOvOJewzDn?3XVW z(bp$Dk9`T+fC@cM4UBuC2=GZ7>ex40a`812UxyRJDM#Tx8Medbq#Yh{Lt$H=RP9Ub+Lx_=4L1F_(`Og6>p#tbwO0{dp?k z*=UW=_S%%gaM=xyoHPG^w&%;O*MCt`Hp8ob;DkV3d%=vabaSZj-sbe(GGEm@`S1F< zx~4wb6Dxb$Yb5cwU$zK#znR}}V^=#(UICo7^zlO@09g6JLQe;AQEwpqLZ&sY+_jf2 z2W~+W-AwSf1eUOF+<_fHa1&@D?@Sb&q0?)-wrUU(rm*nq+q^GuSNB|y1m8E_e~QPI z5{2fC<@{#LZX`bVywcvU$>+V=T(7TsD8=YyNJwBqoF4Hohj`h@-iPd#AETr`PDLfC zS8!8Zk@0n@)8jo<_LRoFKGyMjtnqB*X8K=`B`bJ7dGi;;;w`747^6== z1(Y$%dpulLKMuCTcsKJo1U62B?ca>&kZ}zpDuZl85*ID#!Ii@4g%0cn-X|prNjq}4 zSJs!R=f{cLH7#G8>)>sHog*JY5#rVC{t@c`FhiYVMRW_Gg9&jh(XCxvrtwae2NYcN z7xgDko`95BWZ8KD&|Ry3(?vzdXbpO47p)j@niXoX-|$;`Wa!Xt_u1C_OWfE&SDtpY z;v3JWe5SO#?0|vbAd8hFNj6={cW>W*6-O2Mw>#Pfi7Wy%ucyaiKCia}4rZia@#(}! zQlg;3wa#?)2Bh!8Q>VEC;eKNkpF3nj;H9V#^HXSP$3LwO zJ9n-x47p9V#W*=Rty1@^o+N_A)}-Hbrvf~DM3$)7Rj%l-Hu`bJxxsC*ejyHC?J+PR zG4*)lK5&s@r&CxPqH44did8Hfpgz!Q?13Z%iaC$NvMxZbXUvB|;@R4AhtYoi8hz_>7lgR2g%JZEv>U)%wlItl0LE^&JNyRg%rw{%5vsK<9; zV6yM!Tkc#*hQp1p*u#+re^x)VhNB^94$(Kiewh^w#(_%Qo2}a#yB{VPVu-Q3$08H< zC-COa#E zf9Ni0%a3^?`N)WG6N^^>9*p(=qM|tAq@vOU`XN&MUNb1}w^-%^|EwY^?RQXRWj0j6 zoAMXcsX*^Yw_Za4>=roS-}TCWFF_8gk;w}_#F*H8i*8(BY=F1aZs%bQYcSM*5#*JF zA2`4Lb=}oV-|52}(^R9QHrF7RL!3!1cmK1%xw=7+_=Y~=^6myRHCN6edb700S7Ie> zc{7r3?$|mtEFJoOEb$3Y8(CJ6p&9XW>tMC7hHbmT!l3@NKXBLfl2A0KSGXRdTnGQktT&?3%D zqL=8|!RaF0))|0Tlx>|oK}Uz^Nk_$zDko@18Xh1dGL6cSFaM&NfV-a*wkOhXy5G!` zqPSTI_K#N*bB$@A6t!q+s;uxo0bjXu;kwrQ9>=e``6kvwTk73|qv@xao%R~C#AdMd z*ADVcYxbnJ4U9JMWuVnuy_J**u%m|uq?M}IaB;ZY8V)gpXh{B5zVb6TB&7Jy-axbQ z(QO@)Yfy47o5yQoUM&_HS>*lEJE@XoN~@B6N{jNQ5`X}%jy7>D<885$8iZbp`VM{B zuE4Y06XK%L2XQvp^|Xf1P^{8Ve@Yq(r^zq!#+k@0O^y17Cn_h@%w)xN!g?K#b!4iK zF-&P;x5k4*J2MZ~`%Ov4`#}SleZby&=qI3pMi+g#fS5;uZ1(i`=BOXd@#6zZVyr=O z8D2H{$nMm86Xh9cqa@w@kz`wv}(|}W^0^wWqtxpNsk{09l9}8bMWx#@{bkO6ipxMs6RpG z+l~7Uiszlv8F5A&XU?4AkufO-s;fk=L~eKAXm{ zvCTUy{aM{~I#F>r|f?r8|HPp=lB2UV^Mcg+g!$NRku@B(jXnGXOwnGmoVmqFOUB+>T*T}*%f zm`<6&T6d)!t)Pq_V8U^EFTadaniMTwS(r0?Mqh+k`4Ndir%CK00 zcb^Jw4WI6q!w)zUAK?ca&f=s+-64kOh{Pujgn#L0>UTc3ZzkZD4y)$s-)7&CgcHZl z$sRI^0;Hyo^n-MMy75{>M~JwKixRWtr8{)r5%x>i-o#R4j1;!knCk+3Wo_3yWK3(x zu0`m=ncUz^L+xumc<=zg!xQjomP!;Orpm*w<3~YY)3C?tWNY+cp7-|ZWHUz$zpQxi z?_k>rW4Qn*^$UUWauk5{fL9VBgI83}KqZszaOUW<&|U97VeFEuySVhuz)KEWW?u=< ztpQdnXIb1BcT+R)gbseZUbDNn38CM`3a;?+98NiuJ-p}_`)jO&-EnffHpe5XfaBeG zW!Ie=6zBgJh|_}MEw4_0Qt(v!!DQ^<8ndwR=2TGj$yHt%@&k9}9p-Noqq%Gb3)2Rs zK|34S$t5WY7GZVlU*X*>2ZDfQ?WKPKBp=#2G%)athd`(;k}2-yA6Tl7JS-_| zf@@pB-T992MJdK^@HXe%+dpNnaN+>UR4?iF;y86#`opU2_x-(IX&Kym{3;O;@Qe$$J8 zuzCUM2A{6X>wgxng))&@2;4`|KScHX=VNDP`*QhBsDXtddLG{Mf&{M<=uZV+06fPF zIHw&EZ~)9#AA;OK4Q%kS=>1D8czDSuO!+h?uwV9r7|{?F#P{#t!98x`AP&c=sXG$n z4Am9oPztyCb#kWvYS76y5hOP-oEjzM%y|ON6UrfjdB}%GqgQVo%cusl39{hrQ)KlP z4PL55a@0FQ#5KWh-}5BJuXx#R91R1ffL?*w{?7VIIE4s1UaG;+q58s^GdIZ~hMK}X zVRuN1Cj@i?mn@G_guI4gJa!jK@3Q!TKap(eWacPgY7#2{eEMP1?YFbN*~%=GFK%_= zzRN6CDUtyM`4KWhdT*SptgHa9cmgaUgxA|!Q{(LcwAE41K}jLo3492U!?GT$9|a9J zz+;e16yL0@h>uc~f}jWE!NUK3uzgA^SQaq7?pj*sJx)_l--Xa#4r_n52I$eFwut<& z{r>U$&F~s~W{QwND25e=yyB6=4B+uEk3|PKx!ngnHFwytZ-D^*a4UtJ@c`>Sh0*0& z!AAkln5Hb(_ypZVa*T#KPc}`tRsve5`RxnQYR~l#a~%t=O9VmYzX*-o2!VmsGQc<5 ztVzkso75D@zWhYA!mVq7A>*63LG=cEA1P zNwN|BCIH4^cR2mFNzq7yw56I|^#jV`#Rv`gaN02{@?K_tUW;hf0&pl}BpaA_++zf* z$s+4s;BHcW#{Nn|v2qwE^3sF_j;MO-*E%Kzm0*Quu1@-I-{Q)jfDqrmCg&(Vf7p9NP4@=~uiP z;jG*HO;_%f)K2-`9HSGa(fW`?n6pw&0N{^WBFBX=XVtyEtBmMNA%H)gl;xCu{x!n{ zJyPiw{b%z}ZHXWdo2}jStK8ACz(B2I($ zdgL{<4}`HX2tPm~my$u(>crXC#xT`mc$VdJ#ZETG=wGD>0UQhz`RTsgSpP7|ZXC22 z?U4NMcN1*jXK?_00VJEBCBkF{|EBr_m)V7T<%dBFhTOHZsa#}0*eODGdGP~sR&Y8i z_y8#!s(unq*KZ9PeBoP=3?&Gy?*Oi&3JyLt^aEED1a)P=>rvuaT+6X2;`sPy30nYB z6F`D&*0%xNK=c=sW(tbR&z4Ml1d1$zUI4)SxHQiHz0M@mRWyX^KS$9;RDo*xdae zcqy7^Y^R(U5SCEE5^-_r>yt95dX>}4L^yXw9?o{xks6Wn5&)Aj-}PtDAdTmoNm<|f zuOa65!5Sd_G3EyVCbPv_EzXMg=F?i2vW8JeZ}Dgn>gDs|5_AM5XFvFpn-Q<~a0n-j zl92s@yDZQkb^vHwB>p6RXdn{~TKQ6-ez+HnQG*Or7>pJCX!0(1J|08q%;ft;KR-N} z0oNpR^~opDLjM1WL9-OEfQ@EBLw9Rb6XNbBb9ybl7G@SWM9d}BHHL8vSG%+)Tc^;t z*cxF}>bht??k9YNZur{EXY7EUn&H$bHW9NIaXK1`5W~EMm|gp+ipUn7K$~U9l+PDb zqp$CpRld}O8f&f1`Qlkrlr120V!yhuC>OSvAGlX7&_3Iov6oPc4^5JPzCn|U4`dGc z05CjE*N3G3r?Rx)`TtOsI^Y+hB;ds>?IVnM?c9;Io=CnYq&OL;MnBIIojeL>GeQ8t zz{zAqxCS_!R*BPmRbyTIxI|<)tk@`Qu<>I}w-tP8>cG~C%zsR*kO|@9`!+*TD_g^s zwm;pG^I43C3y+BEd2AzSyS&SS@&r%gjuyZ@q^6{!Tp-KS!?+#P11tC~a43ns<+dpl z!UHtF@_cK0;2oEvbJ*YqkQVybQ)&1I1ZF-fIF)s8C2H{AoUxXWs1?8&VCSD zKy~-WHO}It4#JX3T3zDZpu}wGwVgTow~C8Ci_!coCSAoh-iMAu`<%a}*h1!DqDd$! zIlNw-UY!_1AW+tKYb+qX4X%yI8NGpm`Wt?=BGwx#+MHjK-Fy|VTR{4ms{Fx?=_Svu zfbR3GaL|qT-c3yIqck&n85KdF0sB#(?}JV)yE{*3i;_y6KXV6S`vrqqyR&}q{HKiY zrf-&WV`JsI=XUkV8dQn@py9w6?0XG>jLTsS-*$R+O(T2WW!ez=ouw~)UB*A#wN&C= zI4pRkqPN!E;y*#&c{jAr|F7KVA$s32)|`!D!2&Q^e&tu zEeR1A^WEC}>_x-FaUf zGtWkhR?xjt1{7ZJHe|@#{0{AN``_D)0NYG?OvIrirqJ)8eQTeTAa#NtOMwq|oW*b; zJD5xpR1YBuZ`5A5Q0Y6+wW#05A#P%knPKW`6zDV6Seqj}Fo8N3r^|N>d2n?TzSq8K1DOZTW(qd^VC+UPF zts;_X7k}VxN}n3i8$A}5fXJC^dIM$JD?(8@nPq_@3LxVKptvosr6;4-|FI-HcGhFg z(yl(~a!x}ut31?fpT~ApSm5>I9QIbN>M%?Hcm+JDk~3ng^6Uq7@5c&v*d^L)FN_Xn z%{RtC`(A-kl7KkED!NU~rrwt_Ec-q_2S7hVNZ{=t*;m=SW;07_oPKJFV~-Eefu6ib#?E}aYW8Z zkmO|ta4E`R(Af5@?|NWs&5Be*Uu3<4lnKV(;=N1w{5-GqmvMKc+ojXyi{5}6PU!9g zKKQGv61C7JPTty~9jJDf?ZHGW`%(y%M51Kh%FzXD6{ER>WC)pd`+`}pvTnnnk9*&{ z3lMS7ZPYDSG&#>~|1z}IGWzfyxS1-1FHV1dCfHco)oL`$DVOH@U5Po^QvxT+l;PqJ z9HCPaks}yFrH%0Bo7pOcQbo`Zo`kWz&qnq;K|1(G?#UMIjZnW0E@iJ&+h)%023LH4 zXy9xwC>y0{T=+?ZfR!ygqmeCvyi?GWUe` z)$4`*okmAQ+&MD(5ftsBqbRDTYZXMd=9W zmiiECo;rqhbMP%Z@}|3oAK3LsMK!2Cdb0w?(WGLwS^U6G%ZPZoOS4@wm)rTa+P5~) zTy6QRxyHGUYx@hHqU3rI0M$|WB z5$YZ<-H*G+$6MOQagqK!SumR{;)*~F7nm!SvoBsoW&e@Hz-fZn$gL1oY zqf2Km$W$M$`}b@JUw@yelWPE2C>5dOA5Stc=yu^~6Pk<*NK-S|V9{p_troA_Ya1#D zcWvan><|L<$z*k859|W(4q*JdwfXRiSbPqM>0+52Y;x%tfcF z^4s5=s()ou8pLlU1#o_7J$7YxB0^*MFxXmq;#I|qK|9wxRLN;~JA@G!dX=AH{do1! z?TL3)rH`dU9+2UUsOZTxV~n2}Xs#MOG5do`Imd=LAMfdUK<32z-Xr2XSp=AMbyt&? zvJ6Y@K?f1a4r6C$o)|4wCUAQQ_>B5&Hv0vMdb0N{7K4G4S?w$2B~iYm1I%8z4(jUcaX=MN_IkLA#%N#l@k&EXL>|vtuEugbrA4I8BuqSWYY71WdJBRtaoY z95G^4^g;i<)tVL+AZoa_V%mVn>DN=lIDzS?-JI!r+lRnx5prv7VgTC)fnNIX zDXf)qTXG?MbGknt4D$dP6n=P)=Qk{U#2WCMv|poEMecxURZC;hf!N|dfSbLYgCG?-~qmivKc#I?sp+S zfULf-R?|w*W=SC{1(DSz4x+~+8GJi+CBa^z@c{a`_ zmy*-XC|I?p>5X$1RpgYG<$aP~9p#i37$s}T636NSNX3jl<27@KZlF3rm5H+@&S9~J z+yWwneRHj1-iM}4A;UN~f#_$Lc8mQ?`lkM870Esq5jk(=LCthi(OR|8yo~p8zT4sa z0sy;3J0ckD2czMjG(-s+@`5N#M@6W2_$IrnjBSX8rV(Jg3A+msLXpl?R~UHzT9t*a zr+(N6WK}8=Kme}gvNTjl2pJaTLv>(%%QqohQjTB1(4CG=yv##$yD@p;KWUs1%)*vi z``#*^QV_Lq#yJSIen=YH3gy{}*l9Mkx1!HmC2%3r6^qf-Hh8JA*=<$Hg&O*ZP(LGK z4>*;=GJ#7UOCW=|Nn5A1i4~v%APGc%EJc&PSMe>KZ0Ygbv6c~@9H<+WGZno2tztBD z=RlomSDl=AY#}ldznnB6wk%{IS^@1tD5kjT(ka8@qhm5q?7!^w#1g9nOIuzgr)b za(&(a*u~`8FWUVRRfl@&VGit1Ms#jjP}?=_%t4RP=bRRhu1g0I4Ul9Uk|R zDsgAbKQ26hG_Fy-NGBSr$maLVAvK(A;*?BOJje3%Ag&^mY+{fYr2Cd1Kxn}XF-0e!UiB^Q;CaL;!Q&k{cPIL0V>L!lbKzeaiLRsRtAOgfFX`|{yp zmienDIAM{H-ArZQxm;dN$Dbmw&P$;8N<*;Al*08^-7glnW3^f7#qI?t_hI&hN;jIg z_5D)c0(arursoT9?37&#X(9qLzDfVxHGAS#@BTX)>V7V-%WKS8r1|Lfy0+R!iklYI zYpI~nix;EUC^<*)dQ0Rwb=3iG+{ngh3%@ntbg;J_-waPfx-L}$JNP{yP-A88GICmhOT=Me|yr(iIz-SJq- z1CoD1xbK&A3OilSIbSY4-mA*lnbD+TN;1~NL;Ebgr5Hly6}O#y|1$9qk@86aUDvJuVTQZlC5zX_WIm%!WkHpRcM$X$obX`7|}=wA&+W z@f}loT_3wMdZfA5hxL%n-!P?R8rOyKE_DbqE#0)aFAYt%4;*|-pSRbi!$W2Xc0ICC zRN;-|G=p}~?3N=3QdmF-zG&Jg5^833HHsa&=4O@RMDI(N2u97_k9u*Qbnee>;nm8# z#po~NaW-Vnx! zr)^gvUnjmebl;ysMbL+4MrS{rYL@A+9B$@0SOGu6E0Q{Rqvg5aW)Uq$Mi0J%d2`s` zHHTvw^aC)?285ro9lx_1CQw*mj~&5eV*UCdTwxYIJE*LW<<7ho@sr?(-rHhB$cM7_E-gg2Pkf?E2{ z;*9bsnCgv&{_*b3zrmT)aKcL1`4bQ1P5Cr$rI~K4bqTkHI11*;5o4Jv^B2BKWb_;a z+b?nsi95V#X4hH{cdCp$C?q!p9*|KWLLHtvpz&-QVCJ^GqAI<%M!PvBLNke29!nRO zl)4G=QeeQW01*t3UV_O%#QQH+%9bg~s3kq2d{6G{x{h6qIQtIAZj&~`jE3%2t=*CJ zQ~4iNA(S&$5pZJ~*$R_j)lPCie6@yi99_#<9N^emq6A|LDK$O8ar^&t`~)0&O@`0U zIe~l9P7N!SKYhXe1(eC2^V1O4ylap2S#GyuOFuX|-=xN|5q3I%!eyn zY_-kFyQ)Q645eDyPIT&j#S!yQvns3wkXrq=o_$w-T_LYkXracjDoVX;qg zzb(9Gh2443s2nTdRZQ&3_m=V@nO~3@dHScV432*a1XcA6 zg?JGrEeyIH1-Gr?P?$$eRhHVWG^KDPcv?^Esxla9ZdJMjk%PfYP{`S=EIe4OYh&mH}&1V+woY#7&0{Nc+|@mqrHCM*uamWILP>XA2I)ch4wRE zkA|EVI*ZXlARntj=4t^G`hBZ>D#00$PJ)OW8jywoWB(7oG>ik))s6SR`6w`@`d$Vk zc^v`hoBx~nDnhvd7Ttv5$v_tDe|~2e$U;{_fQ&G_2k_4S2aCllWOOFK?i|nkIJxZU z-P;Sv|4n8A)f{8z)j}P08Nj_Hp?nw-^PIrU#W&`WYp~wSVf+iwDqFR(0Nn&WifU^>6b^~)99Rpb%XOyM66G}8{) zeI500rS5=52IKE{wGYV(X>p2ga~6HL4xnrfkYc~1tlVmyVhu)KdIw-O+F}i6pp+tD zSQ!AgXR;&%du+{bpn)qnF@r0B^*rZM25}km~g__Js_ZG{O2K(r7J+xq6xYtuvADel!?q;{rdGQ z+QjpodL0Z5@#?;yd_EmbR!VGoPhGfv2Mk`Ex&Upf#>;4)!;NQoVg3Ur-v%aq9&68T zeEMo!iP^=5a8P_<#+LyJ?zkhyyp9@1mq9*Nog<5Ka-aNo_!5gPdqu`KhT9-uR-cH%DJ{EkRjqNGw6x z<-XAIV$YehXmN*{z`#J@wM{1@>mxc66-UE$?*1>+5%tOU}5XfT^3@0TSH z)t{pe7mXn+$V*4~H4O z=r>4<-;6oE=5MT2ktd$C$8rsdt;RAb$#QYh?Np1LTK^^@n2x=ZB>>rI92MUW^4Y&2 zyw_*7VGBY)RO18`2NQ1~K~SL6td4U`0Ua0kLE=kUYVVt7X1~ZHxi^*bgGB+*zPDCH z%S0a?^|D7m3*rrE`S3;#>E{>2T!6?C_>>-iGv#D_HQ^M-Dc1!5_QeXWn1XU~n(GsE z+M{D&IMOlv^T|j6B;PlZug_lzGRMOc20EQ9mbc{oT;i!NoDGno8D;?)36kxF(~qN1 zsZ2NlnxmfI8R)%`Oj;n_u>k)43?iow3=`|4kcZ1`WHljq(eoS}KAm!#S~+@M`;-ic z|Jh+APyn}c!)+-lzM$wp*$+MecbW!`rZjnmf8;=5?Uw3;ow+Aw`y+0n&DIo3iVz4J zA}2V4PvafY7q}j%L6b>xLvh28DR*8BO%b}yjnTMLohwPTwSzJ*KKOQ#PU>#fjDu0A^>p>wPG zPt^?}jJOvLKl*U$i_fg#)nBdP#tiK8J-9b$z>ib-k8vT`*BVpvA}@1acRBQ~8$1pZ zo*7TS5vJ`sQS`dS*I3A(nn1usrx!QbGx0axa5h7mVC$Q(A7FHdy~$XIcPqbv%p2sG zy2qn9>G}UGQPYLXP>;V3grFdvPLjovz{?o$QqP8_)2Ut;>iT*#q#Y8fRiqCPVY_Q@ zjvtTYD4tmq>A1AyKzxJBkD@E2gNs_y3-vm$P*E+7e?<@hNf8|2(3=$*M{V}>`jB4Y zhyWso8G!MnmvxeOkQum`4y{ZAq{&X;pAXu>mhPHOs#rx%ciS~J82?}iYZ%XAmh45h z{<}`d1VzFqPB&d4suaiXtm80@ao!~O>@uX5`i(TDziq*m-5&hhvs{B-IA;|1kZy}Y z0+BOh9|fFa723D?Z4832lS%r#iE=qgo?H&ZPZ##@)5Nxy8~D2LF6385Bv1X$G7-^D z`kcS_bs~BUbdGNX^L1_IFO4XyDctK48;Z`_!1?EByesP3@PMzfEYn@b>tT;h1X_u; zmvxr%4tBego`y4gE`$;S*~Yl zu=OjD(^0onGlbA$9H5i5alib=g}_f`JbIK6K$?KrY@^#&F}djknJbS?L#-#5pD0S*iI$M$h$9#L@ql>Sx5F1-XkNC~YdryA;46mNpPn7IwE^JlTcDyL_~98%snsJD!K>Vb-W zU>4jAw>UWx!Gi`r_(Vg4bfE zR^~a69Pmr8ZzA5MC`Pv$Qy#tH-t*mCJ);?$R=fBQJ#IACPRiK1N6;(N3Cc={nyl$l zH(uQsl5#ryL>(SU7 zpzb8dpf-ob0!jQ8zK}0~Iv+oi_60@M{eA766-P0Za*5Tb^>9-0l1cqu zAJ)47R?tKoO?bQ!462alOu6byP_u_Ii}{?OpgzmO z0+5O_%=|cL&d$?>TD=^!9+fN!kALJ$fmD5g?ifLiHM*`M*sSB5U z0ieS!FkR0tK@m%9v|&Xm%_e3M9_ZUI(UZ3vb1~>*3c<^hZ$RY|#gNo|;t~?yd_HB>6C~Hxuv9$aoC0jMf;I&so)SD3zLzYYom3?`$C0kLJ2=A+G36m|7 zt&FS@Y0PBF*q4l5*6(^ozwi71o1>17qnYP^?&p54`&!QXJg?Yzaq+UpToXH3SXelz zxY6Fc*HM70$O`Sa7CzEJB^!z=eKekf1N%6x$MHXdqQ(J-c-H;skk;|5dBXhsUx56r zd}7}(#Z!_qqrF4%?(zWO-4L#Vz6a}HEJFxErHoQ5TDiw}zTWwVv{vianJK2Ohd&AAE3~jwdwZR}x;Wu=;r&64rdlaQyel0XTdPT)!fbr5jS3p2O<<8h{LBUcYU00dw8Z)Eupeb2@ z2vpGM)-b&mD#^}%aIN&VkBf;>L#C^LE?#nGd>F(2$I#Q+?~*7@@Zg!X=Ew;H8xXp zC9N-=Fp1vh98{VOK+yNpXcFKT#8tfY&B=OvLt1xtjuXujI1@J4L5L9{$L<^W2Gi}O z2t}KYIa(!PfDwfO7Qrh|QAAN?_`#7m^B(|qCoxK#V7{w=l1^wyNu zzf2Q`upE(J$)t$1H$2#ock$CdfOT(z~MUo9l+v5<61Iph62ii$~=r%On@9K312pkSk?r>BGN%j|ca%oxVN zO>BJWmx`pa2b1@!tJndRF#N8zkcl6|kPVN)m&~@#oYhIJcjA=(;^S4Bk`-IZgjaz< zGbD{-e^DHDx0m_CyU1rnkQdH2htp%O)i|cgeU#cH)bvMkkhrIZUWIY;@It~#u&J7V zd#g?F+2M|q5WmdEOiEaZO9G_$^)>+kpIV=tX~fsG?=2&xGF|C4DlUeZ0TsIb(0trb zP&xN7CG;xu4zrjle0k;6aMM35j~CUx$4{J);x}q>4lqhr&YO63LiR-E;Dg6hCF1gc zQ|TeYUCU*p8_jfZFOZRInmdd{idz8*LL*+1xf}VYlqNT?Z*@jiGd;5% zH)F6=QbV9|kz%TeSI!XXs?H~a)G1QV^rV8iLDxWVA@>nHlD~R}{IYEf;@a92#7CBxZ zuwWtAMBodx9x^De(B4X@KFD3C))LbWRT#+ z{a&+Tk|z8KmLFi<&aLI|PF$(O2_f$)J!;>-QBMm)h(Oxi1+SO+gXi{KbP#8P80T+f z$&t&6@xWgL4z&(wwvba^l&}E7*`b&e2Y}$wI?qp!4!Ip@`{%#;{8Zvjy2;B6;eSDa zrxu98oTgItcV-z06=>CM8zJ+yqO@}NEQ$;yocia^J%S+xnM0m}O3G{Y>^juX+YCB6 z?x0elt*x!`GQZKax31O*k&KGNmRRgmnUe} z{?JT->kc2jk7Y-_fI=BW5Fqlcw~@VGai#4(bBCmZP{LfS%&{{RezQ|o#Q6dCC#d&u za{x*-nr$0;vAl&foL!e+kFW~>0~6L7wb}GGC<^N5;nw6bcurtm z{%=9X-3!VYAOk{h1Ab-TjrLkHXt99E(w}X7q>i$G+iw0TKu)0K1Pfk)$u^sT0`TU? z+R&YJkaGZw?UCF);NT$W!Pv3kHxj|}sDmLt*wGxkB84Hdz&`^SJg`a0$Uz@S38RV8 zp1~6O@t^cg@1zQQABtqDIR@YX$MsNE5E(p;8GA%xN0-XoZck^i&f}sA%cTMz+$cqf znCu?Py?+k!h-T)M6}zolmjB!lXx#E##RCzpt^E5HUS2~KT2j2@5GxOcR}6bjcoLNJ zk@g_M2i?nKfom`ukbDHJf?VL@(aQ*t4SWCzbT5| z3l2p+SmL0zG7pMzp_s%9YRA1^7MTV~fD1I4x$Yov3xjfStS-L6%6B#-1(>MfREj*) zb>F}i%cmy*1opTAHPGrnmk!6~<4A|Sz(+7A(%$ju@ev&{qi3hj8?vG*cM^ZJVGQe+RMGlI*(e*aNn;+l~8kfUUwgOFu~UY){ufp85=gg$(N$^gh-fyF$s0+8ukytnA#6K!Z7xksehfwzT33bv9CZ(i) z{_EM-UDM(yMEdE<;f!izyjvhhnm5q*O2 z*WoQ@V4`C-@2*b0-YW7hN^>cmMKD~!lLv%_k1cItpq0R2H8|UZWz?025fQgQMGI&u z0HP8=hOT^?fw_vEB7P@>GXXBMuC)EP{OfVCvA>N}avo!-RN_P~OP1}an(qVl_0glc zBCkNY?97pS2hPR+GsBVnpSibYMK^(*E&+zqY}ZFV9;ym5lJ+2;#|_)$Q?%FiE^UGH z;Dw3({mzxH9{ppi1f-=!)~3X5r<+CPnjxHX{(;_7;+<6Rq=m977O}D4yzGf4+I{ksaD+OZh8o zisI|XzT`uBfWX=Bb{JC1T+%#sN*p>g5f`Dm>YjUs$44aP^7N@7o@l!^Ao z|Nf-a-&Z0E|bh2cn7P5d!*_$dVG! zbcLN=r8@yR7B+f@+4T6l9;2T2e!)~F-e1K(Tj?BkGNbq5(D>6^5Q&wC+kmiQkSM1i zmTt1AAs6y9kj88E?P{L@@Dp@XF%KWw$g!aMq%NXwi6Vp7jv>?ZhCG9Q9_W5q_wp{N z{b5^ZQ#-V0P}rJ*5xfaKKHk`#Cq7X~ZXK+KoMy5!i_e;SR6Y^5enMtc39pX`WSie> z*&7s?JCw4;o5jt)wd$C+c+h&lJ@@kWDnE0PuOB;z&=_KB^&c+UjeortCjiBSoCnn> z2`8ziet1&i?9f~gw&6*I5Gxfgl}|4i8yf@1DTmx?v=oC8iMFdi*3+0u4hvYPcs~+W za{<@BuE%{8oQZj+!G3lD)WF7*Qql+On3)p!1_e`qTIjGf%T9)u@ejIKss{`tobR4I z!@GaAhwojR#k>6pt|vO&%t0_6Dh}1-rEPh_O4nJGr+Nb+&y_;=A+(TM_90GFq!b7i z6;k3n;__~wmV$E*(tcI%uI$H0Liy4P2W?|3q0eyjJy2bN2oU7xX@}TQp2}f_ygL+n z+~^f|=}u@fEF4qko+}De-%*p$y-ckKtbyJS<*(7dY|&gI%?n5j*Fv*aD7g**d4x^a z1Qn>55hY5;WdfLwM920B!nuxg4c5@TAl-nNMU?L3f^m@;=diW8VRNnnXko~gAr5#6 zrTB5g!$PE7C~+S(jBD(IV@)Jd9)J{7q&WrT+sNI{!ePtRWDLP=q-A_PW*_QLk75$P zNb?=Qh>@?o_XLHP5@@>G+RS&MjkXWRCs`tE;(-5C$VRRMwhx2BfUu8bEwWl@6*9>H zN+Tq`4q)XI9r~+OgLa`9ZV;Y-KjJBGU^{uINBt=Kc;jC{BW&bIj-5x9xljX7wb9Lg zN4S%j8HOp?1F~wLf$ps5(ZkCmpDCbwDpF(ariU}rTc+oAe0IH5w36UL7~)( zm?ZmlB(;^0XoU3;=4?=SpZIn{5mlr98c=mOlz@ICc-V$=$D-}dDiC_!=pa{ClbpUD zmropn9uM_4fG?rs_D0*SDiEMo1oHJ(q1*5U`9#mv@vQv@E0vI2b1y$agt#Fnlu-L5 z4&&5yteocd4o(-FLm0f7AT9r-z5_MDf%Ke#(~9M>jv%=u4)B`tNe08chFC|WD~2X2 zu^A|Fw%EkzgGcgx7X;kRhpjB5->F z2!7Dg%R;*!Zw7*77=IYfL}+E7(KN)nZC|;yF%tk-nbcpoUC?M_Sb7B#yug``Wt=WL zOK!UNohfs{4ut-0 zDB_ULojb)TY-wQH3__cDGYEh(t2bBmnbUEHcn3n{!TN|aC8B2AeHZYJD_F@GJJ6eV zB3OVp3kvkjATJLaY3FaqjW85~n?IsHcIJ>{TlF>KhX>kLTf&OS(T9z(4KUCFJ3GuVC$n z+c&Y5jm=w)9tKKlYr!(ZbrGk%Tz(}*9#Mzs`X~iRHy1};FHczbf*GNSvY`COU7mFrEb|_CIm|2}$7FzhxTUL=vV#-@iyGC9SKYQ2untr6GIHhw{U~ATotb z+^!%>KvuNZeSlIv8QvYPZ*}H#{MT&Rn>zhItgY4;B~x2m6M~`TA}=Pad(**XE(7pf zmbcF=e-Hd0*ye$wqH4Z4?xSMfV~6S};)S^;<8vNqn8ha$6b=C$xRo2=b>PXL%>yNE&2C<|p`AC;;*z;biE|g`N%}o49?GKJE zv>>vDFBagF;s|+8vQqYlEA)(klXC#@vi&z!eHoN87=q+Z4SV)Fql%oCMpB=kozBj? z+a+|TKagkZx#2ljPuAfGVWi5zQ8VuA*X*Go-VuB_q1>^inH6;>c;G-X??R-Bf%Gdg-;=_8Nh^Zl3*wx zJ>e|0Q59~RI6-0IF=z)&)o(KMpy8w|Egs~3svfcF#`v>?3+<}%TZ31*7qpbXI)GP+ z=-J!}zFrr&XxRnhFSw%h{B*o!^*hND7oGs$Ko_VO@%jUHp?u;pLVE)HQn2yLJ>=Y_ zShwz83}6PjEJpn(Vj7sO^?9RdY6+K+%sQ}P8_gIe+XDJLZ#T%%B*)2VrRo!Lrb)Yw zt1vv&T)1mkktwWs;lVbQk$!YFuPmzl-vfOcT)nFILN?eIef@4O!Ai}BGhbySal=K@ zY|SQTNTurlF>&$=tE=K@j9r^$*0okklQ!6iD25nhl&_$?AlcNzS;ur%4NC(^HNaj0`GGT@u)RkL4zddX)OXCX5~=UUhstcjJ)`!z%Vm}BvL)8Sr`XJGarpUY94$S%qJMrvuqbeC@Ll1hlm zx;i>fj~Ro^QvhC10ua|moAQ<(-;7f(Zf&{cX6O|Dj@^_8lXU+=^_=;H#vyRyFCjoF z)5y=3VgV;Xi5+DZK$!g?{@nKBnb6#%Tw$wm2TNx9+N#US%r$eVeBWNocW2JyTdVP> zL9~am(Cf!1brSOIWRc!N0|?(`4+njuH5ppmn77?GYR-*1IeHYcixj;o243T*j%^FOQZt@=Q4Dd@yaZeboQ=E3^_+#kyFS|R`>Wg6YqJA-U(KL$A_5yu2e`6ai%&{=u z8HQcrSfWv4pCl&k4xl%U`<>7&KIZV=-w6ceK?y%q{f2cdK>BGCx~$938|m^)QqF!+ zF+kssoIc!AnO`4t|6y^Ozw`#11LPVqFX^NfOX&Q%J@0+~pb~}lC5u0Mld(%)OH?K6 z=H%s3Zk~t-Ezc#99y~Km39>G!-yfIPX=&+KC(hnih(BSjHe+(=&7ZH;1WZLMce^ik zwTY{FS8Ce!aae6`CO&`l84d>#FJMN-Tz!-9#A73-;yu&5mO<~WBw>?W78lsq5^CL;nw%ILxE>xIm!e9Phbsh&=o z#;Li(a#%ka4q6d0!%t^X)IP}OQ4ATVdp@PAt^t^)CAM}PzW(e!hXhk__b7h?AQyv- z?`1Al$~^txycryAB1pgnQ>Y1$kdS~TSrKvCv&~xLG4F~dSKIs;of|m!~c74C@i!qhFX&5 z%liG}iUyEg163Ca@cR*kI6G=30n$*U!Cvjh)x}A;T&9C;v-0%w^v5`+#WmoQC|2bG z>W)4pNR)|zB--%#*^rPB0;Ny`Y>oy1m8sYpNczfmqtZ`+H6etWcPGo<`EOKM?40=% z>4NnM@(>V7gB(~Y3lhcy(RMPh2&naDC}u(L9K1^aur(wFMQRjOVE?ZbdQAPJL5Sc; zf2{w@Zx - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" -#include "tracer.hpp" -#include "tracer-qis.hpp" -#include "TracerInternal.hpp" - - -using namespace Microsoft::Quantum; -extern "C" -{ - void __quantum__qis__on_operation_start(int64_t /* id */) // NOLINT - { - } - void __quantum__qis__on_operation_end(int64_t /* id */) // NOLINT - { - } - - void __quantum__qis__swap(QUBIT* /*q1*/, QUBIT* /*q2*/) // NOLINT - { - } - - void __quantum__qis__single_qubit_op(int32_t id, int32_t duration, QUBIT* target) // NOLINT - { - (void)tracer->TraceSingleQubitOp(id, duration, reinterpret_cast(target)); - } - void __quantum__qis__single_qubit_op_ctl(int32_t id, int32_t duration, QirArray* ctls, QUBIT* target) // NOLINT - { - (void)tracer->TraceMultiQubitOp(id, duration, (long)(ctls->count), reinterpret_cast(ctls->buffer), - 1, reinterpret_cast(&target)); - } - void __quantum__qis__multi_qubit_op(int32_t id, int32_t duration, QirArray* targets) // NOLINT - { - (void)tracer->TraceMultiQubitOp(id, duration, 0, nullptr, (long)(targets->count), - reinterpret_cast(targets->buffer)); - } - void __quantum__qis__multi_qubit_op_ctl(int32_t id, int32_t duration, QirArray* ctls, QirArray* targets) // NOLINT - { - (void)tracer->TraceMultiQubitOp(id, duration, (long)(ctls->count), reinterpret_cast(ctls->buffer), - (long)(targets->count), reinterpret_cast(targets->buffer)); - } - - void __quantum__qis__inject_barrier(int32_t id, int32_t duration) // NOLINT - { - (void)tracer->InjectGlobalBarrier(id, duration); - } - - RESULT* __quantum__qis__single_qubit_measure(int32_t id, int32_t duration, QUBIT* q) // NOLINT - { - return tracer->TraceSingleQubitMeasurement(id, duration, reinterpret_cast(q)); - } - - RESULT* __quantum__qis__joint_measure(int32_t id, int32_t duration, QirArray* qs) // NOLINT - { - return tracer->TraceMultiQubitMeasurement(id, duration, (long)(qs->count), - reinterpret_cast(qs->buffer)); - } - - void __quantum__qis__apply_conditionally( // NOLINT - QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, QirCallable* clbOnSomeDifferent) - { - CTracer::FenceScope sf(tracer.get(), (long)(rs1->count), reinterpret_cast(rs1->buffer), - (long)(rs2->count), reinterpret_cast(rs2->buffer)); - - clbOnAllEqual->Invoke(); - clbOnSomeDifferent->Invoke(); - } -} diff --git a/src/Qir/Runtime/lib/Tracer/tracer-qis.hpp b/src/Qir/Runtime/lib/Tracer/tracer-qis.hpp deleted file mode 100644 index 44918fe00c8..00000000000 --- a/src/Qir/Runtime/lib/Tracer/tracer-qis.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - - -#include - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -extern "C" -{ - QIR_SHARED_API void __quantum__qis__on_operation_start(int64_t /* id */); // NOLINT - QIR_SHARED_API void __quantum__qis__on_operation_end(int64_t /* id */); // NOLINT - QIR_SHARED_API void __quantum__qis__swap(QUBIT* /*q1*/, QUBIT* /*q2*/); // NOLINT - - QIR_SHARED_API void __quantum__qis__single_qubit_op(int32_t id, int32_t duration, QUBIT* target); // NOLINT - QIR_SHARED_API void __quantum__qis__single_qubit_op_ctl( // NOLINT - int32_t id, int32_t duration, QirArray* ctls, QUBIT* target); - QIR_SHARED_API void __quantum__qis__multi_qubit_op(int32_t id, int32_t duration, QirArray* targets); // NOLINT - QIR_SHARED_API void __quantum__qis__multi_qubit_op_ctl( // NOLINT - int32_t id, int32_t duration, QirArray* ctls, QirArray* targets); - - QIR_SHARED_API void __quantum__qis__inject_barrier(int32_t id, int32_t duration); // NOLINT - QIR_SHARED_API RESULT* __quantum__qis__single_qubit_measure(int32_t id, int32_t duration, QUBIT* q); // NOLINT - - QIR_SHARED_API RESULT* __quantum__qis__joint_measure(int32_t id, int32_t duration, QirArray* qs); // NOLINT - - QIR_SHARED_API void __quantum__qis__apply_conditionally( // NOLINT - QirArray* rs1, QirArray* rs2, QirCallable* clbOnAllEqual, QirCallable* clbOnSomeDifferent); - -} // extern "C" diff --git a/src/Qir/Runtime/lib/Tracer/tracer.cpp b/src/Qir/Runtime/lib/Tracer/tracer.cpp deleted file mode 100644 index 4e9f9510611..00000000000 --- a/src/Qir/Runtime/lib/Tracer/tracer.cpp +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include -#include - -#include "tracer.hpp" -#include "TracerInternal.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - thread_local std::shared_ptr tracer = nullptr; - std::shared_ptr CreateTracer(int preferredLayerDuration) - { - tracer = std::make_shared(preferredLayerDuration); - return tracer; - } - std::shared_ptr CreateTracer(int preferredLayerDuration, - const std::unordered_map& opNames) - { - tracer = std::make_shared(preferredLayerDuration, opNames); - return tracer; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::LaterLayerOf - //------------------------------------------------------------------------------------------------------------------ - /*static*/ LayerId CTracer::LaterLayerOf(LayerId l1, LayerId l2) - { - return std::max(l1, l2); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer's IRuntimeDriver implementation - //------------------------------------------------------------------------------------------------------------------ - QubitIdType CTracer::AllocateQubit() - { - size_t qubit = qubits.size(); - qubits.emplace_back(QubitState{}); - return static_cast(qubit); - } - - void CTracer::ReleaseQubit(QubitIdType /*qubit*/) - { - // nothing for now - } - - // TODO: what would be meaningful information we could printout for a qubit? - std::string CTracer::QubitToString(QubitIdType q) - { - size_t qubitIndex = static_cast(q); - const QubitState& qstate = this->UseQubit(q); - - std::stringstream str(std::to_string(qubitIndex)); - str << " last used in layer " << qstate.layer << "(pending zero ops: " << qstate.pendingZeroDurationOps.size() - << ")"; - return str.str(); - } - - void CTracer::ReleaseResult(Result /*result*/) - { - // nothing to do, we don't allocate results on measurement [yet] - } - - // Although the tracer should never compare results or get their values, it still has to implement UseZero and - // UseOne methods as they are invoked by the QIR initialization. - Result CTracer::UseZero() - { - return reinterpret_cast(INVALID); - } - - Result CTracer::UseOne() - { - return reinterpret_cast(INVALID); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::CreateNewLayer - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::CreateNewLayer(Duration minRequiredDuration) - { - // Create a new layer for the operation. - Time layerStartTime = 0; - if (!this->metricsByLayer.empty()) - { - const Layer& lastLayer = this->metricsByLayer.back(); - layerStartTime = lastLayer.startTime + lastLayer.duration; - } - this->metricsByLayer.emplace_back( - Layer{layerStartTime, std::max(this->preferredLayerDuration, minRequiredDuration)}); - - return (LayerId)(this->metricsByLayer.size() - 1); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::GetEffectiveFence - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::GetEffectiveFence() const - { - return CTracer::LaterLayerOf(this->globalBarrier, this->latestConditionalFence); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::FindLayerToInsertOperationInto - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::FindLayerToInsertOperationInto(QubitIdType q, Duration opDuration) const - { - const QubitState& qstate = this->UseQubit(q); - - LayerId layerToInsertInto = REQUESTNEW; - - const LayerId barrier = this->GetEffectiveFence(); - const LayerId firstLayerAfterBarrier = - (barrier == INVALID ? (this->metricsByLayer.empty() ? REQUESTNEW : 0) - : (((size_t)(barrier + 1) == this->metricsByLayer.size()) ? REQUESTNEW : barrier + 1)); - - LayerId candidate = CTracer::LaterLayerOf(qstate.layer, firstLayerAfterBarrier); - assert(candidate != INVALID); - - if (candidate != REQUESTNEW) - { - // Find the earliest layer that the operation fits in by duration - const Layer& candidateLayer = this->metricsByLayer[(size_t)candidate]; - const Time lastUsedTime = std::max(qstate.lastUsedTime, candidateLayer.startTime); - if (lastUsedTime + opDuration <= candidateLayer.startTime + candidateLayer.duration) - { - layerToInsertInto = candidate; - } - else - { - for (candidate += 1; (size_t)candidate < this->metricsByLayer.size(); ++candidate) - { - if (opDuration <= this->metricsByLayer[(size_t)candidate].duration) - { - layerToInsertInto = candidate; - break; - } - } - } - } - - return layerToInsertInto; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::AddOperationToLayer - //------------------------------------------------------------------------------------------------------------------ - void CTracer::AddOperationToLayer(OpId id, LayerId layer) - { - assert((size_t)layer < this->metricsByLayer.size()); - assert(this->metricsByLayer[(size_t)layer].barrierId == -1 && "Should not add operations to barriers"); - - this->metricsByLayer[(size_t)layer].operations[id] += 1; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::UpdateQubitState - //------------------------------------------------------------------------------------------------------------------ - void CTracer::UpdateQubitState(QubitIdType q, LayerId layer, Duration opDuration) - { - QubitState& qstate = this->UseQubit(q); - for (OpId idPending : qstate.pendingZeroDurationOps) - { - this->AddOperationToLayer(idPending, layer); - } - - // Update the qubit state. - qstate.layer = layer; - const Time layerStart = this->metricsByLayer[(size_t)layer].startTime; - qstate.lastUsedTime = std::max(layerStart, qstate.lastUsedTime) + opDuration; - qstate.pendingZeroDurationOps.clear(); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceSingleQubitOp - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::TraceSingleQubitOp(OpId id, Duration opDuration, QubitIdType target) - { - this->seenOps.insert(id); - - QubitState& qstate = this->UseQubit(target); - const LayerId barrier = this->GetEffectiveFence(); - if (opDuration == 0 && (qstate.layer == INVALID || (barrier != INVALID && qstate.layer < barrier))) - { - qstate.pendingZeroDurationOps.push_back(id); - return INVALID; - } - - // Figure out the layer this operation should go into. - LayerId layerToInsertInto = this->FindLayerToInsertOperationInto(target, opDuration); - if (layerToInsertInto == REQUESTNEW) - { - layerToInsertInto = this->CreateNewLayer(opDuration); - } - - // Add the operation and the pending zero-duration ones into the layer. - this->AddOperationToLayer(id, layerToInsertInto); - this->UpdateQubitState(target, layerToInsertInto, opDuration); - - return layerToInsertInto; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceMultiQubitOp - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::TraceMultiQubitOp(OpId id, Duration opDuration, long nFirstGroup, QubitIdType* firstGroup, - long nSecondGroup, QubitIdType* secondGroup) - { - assert(nFirstGroup >= 0); - assert(nSecondGroup > 0); - - // Special-casing operations of duration zero enables potentially better reuse of qubits, when we'll start - // optimizing for circuit width. However, tracking _the same_ pending operation across _multiple_ qubits is - // tricky and not worth the effort, so we only do single qubit case. - if (opDuration == 0 && nFirstGroup == 0 && nSecondGroup == 1) - { - return this->TraceSingleQubitOp(id, opDuration, secondGroup[0]); - } - - this->seenOps.insert(id); - - // Figure out the layer this operation should go into. - LayerId layerToInsertInto = this->FindLayerToInsertOperationInto(secondGroup[0], opDuration); - for (long i = 1; i < nSecondGroup && layerToInsertInto != REQUESTNEW; i++) - { - layerToInsertInto = - std::max(layerToInsertInto, this->FindLayerToInsertOperationInto(secondGroup[i], opDuration)); - } - for (long i = 0; i < nFirstGroup && layerToInsertInto != REQUESTNEW; i++) - { - layerToInsertInto = - std::max(layerToInsertInto, this->FindLayerToInsertOperationInto(firstGroup[i], opDuration)); - } - if (layerToInsertInto == REQUESTNEW) - { - layerToInsertInto = this->CreateNewLayer(opDuration); - } - - // Add the operation into the layer. - this->AddOperationToLayer(id, layerToInsertInto); - - // Update the state of the involved qubits. - for (long i = 0; i < nFirstGroup; i++) - { - this->UpdateQubitState(firstGroup[i], layerToInsertInto, opDuration); - } - for (long i = 0; i < nSecondGroup; i++) - { - this->UpdateQubitState(secondGroup[i], layerToInsertInto, opDuration); - } - - return layerToInsertInto; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::InjectGlobalBarrier - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::InjectGlobalBarrier(OpId id, Duration duration) - { - LayerId layer = this->CreateNewLayer(duration); - this->metricsByLayer[(size_t)layer].barrierId = id; - this->globalBarrier = layer; - return layer; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceSingleQubitMeasurement - //------------------------------------------------------------------------------------------------------------------ - Result CTracer::TraceSingleQubitMeasurement(OpId id, Duration duration, QubitIdType target) - { - LayerId layerId = this->TraceSingleQubitOp(id, duration, target); - return reinterpret_cast(layerId); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::TraceMultiQubitMeasurement - //------------------------------------------------------------------------------------------------------------------ - Result CTracer::TraceMultiQubitMeasurement(OpId id, Duration duration, long nTargets, QubitIdType* targets) - { - LayerId layerId = this->TraceMultiQubitOp(id, duration, 0, nullptr, nTargets, targets); - return reinterpret_cast(layerId); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::FindLatestMeasurementLayer - //------------------------------------------------------------------------------------------------------------------ - LayerId CTracer::FindLatestMeasurementLayer(long count, Result* results) - { - LayerId latest = INVALID; - for (long i = 0; i < count; i++) - { - const LayerId id = this->GetLayerIdOfSourceMeasurement(results[i]); - latest = CTracer::LaterLayerOf(latest, id); - } - return latest; - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::FenceScope - //------------------------------------------------------------------------------------------------------------------ - CTracer::FenceScope::FenceScope(CTracer* trc, long count1, Result* rs1, long count2, Result* rs2) : tracer(trc) - { - const LayerId fence1 = - (rs1 != nullptr && count1 > 0) ? this->tracer->FindLatestMeasurementLayer(count1, rs1) : INVALID; - const LayerId fence2 = - (rs2 != nullptr && count2 > 0) ? this->tracer->FindLatestMeasurementLayer(count2, rs2) : INVALID; - - this->fence = CTracer::LaterLayerOf(fence1, fence2); - if (this->fence == INVALID) - { - return; - } - assert((size_t)(this->fence) < this->tracer->metricsByLayer.size()); - - this->tracer->conditionalFences.push_back(this->fence); - this->tracer->latestConditionalFence = CTracer::LaterLayerOf(this->tracer->latestConditionalFence, this->fence); - } - CTracer::FenceScope::~FenceScope() - { - if (this->fence == INVALID) - { - return; - } - - std::vector& fences = this->tracer->conditionalFences; - assert(!fences.empty()); - this->tracer->conditionalFences.pop_back(); - - // Update the latest layer (we expect the stack of fences to be shallow so a linear search through it - // should be OK). - this->tracer->latestConditionalFence = - fences.empty() ? INVALID : *std::max_element(fences.begin(), fences.end()); - } - - //------------------------------------------------------------------------------------------------------------------ - // CTracer::PrintLayerMetrics - //------------------------------------------------------------------------------------------------------------------ - static std::string GetOperationName(OpId opId, const std::unordered_map& opNames) - { - if (opId < 0) - { - return ""; - } - - auto nameIt = opNames.find(opId); - return nameIt == opNames.end() ? std::to_string(opId) : nameIt->second; - } - void CTracer::PrintLayerMetrics(std::ostream& out, const std::string& separator, bool printZeroMetrics) const - { - // Sort the operations by id so the output is deterministic. - std::set seenOpsOrderedById(this->seenOps.begin(), this->seenOps.end()); - - // header row - out << "layer_id" << separator << "name"; - for (OpId opId : seenOpsOrderedById) - { - out << separator << GetOperationName(opId, this->opNames); - } - out << std::endl; - - // data rows - const std::string zeroString = printZeroMetrics ? "0" : ""; - for (const Layer& layer : this->metricsByLayer) - { - out << layer.startTime; - out << separator << GetOperationName(layer.barrierId, this->opNames); - - for (OpId opId : seenOpsOrderedById) - { - auto foundInLayer = layer.operations.find(opId); - out << separator - << ((foundInLayer == layer.operations.end()) ? zeroString : std::to_string(foundInLayer->second)); - } - out << std::endl; - } - } -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/lib/Tracer/tracer.hpp b/src/Qir/Runtime/lib/Tracer/tracer.hpp deleted file mode 100644 index fcaccb96762..00000000000 --- a/src/Qir/Runtime/lib/Tracer/tracer.hpp +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once - -#include -#include -#include -#include -#include - -#include "CoreTypes.hpp" -#include "QirRuntimeApi_I.hpp" -#include "TracerTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - /*================================================================================================================== - Layer - ==================================================================================================================*/ - struct QIR_SHARED_API Layer - { - // Start time of the layer. - const Time startTime; - - // Width of the layer on the time axis. - const Duration duration; - - // Quantum operations, assigned to this layer. - std::unordered_map operations; - - // Optional id, if the layer represents a global barrier. - OpId barrierId = -1; - - Layer(Time startTm, Duration dur) - // clang-format off - : startTime(startTm) - , duration(dur) - // clang-format on - { - } - - Layer(Layer&&) = default; // NOLINT(bugprone-exception-escape) // TODO(rokuzmin): Clang-tidy 14.0.6 complains - // that an exception can escape from the implicit move constructor. Failed to figure out within a reasonable - // time how to make the move constructor `noexecpt`. - }; - - /*================================================================================================================== - QubitState - ==================================================================================================================*/ - struct QIR_SHARED_API QubitState - { - // The last layer this qubit was used in, `INVALID` means the qubit haven't been used yet in any - // operations of non-zero duration. - LayerId layer = INVALID; - - // `lastUsedTime` stores the end time of the last operation, the qubit participated in. It might not match the - // end time of a layer, if the duration of the last operation is less than duration of the layer. Tracking this - // time allows us to possibly fit multiple short operations on the same qubit into a single layer. - Time lastUsedTime = 0; - - std::vector pendingZeroDurationOps; - }; - - /*================================================================================================================== - The tracer implements resource estimation. See readme in this folder for details. - ==================================================================================================================*/ - class QIR_SHARED_API CTracer : public IRuntimeDriver - { - // For now the tracer assumes no reuse of qubits. - std::vector qubits; - - // The preferred duration of a layer. An operation with longer duration will make the containing layer longer. - const int preferredLayerDuration = 0; - - // The index into the vector is treated as implicit id of the layer. - std::vector metricsByLayer; - - // The last barrier, injected by the user. No new operations can be added to the barrier or to any of the - // layer that preceeded it, even if the new operations involve completely new qubits. Thus, the barriers act - // as permanent fences, that are activated at the moment the tracer executes the corresponding user code and are - // never removed. - LayerId globalBarrier = INVALID; - - // The conditional fences are layers that contain measurements for results used to guard conditional branches. - // The set of fences is a stack (for nested conditionals) but we use vector to store them so we can recalculate - // the latest (by time) fence when the stack is popped. - std::vector conditionalFences; - - // We don't expect the stack of conditional fences to be deep, so it's OK to recalculate the latest layer when - // the stack is modified. - LayerId latestConditionalFence = INVALID; - - // Mapping of operation ids to user-chosen names, for operations that user didn't name, the output will use - // operation ids. - std::unordered_map opNames; - - // Operations we've seen so far (to be able to trim output to include only those that were encounted). - std::unordered_set seenOps; - - private: - QubitState& UseQubit(QubitIdType q) - { - size_t qubitIndex = static_cast(q); - assert(qubitIndex < this->qubits.size()); - return this->qubits[qubitIndex]; - } - const QubitState& UseQubit(QubitIdType q) const - { - size_t qubitIndex = static_cast(q); - assert(qubitIndex < this->qubits.size()); - return this->qubits[qubitIndex]; - } - - // If no appropriate layer found, returns `REQUESTNEW`. - LayerId FindLayerToInsertOperationInto(QubitIdType q, Duration opDuration) const; - - // Returns the index of the created layer. - LayerId CreateNewLayer(Duration minRequiredDuration); - - // Adds operation with given id into the given layer. Assumes that duration contraints have been satisfied. - void AddOperationToLayer(OpId id, LayerId layer); - - // Update the qubit state with the new layer information. - void UpdateQubitState(QubitIdType q, LayerId layer, Duration opDuration); - - // Considers global barriers and conditional fences to find the fence currently in effect. - LayerId GetEffectiveFence() const; - - // For the given results finds the latest layer of the measurements that produced the results. - LayerId FindLatestMeasurementLayer(long count, Result* results); - - public: - // Returns the later layer of the two. INVALID LayerId is treated as -Infinity, and REQUESTNEW -- as +Infinity. - static LayerId LaterLayerOf(LayerId l1, LayerId l2); - - explicit CTracer(int preferredLayerDur) - // clang-format off - : preferredLayerDuration(preferredLayerDur) - // clang-format on - { - } - - CTracer(int preferredLayerDur, const std::unordered_map& operNames) - : preferredLayerDuration(preferredLayerDur) - , opNames(operNames) - { - } - - // ------------------------------------------------------------------------------------------------------------- - // IRuntimeDriver interface - // ------------------------------------------------------------------------------------------------------------- - QubitIdType AllocateQubit() override; - void ReleaseQubit(QubitIdType qubit) override; - std::string QubitToString(QubitIdType qubit) override; - void ReleaseResult(Result result) override; - - bool AreEqualResults(Result /*r1*/, Result /*r2*/) override - { - throw std::logic_error("Cannot compare results while tracing!"); - } - ResultValue GetResultValue(Result /*result*/) override - { - throw std::logic_error("Result values aren't available while tracing!"); - } - Result UseZero() override; - Result UseOne() override; - - // ------------------------------------------------------------------------------------------------------------- - // Instead of implementing IQuantumGateSet, the tracer provides 'tracing-by-id' methods. The QIR generation - // should translate all intrinsics to invoke these methods. - // The tracer doesn't differentiate between control and target qubits. However, While it could provide a single - // generic tracing method for and array of qubits, that would require the clients to copy control and target - // qubits into the same array. To avoid the copy, the tracer provides a method that takes two groups of qubits, - // where the first one can be empty or can be viewed as the set of controls. - // ------------------------------------------------------------------------------------------------------------- - LayerId TraceSingleQubitOp(OpId id, Duration duration, QubitIdType target); - LayerId TraceMultiQubitOp(OpId id, Duration duration, long nFirstGroup, QubitIdType* firstGroup, - long nSecondGroup, QubitIdType* secondGroup); - - Result TraceSingleQubitMeasurement(OpId id, Duration duration, QubitIdType target); - Result TraceMultiQubitMeasurement(OpId id, Duration duration, long nTargets, QubitIdType* targets); - LayerId GetLayerIdOfSourceMeasurement(Result r) const - { - return reinterpret_cast(r); - } - - // ------------------------------------------------------------------------------------------------------------- - // Backing of the rest of the bridge methods. - // ------------------------------------------------------------------------------------------------------------- - LayerId InjectGlobalBarrier(OpId id, Duration duration); - - struct FenceScope - { - CTracer* tracer = nullptr; - LayerId fence = INVALID; - explicit FenceScope(CTracer* tracer, long count1, Result* results1, long count2, Result* results2); - ~FenceScope(); - }; - - // ------------------------------------------------------------------------------------------------------------- - // Configuring the tracer and getting data back from it. - // ------------------------------------------------------------------------------------------------------------- - // Temporary method for initial testing - // TODO: replace with a safer accessor - const std::vector& UseLayers() - { - return this->metricsByLayer; - } - - void PrintLayerMetrics(std::ostream& out, const std::string& separator, bool printZeroMetrics) const; - }; - - QIR_SHARED_API std::shared_ptr CreateTracer(int preferredLayerDuration); - QIR_SHARED_API std::shared_ptr CreateTracer(int preferredLayerDuration, - const std::unordered_map& opNames); - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index 217123cd44e..84447e6483f 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -50,5 +50,29 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { apt-get install -y clang-14 clang-tidy-14 clang-format-14 } } -} + + if (-not (Get-Command rustup -ErrorAction SilentlyContinue)) { + if ($IsWindows -or $PSVersionTable.PSEdition -eq "Desktop") { + Invoke-WebRequest "https://win.rustup.rs" -OutFile rustup-init.exe + Unblock-File rustup-init.exe; + ./rustup-init.exe -y + } elseif ($IsLinux -or $IsMacOS) { + Invoke-WebRequest "https://sh.rustup.rs" | Select-Object -ExpandProperty Content | sh -s -- -y; + } else { + Write-Error "Host operating system not recognized as being Windows, Linux, or macOS; please download Rust manually from https://rustup.rs/." + } + + if (-not (Get-Command rustup -ErrorAction SilentlyContinue)) { + Write-Error "After running rustup-init, rustup was not available. Please check logs above to see if something went wrong."; + exit -1; + } + } + + # Now that rustup is available, go on and make sure that nightly support for + # rustfmt and clippy is available. + rustup install nightly + rustup toolchain install nightly + rustup component add rustfmt clippy llvm-tools-preview + rustup component add rustfmt clippy llvm-tools-preview --toolchain nightly + } diff --git a/src/Qir/Runtime/public/BasicRuntimeDriverFactory.h b/src/Qir/Runtime/public/BasicRuntimeDriverFactory.h deleted file mode 100644 index ca33e2b609b..00000000000 --- a/src/Qir/Runtime/public/BasicRuntimeDriverFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef BASICRUNTIMEDRIVERFACTORY_H -#define BASICRUNTIMEDRIVERFACTORY_H - -#include -#include "CoreDefines.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - QIR_SHARED_API void* CreateBasicRuntimeDriver(); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef BASICRUNTIMEDRIVERFACTORY_H diff --git a/src/Qir/Runtime/public/CoreDefines.h b/src/Qir/Runtime/public/CoreDefines.h deleted file mode 100644 index c98f5f64500..00000000000 --- a/src/Qir/Runtime/public/CoreDefines.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef COREDEFINES_H -#define COREDEFINES_H - -#ifdef _WIN32 -#ifdef EXPORT_QIR_API -#define QIR_SHARED_API __declspec(dllexport) -#else -#define QIR_SHARED_API __declspec(dllimport) -#endif -#else -#define QIR_SHARED_API -#endif - -#endif // #ifndef COREDEFINES_H diff --git a/src/Qir/Runtime/public/CoreTypes.hpp b/src/Qir/Runtime/public/CoreTypes.hpp deleted file mode 100644 index 2491ac5306e..00000000000 --- a/src/Qir/Runtime/public/CoreTypes.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include "CoreDefines.h" - -// The core types will be exposed in the C-interfaces for interop, thus no -// namespaces or scoped enums can be used to define them. - -/*============================================================================== - Qubit & Result - - These two types are opaque to the clients: clients cannot directly create, delete, - copy or check state of qubits and results. QUBIT* and RESULT* should never be - dereferenced in client's code. -==============================================================================*/ - -// Although QIR uses an opaque pointer to the type "QUBIT", it never points to an actual memory -// and is never intended to be dereferenced anywhere - in the client code or in our runtime. -// Runtime always operates in terms of qubit ids, which are integers. Qubit ids are casted -// to this pointer type and stored as pointer values. This is done to ensure that qubit type -// is a unique type in the QIR. - -class QUBIT; -typedef intptr_t QubitIdType; - -class RESULT; -typedef RESULT* Result; // TODO(rokuzmin): Replace with `typedef uintXX_t Result`, where XX is 8|16|32|64. - // Remove all the `RESULT`. - -enum ResultValue -{ - Result_Zero = 0, - Result_One = 1, - Result_Pending, // indicates that this is a deferred result -}; - -/*============================================================================== - PauliId matrices -==============================================================================*/ -enum PauliId : int8_t -{ - PauliId_I = 0, - PauliId_X = 1, - PauliId_Z = 2, - PauliId_Y = 3, -}; diff --git a/src/Qir/Runtime/public/OutputStream.hpp b/src/Qir/Runtime/public/OutputStream.hpp deleted file mode 100644 index 8bfba57e4fa..00000000000 --- a/src/Qir/Runtime/public/OutputStream.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef OUTPUTSTREAM_HPP -#define OUTPUTSTREAM_HPP - -#include -#include "CoreTypes.hpp" // QIR_SHARED_API - -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - struct QIR_SHARED_API OutputStream - { - struct QIR_SHARED_API ScopedRedirector - { - explicit ScopedRedirector(std::ostream& newOstream); - ~ScopedRedirector(); - - private: - ScopedRedirector(const ScopedRedirector&) = delete; - ScopedRedirector& operator=(const ScopedRedirector&) = delete; - ScopedRedirector(ScopedRedirector&&) = delete; - ScopedRedirector& operator=(ScopedRedirector&&) = delete; - - private: - std::ostream& old; - }; - - static std::ostream& Get(); - static std::ostream& Set(std::ostream& newOStream); - - private: - static std::ostream* currentOutputStream; - }; - -} // namespace Quantum -} // namespace Microsoft - -#endif // #ifndef OUTPUTSTREAM_HPP diff --git a/src/Qir/Runtime/public/QSharpSimApi_I.hpp b/src/Qir/Runtime/public/QSharpSimApi_I.hpp deleted file mode 100644 index 11d0473cde3..00000000000 --- a/src/Qir/Runtime/public/QSharpSimApi_I.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct QIR_SHARED_API IQuantumGateSet - { - virtual ~IQuantumGateSet() - { - } - IQuantumGateSet() = default; - - // Elementary operatons - virtual void X(QubitIdType target) = 0; - virtual void Y(QubitIdType target) = 0; - virtual void Z(QubitIdType target) = 0; - virtual void H(QubitIdType target) = 0; - virtual void S(QubitIdType target) = 0; - virtual void T(QubitIdType target) = 0; - virtual void R(PauliId axis, QubitIdType target, double theta) = 0; - virtual void Exp(long numTargets, PauliId paulis[], QubitIdType targets[], double theta) = 0; - - // Multicontrolled operations - virtual void ControlledX(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledY(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledZ(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledH(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledS(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledT(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledR(long numControls, QubitIdType controls[], PauliId axis, QubitIdType target, - double theta) = 0; - virtual void ControlledExp(long numControls, QubitIdType controls[], long numTargets, PauliId paulis[], - QubitIdType targets[], double theta) = 0; - - // Adjoint operations - virtual void AdjointS(QubitIdType target) = 0; - virtual void AdjointT(QubitIdType target) = 0; - virtual void ControlledAdjointS(long numControls, QubitIdType controls[], QubitIdType target) = 0; - virtual void ControlledAdjointT(long numControls, QubitIdType controls[], QubitIdType target) = 0; - - // Results - virtual Result Measure(long numBases, PauliId bases[], long numTargets, QubitIdType targets[]) = 0; - - private: - IQuantumGateSet& operator=(const IQuantumGateSet&) = delete; - IQuantumGateSet(const IQuantumGateSet&) = delete; - }; - - struct QIR_SHARED_API IDiagnostics - { - virtual ~IDiagnostics() - { - } - IDiagnostics() = default; - - // The callback should be invoked on each basis vector (in the standard computational basis) in little-endian - // order until it returns `false` or the state is fully dumped. - typedef bool (*TGetStateCallback)(const char* /*basis vector*/, double /* amplitude Re*/, - double /* amplitude Im*/); - - // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - virtual void GetState(TGetStateCallback callback) = 0; - - virtual void DumpMachine(const void* location) = 0; - virtual void DumpRegister(const void* location, const QirArray* qubits) = 0; - - // Both Assert methods return `true`, if the assert holds, `false` otherwise. - virtual bool Assert(long numTargets, PauliId bases[], QubitIdType targets[], Result result, - const char* failureMessage) = 0; // TODO: The `failureMessage` is not used, consider - // removing. The `bool` is returned. - - virtual bool AssertProbability(long numTargets, PauliId bases[], QubitIdType targets[], - double probabilityOfZero, double precision, - const char* failureMessage) = 0; // TODO: The `failureMessage` is not used, - // consider removing. The `bool` is returned. - - private: - IDiagnostics& operator=(const IDiagnostics&) = delete; - IDiagnostics(const IDiagnostics&) = delete; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirContext.h b/src/Qir/Runtime/public/QirContext.h deleted file mode 100644 index 01f140b07a8..00000000000 --- a/src/Qir/Runtime/public/QirContext.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef QIRCONTEXT_H -#define QIRCONTEXT_H - -#include -#include "CoreDefines.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - QIR_SHARED_API void InitializeQirContext(void* driver, bool trackAllocatedObjects); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef QIRCONTEXT_H diff --git a/src/Qir/Runtime/public/QirContext.hpp b/src/Qir/Runtime/public/QirContext.hpp deleted file mode 100644 index ec298db64a0..00000000000 --- a/src/Qir/Runtime/public/QirContext.hpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -#include "CoreTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct IRuntimeDriver; - struct AllocationsTracker; - - // Deprecated: Use `QirExecutionContext::Init()` instead. - QIR_SHARED_API void InitializeQirContext(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - // Deprecated: Use `QirExecutionContext::Deinit()` instead. - QIR_SHARED_API void ReleaseQirContext(); - - struct QIR_SHARED_API QirExecutionContext - { - // Direct access from outside of `QirExecutionContext` is deprecated: The variables are to become `private`. - // { - // Use `Microsoft::Quantum::GlobalContext()->GetDriver()` instead: - IRuntimeDriver* driver = nullptr; - // Use `QirExecutionContext::{OnAddRef(), OnRelease(), OnAllocate()}`instead of direct access: - bool trackAllocatedObjects = false; - std::unique_ptr allocationsTracker; - // } - - static void Init(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - static void Deinit(); - - QirExecutionContext(IRuntimeDriver* driver, bool trackAllocatedObjects); - ~QirExecutionContext(); - - void OnAddRef(void* object); - void OnRelease(void* object); - void OnAllocate(void* object); - - IRuntimeDriver* GetDriver() const; - - struct QIR_SHARED_API Scoped - { - Scoped(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - ~Scoped(); - - private: - Scoped& operator=(const Scoped&) = delete; - }; - }; - - // Direct access is deprecated, use GlobalContext() instead. - extern std::unique_ptr g_context; - extern QIR_SHARED_API std::unique_ptr& GlobalContext(); - - // Deprecated, use `QirExecutionContext::Scoped` instead. - struct QIR_SHARED_API QirContextScope - { - QirContextScope(IRuntimeDriver* driver, bool trackAllocatedObjects = false); - ~QirContextScope(); - - private: - QirContextScope& operator=(const QirContextScope&) = delete; - }; -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirOutputHandling.hpp b/src/Qir/Runtime/public/QirOutputHandling.hpp deleted file mode 100644 index b964b649368..00000000000 --- a/src/Qir/Runtime/public/QirOutputHandling.hpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef QIROUTPUTHANDLING_HPP -#define QIROUTPUTHANDLING_HPP - -// QOH QIR Output Handling. -// REC Record. - -#ifdef __cplusplus -#include -#else -#include -#endif - -#include "CoreDefines.h" -#include "CoreTypes.hpp" - -// clang-format off -#define QOH_REC_TYPE "RESULT" -#define QOH_REC_COLUMN_DELIMITER "\t" -#define QOH_REC_PREFIX QOH_REC_TYPE QOH_REC_COLUMN_DELIMITER /* "RESULT" "\t" (== "RESULT\t") */ -#define QOH_REC_DELIMITER "\n" - -#define QOH_REC_VAL_TUPLE_START "TUPLE_START" -#define QOH_REC_VAL_TUPLE_END "TUPLE_END" -#define QOH_REC_VAL_ARRAY_START "ARRAY_START" -#define QOH_REC_VAL_ARRAY_END "ARRAY_END" -#define QOH_REC_VAL_RESULT_ZERO "0" -#define QOH_REC_VAL_RESULT_ONE "1" -#define QOH_REC_VAL_FALSE "false" -#define QOH_REC_VAL_TRUE "true" - -#define QOH_REC_TUPLE_START QOH_REC_PREFIX QOH_REC_VAL_TUPLE_START /* "RESULT" "\t" "TUPLE_START" (== "RESULT\tTUPLE_START") */ -#define QOH_REC_TUPLE_END QOH_REC_PREFIX QOH_REC_VAL_TUPLE_END -#define QOH_REC_ARRAY_START QOH_REC_PREFIX QOH_REC_VAL_ARRAY_START -#define QOH_REC_ARRAY_END QOH_REC_PREFIX QOH_REC_VAL_ARRAY_END -#define QOH_REC_RESULT_ZERO QOH_REC_PREFIX QOH_REC_VAL_RESULT_ZERO -#define QOH_REC_RESULT_ONE QOH_REC_PREFIX QOH_REC_VAL_RESULT_ONE -#define QOH_REC_FALSE QOH_REC_PREFIX QOH_REC_VAL_FALSE -#define QOH_REC_TRUE QOH_REC_PREFIX QOH_REC_VAL_TRUE -// clang-format on - - -#ifdef __cplusplus -extern "C" -{ -#endif - - // Primitive Result Records - - /// Produces output records of exactly "RESULT\\t0" or "RESULT\\t1" - QIR_SHARED_API void __quantum__rt__result_record_output(Result); // NOLINT - - /// Produces output records of exactly "RESULT\\tfalse" or "RESULT\\ttrue" - QIR_SHARED_API void __quantum__rt__bool_record_output(bool); // NOLINT - - /// Produces output records of the format "RESULT\\tn" where n is the string representation of the integer value, - /// such as "RESULT\\t42" - QIR_SHARED_API void __quantum__rt__integer_record_output(int64_t); // NOLINT - - /// Produces output records of the format "RESULT\\td" where d is the string representation of the double value, - /// such as "RESULT\\t3.14" - QIR_SHARED_API void __quantum__rt__double_record_output(double); // NOLINT - - - // Tuple Type Records - - /// Produces output records of exactly "RESULT\\tTUPLE_START" - QIR_SHARED_API void __quantum__rt__tuple_start_record_output(); // NOLINT - - /// Produces output records of exactly "RESULT\\tTUPLE_END" - QIR_SHARED_API void __quantum__rt__tuple_end_record_output(); // NOLINT - - - // Array Type Records - - /// Produces output records of exactly "RESULT\\tARRAY_START" - QIR_SHARED_API void __quantum__rt__array_start_record_output(); // NOLINT - - /// Produces output records of exactly "RESULT\\tARRAY_END" - QIR_SHARED_API void __quantum__rt__array_end_record_output(); // NOLINT - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef QIROUTPUTHANDLING_HPP diff --git a/src/Qir/Runtime/public/QirRuntime.hpp b/src/Qir/Runtime/public/QirRuntime.hpp deleted file mode 100644 index 93b0da45eac..00000000000 --- a/src/Qir/Runtime/public/QirRuntime.hpp +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include // for va_list - -#include "CoreTypes.hpp" -#include "QirTypes.hpp" - -extern "C" -{ - // ------------------------------------------------------------------------ - // Qubit Management. - // ------------------------------------------------------------------------ - - // Allocate one qubit. Qubit is guaranteed to be in |0> state. - // Qubit needs to be released via __quantum__rt__qubit_release. - QIR_SHARED_API QUBIT* __quantum__rt__qubit_allocate(); // NOLINT - - // Allocate 'count' qubits, allocate and return an array that owns these qubits. - // Array and qubits in the array need to be released via __quantum__rt__qubit_release_array. - QIR_SHARED_API QirArray* __quantum__rt__qubit_allocate_array(int64_t count); // NOLINT - - // Release one qubit. - QIR_SHARED_API void __quantum__rt__qubit_release(QUBIT*); // NOLINT - - // Release qubits, owned by the array and the array itself. - QIR_SHARED_API void __quantum__rt__qubit_release_array(QirArray*); // NOLINT - - // Borrow one qubit. Qubit is not guaranteed to be in |0> state. - // Qubit needs to be returned via __quantum__rt__qubit_return in the same state in which it was borrowed. - QIR_SHARED_API QUBIT* __quantum__rt__qubit_borrow(); // NOLINT - - // Borrow 'count' qubits, allocate and return an array that owns these qubits. - // Array and qubits in the array need to be returned via __quantum__rt__qubit_return_array. - QIR_SHARED_API QirArray* __quantum__rt__qubit_borrow_array(int64_t count); // NOLINT - - // Return one borrowed qubit. Qubit must be in the same state in which it was borrowed. - QIR_SHARED_API void __quantum__rt__qubit_return(QUBIT*); // NOLINT - - // Return borrowed qubits owned by the array. Release array itself. - QIR_SHARED_API void __quantum__rt__qubit_return_array(QirArray*); // NOLINT - - // ------------------------------------------------------------------------ - // Qubit Management Restricted Reuse Control. - // ------------------------------------------------------------------------ - - // Start restricted reuse area. - // Qubits released within one segment of an area cannot be reused in other segments of the same area. - QIR_SHARED_API void __quantum__rt__qubit_restricted_reuse_area_start(); // NOLINT - - // End current restricted reuse segment and start the next one within the current area. - QIR_SHARED_API void __quantum__rt__qubit_restricted_reuse_segment_next(); // NOLINT - - // End current restricted reuse area. - QIR_SHARED_API void __quantum__rt__qubit_restricted_reuse_area_end(); // NOLINT - - // ------------------------------------------------------------------------ - // Utils - // ------------------------------------------------------------------------ - - // Returns a pointer to the malloc-allocated block. - QIR_SHARED_API char* __quantum__rt__memory_allocate(uint64_t size); // NOLINT - - // Fail the computation with the given error message. - [[noreturn]] QIR_SHARED_API void __quantum__rt__fail(QirString* msg); // NOLINT - [[noreturn]] QIR_SHARED_API void __quantum__rt__fail_cstr(const char* msg); // NOLINT - - // Include the given message in the computation's execution log or equivalent. - QIR_SHARED_API void __quantum__rt__message(QirString* msg); // NOLINT - - // ------------------------------------------------------------------------ - // Results - // ------------------------------------------------------------------------ - - // Returns true if the two results are the same, and false if they are different. - QIR_SHARED_API bool __quantum__rt__result_equal(RESULT*, RESULT*); // NOLINT - - // Adds the given integer value to the reference count for the result. Deallocates the result if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__result_update_reference_count(RESULT*, int32_t); // NOLINT - - QIR_SHARED_API RESULT* __quantum__rt__result_get_one(); // NOLINT - QIR_SHARED_API RESULT* __quantum__rt__result_get_zero(); // NOLINT - - // ------------------------------------------------------------------------ - // Tuples - // ------------------------------------------------------------------------ - - // Allocates space for a tuple requiring the given number of bytes and sets the reference count to 1. - QIR_SHARED_API PTuple __quantum__rt__tuple_create(int64_t); // NOLINT - - // Adds the given integer value to the reference count for the tuple. Deallocates the tuple if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__tuple_update_reference_count(PTuple, int32_t); // NOLINT - - // Adds the given integer value to the alias count for the tuple. Fails if the count becomes negative. - QIR_SHARED_API void __quantum__rt__tuple_update_alias_count(PTuple, int32_t); // NOLINT - - // Creates a shallow copy of the tuple if the user count is larger than 0 or the second argument is `true`. - QIR_SHARED_API PTuple __quantum__rt__tuple_copy(PTuple, bool force); // NOLINT - - // ------------------------------------------------------------------------ - // Arrrays - // ------------------------------------------------------------------------ - - // Creates a new 1-dimensional array. The int is the size of each element in bytes. The int64_t is the length - // of the array. The bytes of the new array should be set to zero. - QIR_SHARED_API QirArray* __quantum__rt__array_create_1d(int32_t, int64_t); // NOLINT - - // Adds the given integer value to the reference count for the array. Deallocates the array if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__array_update_reference_count(QirArray*, int32_t); // NOLINT - - // Adds the given integer value to the alias count for the array. Fails if the count becomes negative. - QIR_SHARED_API void __quantum__rt__array_update_alias_count(QirArray*, int32_t); // NOLINT - - // Creates a shallow copy of the array if the user count is larger than 0 or the second argument is `true`. - QIR_SHARED_API QirArray* __quantum__rt__array_copy(QirArray*, bool); // NOLINT - - // Returns a new array which is the concatenation of the two passed-in arrays. - QIR_SHARED_API QirArray* __quantum__rt__array_concatenate(QirArray*, QirArray*); // NOLINT - - // Returns the length of a dimension of the array. The int is the zero-based dimension to return the length of; it - // must be 0 for a 1-dimensional array. - QIR_SHARED_API int64_t __quantum__rt__array_get_size(QirArray*, int32_t); // NOLINT - QIR_SHARED_API int64_t __quantum__rt__array_get_size_1d(QirArray*); // NOLINT - - // Returns a pointer to the element of the array at the zero-based index given by the int64_t. - QIR_SHARED_API char* __quantum__rt__array_get_element_ptr_1d(QirArray*, int64_t); // NOLINT - - // Creates a new array. The first int is the size of each element in bytes. The second int is the dimension count. - // The variable arguments should be a sequence of int64_ts contains the length of each dimension. The bytes of the - // new array should be set to zero. - QIR_SHARED_API QirArray* __quantum__rt__array_create(int, int, ...); // NOLINT - QIR_SHARED_API QirArray* __quantum__rt__array_create_nonvariadic( // NOLINT - int itemSizeInBytes, int countDimensions, va_list dims); - - // Returns the number of dimensions in the array. - QIR_SHARED_API int32_t __quantum__rt__array_get_dim(QirArray*); // NOLINT - - // Returns a pointer to the indicated element of the array. The variable arguments should be a sequence of int64_ts - // that are the indices for each dimension. - QIR_SHARED_API char* __quantum__rt__array_get_element_ptr(QirArray*, ...); // NOLINT - QIR_SHARED_API char* __quantum__rt__array_get_element_ptr_nonvariadic(QirArray*, va_list dims); // NOLINT - - // Creates and returns an array that is a slice of an existing array. The int indicates which dimension - // the slice is on. The %Range specifies the slice. - QIR_SHARED_API QirArray* quantum__rt__array_slice(QirArray*, int32_t, const QirRange&, // NOLINT - bool /*ignored: forceNewInstance*/); - - // Creates and returns an array that is a projection of an existing array. The int indicates which dimension the - // projection is on, and the int64_t specifies the specific index value to project. The returned Array* will have - // one fewer dimension than the existing array. - QIR_SHARED_API QirArray* __quantum__rt__array_project(QirArray*, int32_t, int64_t); // NOLINT - - // ------------------------------------------------------------------------ - // Callables - // ------------------------------------------------------------------------ - - // Initializes the callable with the provided function table and capture tuple. The capture tuple pointer - // should be null if there is no capture. - QIR_SHARED_API QirCallable* __quantum__rt__callable_create(t_CallableEntry*, t_CaptureCallback*, PTuple); // NOLINT - - // Adds the given integer value to the reference count for the callable. Deallocates the callable if the reference - // count becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__callable_update_reference_count(QirCallable*, int32_t); // NOLINT - - // Adds the given integer value to the alias count for the callable. Fails if the count becomes negative. - QIR_SHARED_API void __quantum__rt__callable_update_alias_count(QirCallable*, int32_t); // NOLINT - - // Creates a shallow copy of the callable if the alias count is larger than 0 or the second argument is `true`. - // Returns the given callable pointer otherwise, after increasing its reference count by 1. - QIR_SHARED_API QirCallable* __quantum__rt__callable_copy(QirCallable*, bool); // NOLINT - - // Invokes the callable with the provided argument tuple and fills in the result tuple. - QIR_SHARED_API void __quantum__rt__callable_invoke(QirCallable*, PTuple, PTuple); // NOLINT - - // Updates the callable by applying the Adjoint functor. - QIR_SHARED_API void __quantum__rt__callable_make_adjoint(QirCallable*); // NOLINT - - // Updates the callable by applying the Controlled functor. - QIR_SHARED_API void __quantum__rt__callable_make_controlled(QirCallable*); // NOLINT - - // Invokes the function in the corresponding index in the memory management table of the callable with the capture - // tuple and the given 32-bit integer. Does nothing if the memory management table pointer or the function pointer - // at that index is null. - QIR_SHARED_API void __quantum__rt__capture_update_reference_count(QirCallable*, int32_t); // NOLINT - QIR_SHARED_API void __quantum__rt__capture_update_alias_count(QirCallable*, int32_t); // NOLINT - - // ------------------------------------------------------------------------ - // Strings - // ------------------------------------------------------------------------ - - // Creates a string from an array of UTF-8 bytes. - // TODO the provided constructor doesn't match the spec! - // QIR_SHARED_API QirString* __quantum__rt__string_create(int, char*); // NOLINT - QIR_SHARED_API QirString* __quantum__rt__string_create(const char*); // NOLINT - - // Adds the given integer value to the reference count for the string. Deallocates the string if the reference count - // becomes 0. The behavior is undefined if the reference count becomes negative. - QIR_SHARED_API void __quantum__rt__string_update_reference_count(QirString*, int32_t); // NOLINT - - // Creates a new string that is the concatenation of the two argument strings. - QIR_SHARED_API QirString* __quantum__rt__string_concatenate(QirString*, QirString*); // NOLINT - - // Returns true if the two strings are equal, false otherwise. - QIR_SHARED_API bool __quantum__rt__string_equal(QirString*, QirString*); // NOLINT - - // Returns a string representation of the integer. - QIR_SHARED_API QirString* __quantum__rt__int_to_string(int64_t); // NOLINT - - // Returns a string representation of the double. - QIR_SHARED_API QirString* __quantum__rt__double_to_string(double); // NOLINT - - // Returns a string representation of the Boolean. - QIR_SHARED_API QirString* __quantum__rt__bool_to_string(bool); // NOLINT - - // Returns a string representation of the result. - QIR_SHARED_API QirString* __quantum__rt__result_to_string(RESULT*); // NOLINT - - // Returns a string representation of the Pauli. - QIR_SHARED_API QirString* __quantum__rt__pauli_to_string(PauliId); // NOLINT - - // Returns a string representation of the qubit. - QIR_SHARED_API QirString* __quantum__rt__qubit_to_string(QUBIT*); // NOLINT - - // Returns a string representation of the range. - QIR_SHARED_API QirString* quantum__rt__range_to_string(const QirRange&); // NOLINT - - // Returns a pointer to an array that contains a null-terminated sequence of characters - // (i.e., a C-string) representing the current value of the string object. - QIR_SHARED_API const char* __quantum__rt__string_get_data(QirString* str); // NOLINT - - // Returns the length of the string, in terms of bytes. - // http://www.cplusplus.com/reference/string/string/size/ - QIR_SHARED_API uint32_t __quantum__rt__string_get_length(QirString* str); // NOLINT - - // Returns a string representation of the big integer. - // TODO QIR_SHARED_API QirString* __quantum__rt__bigint_to_string(QirBigInt*); // NOLINT - - // ------------------------------------------------------------------------ - // BigInts - // ------------------------------------------------------------------------ - - // Creates a big integer with the specified initial value. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_int64_t(int64_t); // NOLINT - - // Creates a big integer with the initial value specified by the i8 array. The 0-th element of the array is the - // highest-order byte, followed by the first element, etc. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_array(int, char*); // NOLINT - - // Adds the given integer value to the reference count for the big integer. Deallocates the big integer if the - // reference count becomes 0. The behavior is undefined if the reference count becomes negative. - // TODO QIR_SHARED_API void __quantum__rt__bigint_update_reference_count(QirBigInt*, int32_t); // NOLINT - - // Returns the negative of the big integer. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_negate(QirBigInt*); // NOLINT - - // Adds two big integers and returns their sum. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_add(QirBigInt*, QirBigInt*); // NOLINT - - // Subtracts the second big integer from the first and returns their difference. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_subtract(QirBigInt*, QirBigInt*); // NOLINT - - // Multiplies two big integers and returns their product. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_multiply(QirBigInt*, QirBigInt*); // NOLINT - - // Divides the first big integer by the second and returns their quotient. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_divide(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the first big integer modulo the second. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_modulus(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the big integer raised to the integer power. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_power(QirBigInt*, int); // NOLINT - - // Returns the bitwise-AND of two big integers. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitand(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the bitwise-OR of two big integers. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitor(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the bitwise-XOR of two big integers. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitxor(QirBigInt*, QirBigInt*); // NOLINT - - // Returns the bitwise complement of the big integer. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitnot(QirBigInt*); // NOLINT - - // Returns the big integer arithmetically shifted left by the integer amount of bits. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftleft(QirBigInt*, int64_t); // NOLINT - - // Returns the big integer arithmetically shifted right by the integer amount of bits. - // TODO QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftright(QirBigInt*, int64_t); // NOLINT - - // Returns true if the two big integers are equal, false otherwise. - // TODO QIR_SHARED_API bool __quantum__rt__bigint_equal(QirBigInt*, QirBigInt*); // NOLINT - - // Returns true if the first big integer is greater than the second, false otherwise. - // TODO QIR_SHARED_API bool __quantum__rt__bigint_greater(QirBigInt*, QirBigInt*); // NOLINT - - // Returns true if the first big integer is greater than or equal to the second, false otherwise. - // TODO QIR_SHARED_API bool __quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT -} - -// TODO(rokuzmin): Consider separating the `extern "C"` exports and C++ exports. -namespace Microsoft // Replace with `namespace Microsoft::Quantum` after migration to C++17. -{ -namespace Quantum -{ - // Deprecated, use `Microsoft::Quantum::OutputStream::ScopedRedirector` or `Microsoft::Quantum::OutputStream::Set()` - // instead. - QIR_SHARED_API std::ostream& SetOutputStream(std::ostream& newOStream); -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirRuntimeApi_I.hpp b/src/Qir/Runtime/public/QirRuntimeApi_I.hpp deleted file mode 100644 index 318e1f29fc8..00000000000 --- a/src/Qir/Runtime/public/QirRuntimeApi_I.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -#include "CoreTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - struct QIR_SHARED_API IRuntimeDriver - { - virtual ~IRuntimeDriver() - { - } - IRuntimeDriver() = default; - - // Doesn't necessarily provide insight into the state of the qubit (for that look at IDiagnostics) - virtual std::string QubitToString(QubitIdType qubit) = 0; - - // Qubit management - virtual QubitIdType AllocateQubit() = 0; - virtual void ReleaseQubit(QubitIdType qubit) = 0; - - virtual void ReleaseResult(Result result) = 0; - virtual bool AreEqualResults(Result r1, Result r2) = 0; - virtual ResultValue GetResultValue(Result result) = 0; - // The caller *should not* release results obtained via these two methods. The - // results are guaranteed to be finalized to the corresponding ResultValue, but - // it's not required from the runtime to return same Result on subsequent calls. - virtual Result UseZero() = 0; - virtual Result UseOne() = 0; - - private: - IRuntimeDriver& operator=(const IRuntimeDriver&) = delete; - IRuntimeDriver(const IRuntimeDriver&) = delete; - }; - - struct QIR_SHARED_API IRestrictedAreaManagement - { - virtual ~IRestrictedAreaManagement() - { - } - IRestrictedAreaManagement() = default; - - virtual void StartArea() = 0; - virtual void NextSegment() = 0; - virtual void EndArea() = 0; - - private: - IRestrictedAreaManagement& operator=(const IRestrictedAreaManagement&) = delete; - IRestrictedAreaManagement(const IRestrictedAreaManagement&) = delete; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/QirTypes.hpp b/src/Qir/Runtime/public/QirTypes.hpp deleted file mode 100644 index 4dba8aa976a..00000000000 --- a/src/Qir/Runtime/public/QirTypes.hpp +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include -#include - -#include "CoreTypes.hpp" - -/*====================================================================================================================== - QirArray -======================================================================================================================*/ -struct QIR_SHARED_API QirArray -{ - using TItemCount = uint32_t; // Data type of number of items (potentially can be increased to `uint64_t`). - using TItemSize = uint32_t; // Data type of item size. - using TBufSize = size_t; // Size of the buffer pointed to by `buffer` - // (32 bit on 32-bit arch, 64 bit on 64-bit arch). - using TDimCount = uint8_t; // Data type for number of dimensions (3 for 3D array). - using TDimContainer = std::vector; // Data type for container of dimensions - // (for array 2x3x5 3 items: 2, 3, 5). - - // The product of all dimensions (2x3x5 = 30) should be equal to the overall number of items - `count`, - // i.e. the product must fit in `TItemCount`. That is why `TItemCount` should be sufficient to store each dimension. - - TItemCount count = 0; // Overall number of elements in the array across all dimensions - const TItemSize itemSizeInBytes = 0; - - const TDimCount dimensions = 1; - TDimContainer dimensionSizes; // not set for 1D arrays, as `count` is sufficient - - char* buffer = nullptr; - - bool ownsQubits = false; - int refCount = 1; - int aliasCount = 0; // used to enable copy elision, see the QIR specifications for details - - // NB: Release doesn't trigger destruction of the Array itself (only of its data buffer) to allow for it being used - // both on the stack and on the heap. The creator of the array should delete it, if allocated from the heap. - int AddRef(); - int Release(); - - explicit QirArray(TItemCount cQubits); - QirArray(TItemCount cItems, TItemSize itemSizeInBytes, TDimCount dimCount = 1, TDimContainer&& dimSizes = {}); - QirArray(const QirArray& other); - - ~QirArray(); - - [[nodiscard]] char* GetItemPointer(TItemCount index) const; - void Append(const QirArray* other); -}; - -/*====================================================================================================================== - QirString is just a wrapper around std::string -======================================================================================================================*/ -struct QIR_SHARED_API QirString -{ - long refCount = 1; - std::string str; - - explicit QirString(std::string&& str); - explicit QirString(const char* cstr); -}; - -/*====================================================================================================================== - Tuples are opaque to the runtime and the type of the data contained in them isn't (generally) known, thus, we use - char* to represent the tuples QIR operates with. However, we need to manage tuples' lifetime and in case of nested - controlled callables we also need to peek into the tuple's content. To do this we associate with each tuple's buffer - a header that contains the relevant data. The header immediately precedes the tuple's buffer in memory when the - tuple is created. -======================================================================================================================*/ -// TODO (rokuzmin): Move these types to inside of `QirTupleHeader`. -using PTuplePointedType = uint8_t; -using PTuple = PTuplePointedType*; // TODO(rokuzmin): consider replacing `uint8_t*` with `void*` in order to block - // the accidental {dereferencing and pointer arithmetic}. - // Much pointer arithmetic in tests. GetHeader() uses the pointer arithmetic. -struct QIR_SHARED_API QirTupleHeader -{ - using TBufSize = size_t; // Type of the buffer size. - - int refCount = 0; - int32_t aliasCount = 0; // used to enable copy elision, see the QIR specifications for details - TBufSize tupleSize = 0; // when creating the tuple, must be set to the size of the tuple's data buffer (in bytes) - - // flexible array member, must be last in the struct - PTuplePointedType data[]; - - PTuple AsTuple() - { - return (PTuple)data; - } - - int AddRef(); - int Release(); - - static QirTupleHeader* Create(TBufSize size); - static QirTupleHeader* CreateWithCopiedData(QirTupleHeader* other); - - static QirTupleHeader* GetHeader(PTuple tuple) - { - return reinterpret_cast(tuple - offsetof(QirTupleHeader, data)); - } -}; - -/*====================================================================================================================== - A helper type for unpacking tuples used by multi-level controlled callables -======================================================================================================================*/ -struct QIR_SHARED_API TupleWithControls -{ - QirArray* controls; - TupleWithControls* innerTuple; - - PTuple AsTuple() - { - return reinterpret_cast(this); - } - - static TupleWithControls* FromTuple(PTuple tuple) - { - return reinterpret_cast(tuple); - } - - static TupleWithControls* FromTupleHeader(QirTupleHeader* th) - { - return FromTuple(th->AsTuple()); - } - - QirTupleHeader* GetHeader() - { - return QirTupleHeader::GetHeader(this->AsTuple()); - } -}; -static_assert(sizeof(TupleWithControls) == 2 * sizeof(void*), - L"TupleWithControls must be tightly packed for FlattenControlArrays to be correct"); - -/*====================================================================================================================== - QirCallable -======================================================================================================================*/ -typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); // TODO(rokuzmin): Move to `QirCallable::t_CallableEntry`. -typedef void (*t_CaptureCallback)(PTuple, int32_t); // TODO(rokuzmin): Move to `QirCallable::t_CaptureCallback`. -struct QIR_SHARED_API QirCallable -{ - static int constexpr Adjoint = 1; - static int constexpr Controlled = 1u << 1; - - private: - static int constexpr TableSize = 4; - static_assert(QirCallable::Adjoint + QirCallable::Controlled < QirCallable::TableSize, - L"functor kind is used as index into the functionTable"); - - int refCount = 1; - int aliasCount = 0; - - // If the callable doesn't support Adjoint or Controlled functors, the corresponding entries in the table should be - // set to nullptr. - t_CallableEntry functionTable[QirCallable::TableSize] = {nullptr, nullptr, nullptr, nullptr}; - - static int constexpr CaptureCallbacksTableSize = 2; - t_CaptureCallback captureCallbacks[QirCallable::CaptureCallbacksTableSize] = {nullptr, nullptr}; - - // The callable stores the capture, it's given at creation, and passes it to the functions from the function table, - // but the runtime doesn't have any knowledge about what the tuple actually is. - PTuple const capture = nullptr; - - // By default the callable is neither adjoint nor controlled. - int appliedFunctor = 0; - - // Per https://github.com/microsoft/qsharp-language/blob/main/Specifications/QIR/Callables.md, the callable must - // unpack the nested controls from the input tuples. Because the tuples aren't typed, the callable will assume - // that its input tuples are formed in a particular way and will extract the controls to match its tracked depth. - int controlledDepth = 0; - - // Prevent stack allocations. - ~QirCallable(); - - public: - QirCallable(const t_CallableEntry* ftEntries, const t_CaptureCallback* captureCallbacks, PTuple capture); - QirCallable(const QirCallable& other); - QirCallable* CloneIfShared(); - - int AddRef(); - int Release(); - void UpdateAliasCount(int increment); - - void Invoke(PTuple args, PTuple result); - void Invoke(); // a shortcut to invoke a callable with no arguments and Unit result - void ApplyFunctor(int functor); - - void InvokeCaptureCallback(int32_t index, int32_t parameter); -}; - -extern "C" -{ - // https://docs.microsoft.com/azure/quantum/user-guide/language/expressions/valueliterals#range-literals - struct QirRange - { - int64_t start; // Inclusive. - int64_t step; - int64_t end; // Inclusive. - }; -} diff --git a/src/Qir/Runtime/public/QubitManager.hpp b/src/Qir/Runtime/public/QubitManager.hpp deleted file mode 100644 index 3f2794e9fcb..00000000000 --- a/src/Qir/Runtime/public/QubitManager.hpp +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include - -#include "CoreTypes.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - // CQubitManager maintains mapping between user qubit objects and - // underlying qubit identifiers (Ids). When user program allocates - // a qubit, Qubit Manager decides whether to allocate a fresh id or - // reuse existing id that was previously freed. When user program - // releases a qubit, Qubit Manager tracks it as a free qubit id. - // Decision to reuse a qubit id is influenced by restricted reuse - // areas. When a qubit id is freed in one section of a restricted - // reuse area, it cannot be reused in other sections of the same area. - // True borrowing of qubits is not supported and is currently - // implemented as a plain allocation. - class QIR_SHARED_API CQubitManager - { - public: - // We want status array to be reasonably large. - constexpr static QubitIdType DefaultQubitCapacity = 8; - - // Indexes in the status array can potentially be in range 0 .. QubitId.MaxValue-1. - // This gives maximum capacity as QubitId.MaxValue. Actual configured capacity may be less than this. - // Index equal to QubitId.MaxValue doesn't exist and is reserved for 'NoneMarker' - list terminator. - constexpr static QubitIdType MaximumQubitCapacity = std::numeric_limits::max(); - - public: - CQubitManager(QubitIdType initialQubitCapacity = DefaultQubitCapacity, bool mayExtendCapacity = true); - - // No complex scenarios for now. Don't need to support copying/moving. - CQubitManager(const CQubitManager&) = delete; - CQubitManager& operator=(const CQubitManager&) = delete; - ~CQubitManager(); // If this dtor is made _virtual_ then the QIR RT tests crash (at least in Debug config) - // if the native simulator is compiled with Clang (as opposed to GCC). Nothing wrong found in - // the code, probably is the compiler bug. - - // Restricted reuse area control - void StartRestrictedReuseArea(); - void NextRestrictedReuseSegment(); - void EndRestrictedReuseArea(); - - // Allocate a qubit. Extend capacity if necessary and possible. - // Fail if the qubit cannot be allocated. - // Computation complexity is O(number of nested restricted reuse areas) worst case, O(1) amortized cost. - QubitIdType Allocate(); - // Allocate qubitCountToAllocate qubits and store them in the provided array. - // Extend manager capacity if necessary and possible. - // Fail without allocating any qubits if the qubits cannot be allocated. - // Caller is responsible for providing array of sufficient size to hold qubitCountToAllocate. - void Allocate(QubitIdType* qubitsToAllocate, int32_t qubitCountToAllocate); - - // Releases a given qubit. - void Release(QubitIdType qubit); - // Releases qubitCountToRelease qubits in the provided array. - // Caller is responsible for managing memory used by the array itself - // (i.e. delete[] array if it was dynamically allocated). - void Release(QubitIdType* qubitsToRelease, int32_t qubitCountToRelease); - - // Borrow (We treat borrowing as allocation currently) - QubitIdType Borrow(); - void Borrow(QubitIdType* qubitsToBorrow, int32_t qubitCountToBorrow); - // Return (We treat returning as release currently) - void Return(QubitIdType qubit); - void Return(QubitIdType* qubitsToReturn, int32_t qubitCountToReturn); - - // Disables a given qubit. - // Once a qubit is disabled it can never be "enabled" or reallocated. - void Disable(QubitIdType qubit); - // Disables a set of given qubits. - // Once a qubit is disabled it can never be "enabled" or reallocated. - void Disable(QubitIdType* qubitsToDisable, int32_t qubitCountToDisable); - - bool IsValidQubit(QubitIdType qubit) const; - bool IsDisabledQubit(QubitIdType qubit) const; - bool IsExplicitlyAllocatedQubit(QubitIdType qubit) const; - bool IsFreeQubitId(QubitIdType id) const; - - // Qubit counts: - - // Number of qubits that are disabled. When an explicitly allocated qubit - // gets disabled, it is removed from allocated count and is added to - // disabled count immediately. Subsequent Release doesn't affect counts. - QubitIdType GetDisabledQubitCount() const - { - return disabledQubitCount; - } - - // Number of qubits that are explicitly allocated. This counter gets - // increased on allocation of a qubit and decreased on release of a qubit. - // Note that we treat borrowing as allocation now. - QubitIdType GetAllocatedQubitCount() const - { - return allocatedQubitCount; - } - - // Number of free qubits that are currently tracked by this qubit manager. - // Note that when qubit manager may extend capacity, this doesn't account - // for qubits that may be potentially added in future via capacity extension. - // If qubit manager may extend capacity and reuse is discouraged, released - // qubits still increase this number even though they cannot be reused. - QubitIdType GetFreeQubitCount() const - { - return freeQubitCount; - } - - // Total number of qubits that are currently tracked by this qubit manager. - QubitIdType GetQubitCapacity() const - { - return qubitCapacity; - } - bool GetMayExtendCapacity() const - { - return mayExtendCapacity; - } - - private: - // The end of free lists are marked with NoneMarker value. It is used like null for pointers. - // This value is non-negative just like other values in the free lists. See sharedQubitStatusArray. - constexpr static QubitIdType NoneMarker = std::numeric_limits::max(); - - // Explicitly allocated qubits are marked with AllocatedMarker value. - // If borrowing is implemented, negative values may be used for refcounting. - // See sharedQubitStatusArray. - constexpr static QubitIdType AllocatedMarker = std::numeric_limits::min(); - - // Disabled qubits are marked with this value. See sharedQubitStatusArray. - constexpr static QubitIdType DisabledMarker = -1; - - // QubitListInSharedArray implements a singly-linked list with "pointers" - // to the first and the last element stored. Pointers are the indexes - // in a single shared array. Shared array isn't sotored in this class - // because it can be reallocated. This class maintains status of elements - // in the list by virtue of linking them as part of this list. This class - // sets Allocated status of elementes taken from the list (via TakeQubitFromFront). - // This class is small, contains no C++ pointers and relies on default shallow copying/destruction. - struct QubitListInSharedArray final - { - private: - QubitIdType firstElement = NoneMarker; - QubitIdType lastElement = NoneMarker; - // We are not storing pointer to shared array because it can be reallocated. - // Indexes and special values remain the same on such reallocations. - - public: - // Initialize empty list - QubitListInSharedArray() = default; - - // Initialize as a list with sequential elements from startId to endId inclusve. - QubitListInSharedArray(QubitIdType startId, QubitIdType endId, QubitIdType* sharedQubitStatusArray); - - bool IsEmpty() const; - void AddQubit(QubitIdType id, QubitIdType* sharedQubitStatusArray); - QubitIdType TakeQubitFromFront(QubitIdType* sharedQubitStatusArray); - void MoveAllQubitsFrom(QubitListInSharedArray& source, QubitIdType* sharedQubitStatusArray); - }; - - // Restricted reuse area consists of multiple segments. Qubits released - // in one segment cannot be reused in another. One restricted reuse area - // can be nested in a segment of another restricted reuse area. This class - // tracks current segment of an area by maintaining a list of free qubits - // in a shared status array FreeQubitsReuseAllowed. Previous segments are - // tracked collectively (not individually) by maintaining FreeQubitsReuseProhibited. - // This class is small, contains no C++ pointers and relies on default shallow copying/destruction. - struct RestrictedReuseArea final - { - public: - QubitListInSharedArray FreeQubitsReuseProhibited; - QubitListInSharedArray FreeQubitsReuseAllowed; - // When we are looking for free qubits we skip areas that are known not - // to have them (to achieve amortized cost O(1)). It is guaranteed that - // there're no free qubits available in areas between this one and the - // one pointed to by prevAreaWithFreeQubits (bounds non-inclusinve). - // This invariant is maintained in all operations. It is NOT guaranteed - // that the area pointed to by prevAreaWithFreeQubits actually has - // free qubits available, and the search may need to continue. - int32_t prevAreaWithFreeQubits = 0; - - RestrictedReuseArea() = default; - explicit RestrictedReuseArea(QubitListInSharedArray freeQubits); - }; - - // This is NOT a pure stack! We modify it only by push/pop, but we also iterate over elements. - class CRestrictedReuseAreaStack final : public std::vector - { - public: - // No complex scenarios for now. Don't need to support copying/moving. - CRestrictedReuseAreaStack() = default; - CRestrictedReuseAreaStack(const CRestrictedReuseAreaStack&) = delete; - CRestrictedReuseAreaStack& operator=(const CRestrictedReuseAreaStack&) = delete; - CRestrictedReuseAreaStack(CRestrictedReuseAreaStack&&) = delete; - CRestrictedReuseAreaStack& operator=(CRestrictedReuseAreaStack&&) = delete; - ~CRestrictedReuseAreaStack() = default; - - void PushToBack(RestrictedReuseArea area); - RestrictedReuseArea PopFromBack(); - RestrictedReuseArea& PeekBack(); - [[nodiscard]] int32_t Count() const; - }; - - void EnsureCapacity(QubitIdType requestedCapacity); - - // Take free qubit id from a free list without extending capacity. - // Free list to take from is found by amortized O(1) algorithm. - QubitIdType TakeFreeQubitId(); - // Allocate free qubit id extending capacity if necessary and possible. - QubitIdType AllocateQubitId(); - // Put qubit id back into a free list for the current restricted reuse area. - void ReleaseQubitId(QubitIdType id); - - [[nodiscard]] bool IsValidId(QubitIdType id) const; - [[nodiscard]] bool IsDisabledId(QubitIdType id) const; - [[nodiscard]] bool IsFreeId(QubitIdType id) const; - [[nodiscard]] bool IsExplicitlyAllocatedId(QubitIdType id) const; - - private: - // Configuration Properties: - bool mayExtendCapacity = true; - - // State: - // sharedQubitStatusArray is used to store statuses of all known qubits. - // Integer value at the index of the qubit id represents the status of that qubit. - // (Ex: sharedQubitStatusArray[4] is the status of qubit with id = 4). - // Therefore qubit ids are in the range of [0..qubitCapacity). - // Capacity may be extended if MayExtendCapacity = true. - // If qubit X is allocated, sharedQubitStatusArray[X] = AllocatedMarker (negative number) - // If qubit X is disabled, sharedQubitStatusArray[X] = DisabledMarker (negative number) - // If qubit X is free, sharedQubitStatusArray[X] is a non-negative number, denote it Next(X). - // Next(X) is either the index of the next element in the list or the list terminator - NoneMarker. - // All free qubits form disjoint singly linked lists bound to to respective resricted reuse areas. - // Each area has two lists of free qubits - see RestrictedReuseArea. - QubitIdType* sharedQubitStatusArray = nullptr; - // qubitCapacity is always equal to the array size. - QubitIdType qubitCapacity = 0; - // All nested restricted reuse areas at the current moment. - // Fresh Free Qubits are added to the outermost area: freeQubitsInAreas[0].FreeQubitsReuseAllowed - CRestrictedReuseAreaStack freeQubitsInAreas; - - // Counts: - QubitIdType disabledQubitCount = 0; - QubitIdType allocatedQubitCount = 0; - QubitIdType freeQubitCount = 0; - }; - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/README.md b/src/Qir/Runtime/public/README.md deleted file mode 100644 index 658f8716347..00000000000 --- a/src/Qir/Runtime/public/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# API Dependency - -(Try to keep the readability balance between the web view and raw file, give the preference to the raw file) - -The listed earlier ones provide the functionality to the listed later ones -(the listed later ones include and/or call the listed earlier ones, -the listed later ones cannot be compiled into an executable without the listed earlier ones). - -Same-level entities are independent of each other (unless specified otherwise). Entities depend on the levels listed earlier only. - - -TODO: Consider moving `public\` one level up, or all the rest one level down the directory tree. - -## Level 0. External To This Directory - -**..\..\Common\Include\qsharp__foundation_internal.hpp** - Depends on `QIR_SHARED_API` - consider moving below `public\`. - -**..\..\Common\Include\SimulatorStub.hpp** - Depends on `public\` - consider moving below `public\`. - - -## Level 1 - -**TracerTypes.hpp** Defines types `OpId`, `Time`, `Duration`, `LayerId`; constants `INVALID`, `REQUESTNEW`. - Does not depend on anything of our code. - Only used by Tracer and Tracer Test. Consider moving from `public` to a location still visible for tests. - -**CoreTypes.hpp** Defines `QIR_SHARED_API`, `QUBIT`, `Qubit`, `RESULT`, `Result`, `ResultValue`, `PauliId`. - Does not depend on anything of our code. - -**QirTypes.hpp** Defines `QirArray`, `QirString`, `PTuple`, `QirTupleHeader`, `TupleWithControls`, `QirCallable`, `QirRange`. - Depends on the listed earlier ones. - -**QirRuntime.hpp** Declares `quantum__rt__*()`. Depends on the listed earlier ones. - - -## Level 2 - -**OutputStream.hpp** Defines `OutputStream`, `ScopedRedirector` - the means to redirect the output stream from `std::cout` to string, file, etc. - Used by the tests that verify the output (e.g. `Message()`). - Depends on `QIR_SHARED_API`. - -**QirRuntimeApi_I.hpp** Defines `IRuntimeDriver` that provides the access to the quantum machine simulator. - Depends on `QIR_SHARED_API`, `Qubit`, `Result`. - -**QirContext.hpp** Defines `QirExecutionContext`, `?Scoped`. - Uses `IRuntimeDriver *`, {`AllocationsTracker *` defined in `QIR` - reverse dependency}. - Depends on `QIR_SHARED_API` - -**QSharpSimApi_I.hpp** Defines `IQuantumGateSet`, `IDiagnostics`. - Depends on `QIR_SHARED_API`, `Qubit`, `PauliId`, `Result`, `QirArray`. - -## Level 3 - -**SimFactory.hpp** Defines `CreateToffoliSimulator()`, `CreateFullstateSimulator()`. - Depends on `QIR_SHARED_API`, `IRuntimeDriver` diff --git a/src/Qir/Runtime/public/SimFactory.h b/src/Qir/Runtime/public/SimFactory.h deleted file mode 100644 index 6b057b62e6f..00000000000 --- a/src/Qir/Runtime/public/SimFactory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#ifndef SIMFACTORY_H -#define SIMFACTORY_H - -#include -#include "CoreDefines.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - QIR_SHARED_API void* CreateFullstateSimulatorC(uint32_t userProvidedSeed); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // #ifndef SIMFACTORY_H diff --git a/src/Qir/Runtime/public/SimFactory.hpp b/src/Qir/Runtime/public/SimFactory.hpp deleted file mode 100644 index 36390e2b496..00000000000 --- a/src/Qir/Runtime/public/SimFactory.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include -#include -#include - -#include "QirRuntimeApi_I.hpp" - -namespace Microsoft -{ -namespace Quantum -{ - // Toffoli Simulator - QIR_SHARED_API std::unique_ptr CreateToffoliSimulator(); - - // Full State Simulator - QIR_SHARED_API std::unique_ptr CreateFullstateSimulator(uint32_t userProvidedSeed = 0); - -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/public/TracerTypes.hpp b/src/Qir/Runtime/public/TracerTypes.hpp deleted file mode 100644 index 9b1f119c3b3..00000000000 --- a/src/Qir/Runtime/public/TracerTypes.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#pragma once - -#include - -namespace Microsoft -{ -namespace Quantum -{ - using OpId = int; - using Time = int; - using Duration = int; - using LayerId = int64_t; // TODO: Use unsigned type. - - constexpr LayerId INVALID = std::numeric_limits::min(); - constexpr LayerId REQUESTNEW = std::numeric_limits::max(); -} // namespace Quantum -} // namespace Microsoft diff --git a/src/Qir/Runtime/qir.png b/src/Qir/Runtime/qir.png deleted file mode 100644 index 588193ffa2179a11806e8cb8ffee3e4516c1febf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37446 zcmeFZc|6o>_&==eNRAXqksOt#NSv~ii7BLPV^0!7VlX5c3`UD3Bq9`zeakLm2t!$m zvW$JkQkJY^A7dNy+@o~rEZ^Vn_5AVt^L+m}Co}W8m+QK({eE8`e=YbWjvahESXfv% zR4yy&u&`|9V`14MxP3GDhSqvm3jDLlN#~LROLoJ7QSf1_)dh_UEG&7!?5k$mz-PAG zmkpg*SOol-|7{8$nz3YIc}iAMx}fK3LZQ30T6P)sxz(gkj=f<|uhX}*kf=C)@SXJK z3hBLVlSA2)e7{?!m%K_(X}?1gRK8lHerxZ+3xAy6XRGi>?$c*cmqK(DMYuV3>!f(= z=&C?4j|P4HdB zu>bu5u<}1xQgw3hi*6)^^NXcJIZXX&CFESE{x??Vv8#A(?Z^=%45&XpFq%t@o9Ir1w;&pP)&adL3 zF;fZISK8oL#YE#@A|GcxTVik|I44-3{_am7@9#Hny4%M#5(+!RID%f2Ag~Oz@4+U zRT*BR1h%FA>3Y^^{n-_V!+ZJntn9;M&;7D2;>7I&QHH~qvf#KS?h^Dv$N2E1)3oKX zA=y`Ln%oO_?YVAXn)y_+ zBZ;E!fiGKnYqN45()ApNo?b3RTJsA&KmkJI_N)`a6@UV8D4~SHq3~LF6oHZ&Y2tI$TgvI8k{Y7Z=OYf%(}}jsA4|Wl^HG*YGyn2j0~9f@=?OLtjX2YBUwpk*tH) z`-fR)FOb-PvTWXqU~^R7>;N z`#7|fFxa>cPKW@`U+pYGRj7F&M9thq2WpZyUaR)h&)jF+g#{yznXQJXKm|(4xZ)|U zS!jC^k=Gkl;m|z2QcB-o%0f2wJ-aDeTUP(J>{d~WPd2$GE6Z2RxcQ&q?_4iQk9`;_ zZIppMGopg3lKGRWTQH~D!cH9!;lp+1vi3;}zBJlkl6wdImS`hOGCwEu!({8!W91X0PRb9>oC0HY&funw{4~=_C;V7^ zU5>42V2O;~i5B-@VJb&U)wR0|e2^}zP-KYi+^&W5Oy`*!wt&Otc6#O000;-%Brn{Dd|t>=-KxMg>+v8$^8(1HhcXT&&4zK^r z`TyC^x6hky%H`w?~s?Y%b(HA2H6SH5jqG z3%9JeS^qMKTl9KqbY1&W>L;-_ywDRh_>x%ELYWXXdMYiHRT(`D-IK znKx&Y6{La!_<3Z98)Lm@KR{w#`~6Q_HN2GfT;6jph-jM}IX=;qk8YkX9!+k^uqw}) zPN^_yoXlIulpxe;?x~Xuv<&nf;lV#d=A)_ZIa)f*B+MTfE&Q|eb z){yj0p0ri!j;jT)g>(F=f7*=*-!1#-)nM?IJ;{-=e73JD^y}XIYl360{Ccw*9v3>_ z&~MnTCSshv%5c>}ciZ_}~Uo2AGRNiH>d$c80 zzv<;Ux7lv-HJW#b*rl8pCacl=sj4O=s80+)F{0xlR9o1%Lg!c_!8yX?I-8}lQT$8s z@wDb-N)uI%T;%O&`Q8u_R5K1qT)a?lRNY6;Z(CVB+e$|xA^~Tr#k8z^&2W~h*H(EO zTj79mYK0HGRb!0jL|&~tW2sRPhB>Eq7A&r`u{ecyPxSH%9LKFs6volEAd0V?A0E0( z#w*rYi&s^`JCb^wFw;-tV$o0soThP)_f4pYj}u_nSWz1M^y&9jafWVTtY~CrSPy zqg+wxsKChPcvXA;#?v>0%x#n8JBz-AB$Tf_Le9u_$k60EIBoI_U&-*RPJNwYTFSWH zl&$xK9Uwp@Q__ckwyY8(%jTCFz1A9_Q>7FV5?<;&8cO$s zko-R#F|H6I`9E29y}^OBJtAI(Ov)1->7=|p>^oAlSD--}rVia*BSw1jN!p~@ZsSC6 z9Cv`|6}*&lUueE0NMNk3z~){w9*{%NuZpi4E4+`G%M13L$d9E@c+z@OBWL^}F-5L3 zhbUD?!%oH%u+y?vXJ>?bN(_ccD$nZ%>Sv9+9xXmf9cWT)s7u&}>6Qh4{b#~V2To4n_j}nY|DN5naY;(2{ z4+a&9yk1dsYA_R5`9!&*34WF7DuaM!Kk+JZ+RM*#roXx+RTDPfq*xm)SPqzFUp$&J zS3cbbj_Lpbjow7%8)MGBw(3Um*EniiagOBgVTiddpdiQtOOtFLVwDx8?}}KuFbnt(%}hiZ|UW2YPp`^&zLcE3L=HtDie|@hz+T?KNSBn9$97H^xd| z4(~KL7Gizc&RCRrrAnkk9CrKN>H9lqPox5}b_>+AdXklq7u}`|mmbu}HNm}qh*}-l zpPl#QxwXXs)5Qwj$VH6%UO|9F)8G!2#(6j)L=y7WsgnLD9MS7=7Ail0P%aTelr*AO>7(logOe z>q&iDid>ZeS~Cn*$yr{4P8KPO54_S%wT4K)bY>H?v^N>|J5&aymX)TtW`s=Dy7c1p zjnIwh?)MvsKQUhl&z)}33QtQp%Lrve0fSdMEH~B6ei{1b6{7HoG$O}PNyl<%uqa`u zpqi%D=O!<@WM|^hQ8;!PaA7l>E=H@_vC?iT26?5TNVq&fLh3L58$&=aYvo71?at!3 z2aXymC{wdVzLrizpf&o8OpIpvj6H@4E1dkYly@kdKBbwSF+Yi+yln||Ye>M}@nOm0 zxL~u4UO956w_~6w+_TXMz9EUpWvG6}h3#E;tRXcT#oiY$2&&o?qQ2OVb~4RyN1p(W zoJ$GGAM4D=zt+v-r4!uohHL|SVb=ec1pZ?Z%iR-HTyt&BHPe~pqO8)LZ0>tK?a0zO zi%7#5$-G_ZbN8*1EtfDj?!Mq4zaEkAQ}&gX4WDJktW3ZBR>u@NW6Bg)lD{KRujK79 zBI$1^nWTWJ(!Qofvj{tp4UZFnCi%y%^&gsZw~(6`GdZ zH#*I?mtXY$UGx4j$-i7k5CUn-uQt3ec<}$%>D#no`P@cc{xf|`QlDs>J&kxg8Ztz?T&yrOFiP7lKKm+?iJ%!RZ*zm)=56!L#e9vTBEGIOw~NM z!hjV%jS8@pS+nU32vPL?m68NYh2tuhT@}*zq=7S!UfD0jHu{_8L?qX!;;m-wD!Dzp zQ`m|#T^LXn`kC3+jy?g1mE#jo>{tvHYMk2guq5e-++*(H`yQcyQ*gze~XH zUka^XXsL2>#e|4#8?%M1)szj9g33efHI_@%%enTt&OSM$9{$`}80sjN?DwA=OvNr* zd3pAQP6pB&;-#a9N4W)6)H3>RcpUi{!Q+sC&WW;P#;gC%5*)YN?9TXQf)et=j_BnMn)l&0QCWilbnd(413RB{{Hc@`CPoI9Jv#7_fW_(tI)Q~!} zmvZ|*xejz-cuF(7XHji5MUTe`)OmPo10T3rjsc$Fk5i=84efwk4DIM91rx9O6e8OQ zZe{)rpY|rb8A7X_aWaZC9(s7N&mZ%?mY)4}I4-u}gkbY2hIM|KV%U_^@IM6lhd2WL zY5@z!$d{dX6zp|Iy`Z{X%`;3gUSWw)z9(f52Pso^SYjwu$c$XNmhJHTXha1bfgAE5 zzD!>rJ*#d^$6ODLHR|-joaK^k%y~Z;4a_YGV-DT_Kk?LUCCvGf&P9GJ{xIpw6s0-6 zeTA-11*AW+kJPkGFPB&-zH%tJHkqTogb%#tWm(sPsyG-OE)gU1JC(j-uR-r7D%B&q z+y186IQ3(~Y;2mB!#7h4xEp)IuDef_!(Ea@Y#4W+Bh_n<$`>4qixr@`?iY~0o<#zG z;gHW`)d%o~(A_!Nv^dq;uY04jOmiHLnW@ewstq)^TyMccUmhZ}DWIf5zzb}|Pnrb; zx67d?Z167fxs6(xivCR*4SnG|(vya-8uA_apGiz2$hovtFh!;V8KqUd_wv3%YsKg+ zl7HKA{>U1mFOj-^TBDs5d!}lfWyUVRH2p*kzR!4Uq;B7+#%b$$ODD>c>EtbF2NSf=l8va zxGgVErSjkT`c9(lx$Ttat1Mn5|E(4!{kU>T_2Xs8GZC?ho_-N{4j%qU3Huoiewv*w zSB)G&=F$NEkLwOoJY7sDI)z8h8FNs!BHnX)(sJmaf;>VoUHN48^6^0sBCT7@9|8vK zU4h&Oo9Y8r<1^IPY4HZk)r4~%8(j&Uu&Ol(oVBWzL-J?i_cuOqCrRs}3-x8C3p+Et zXC_6*CvHrBsStMlK%&0-V9vN zU)GT6n~EVb#48nga)=&<=Q`PhVP2$-ws37%&daq2p8~FY)kWZ>LBw#o;G`F-+ajN< zhNlld6So)Uyw70O{9=8DU6l2k;^S{)POOmVaSc zI9;Xq@GDGEeuVAR(l+r27X>Rf4~-50#sC9!%|Z9*$CT!W4kM|puJTKGGu6@Fc60V4 zk42Yywx?2RktMNZND#Gl*I+?qg0BgF!{l!g22`Z46!t-M;NrH@QyKl8?APp0R;J;a-TlGHiITftd>1{f$?K8v?`QLv zg7B|v;J9IPpWMTpbtCkeuMvm!&B*5v)#jY(zJ%O}@P5qQ43Dm9j=9(Jg0w|=g>S^_ z*yJ)rXjkjce(Z^3}-<9@6q^IX|9ZBo_yQDXGj`4bp4KG{o{ya6f zFH@&8!z?kF&%jK;#+JkPlNde>cg!e7(C()8>l{1tvm?F)C=LkQL1d?E)wyc}tXx^N z%B4r*lLrf(WAVQsfl~GC{5y^HK{8te_*S+C(}aTR?b6Bik~~b;^asgbs=qqh_QX3j zL$GP<^U(WlAl72WD)G-wNK#D&tgAxh#ij5&BJNYXZxs;3lp|4biQCuhBRCj(xsaPm_cML$-PQlYrkL`h0jYs2ci zjHr!>EXltfM&w^~@O_>$yOim5nIp#p4(A^TH_JvrCa{v`DYnMo|b^(R=o$=W7TS#ml)z&og%4zO7 zeI9c$U(YtWEyxcpnXW~So%DG}U#W-tIY1;c!XqScx%}l(TFk)oSiDc}OUw{W-g&}> z-Uq+F>(PfAQNpT%jpK*ZrY8;mqjz>22OKPt%G$7yDE6Js~R?`P_ z8TK1s@RwYRSKPiI`a_^4+@McoxN1^CNOse3Mh6pwN+|n)rYOlvtxA4bzaZ!VlINt($z+~SoAjxN}Elv-AkG6jn|L;+T z;q_9*kjcK^=d;hLL*w5`8n1~TTTcU+zFq7GcQ(}hPq)~nPPVkN#*Q@PcJ>o~2*@6o z5_wHcW!*jj&p$oj74UC+sv0cce_LcyY}Dyi66z2vNJ9V0`Z6F93Z@@lPjWUS_ggmd z;Z95ESzL2J+Tiwkl!A)r4p7)*F7gOub3zBt3|zZ*MpGL z6h~$0mDrc{aU8>-Dp;KUT*5Kn=D`T!P&vLHyfwoIj~5>E`r7(Iq48zwUz+oMeQYq= zx5;6T@pACf^<>Ni>bO?BHfSO&URT-ralq8a=LgSC~&qxWC#Y)(Z_FfeQTfI5Zamit>c(F}K;@J+kZ*61b{UEeW0 zqgvdMYS-vycircX=UnbGbdYqo(Q9uVCt&>5D0B?@?*o^5#s z!_?z_xp~_skByfEj~Kc$5V*M_F7$808kzRb1&_^h?d?N)`Yr9|&rujH+NBm{`=Te0 znfZ8HL(om|UnCC*RLtU2ht9A;VTb2Bvy9sZQ|v&YwD#6GKT5DU?8-dF)c3J_+vKH= zDQlqyC26kgi3T^ITyl;q!0KD_Ba&iN{lVtqW49DoqytS*Fz z_fG?tdl6f=-|MG4{Lx+bTaX|8mNS`Da2$@}Q-Rmu;}LSNs(OJ}%VTxk^$v_8 z{|{-IMDCBb&|Y%=1GzP0+QCq&Vru+BxQyFiMmo>TWypy@YcHkVib`opOWA z;xI+M`B{SXrw3J$_pLQza_ziybf7cEeeU-{u)qK7t_isfiN)==6tNU3XTlnO(SDr0 zIM{l$r(8TlB6$&ES_A z+M|d9n!|_S{*pT0#kecWy<_xy=DIqt5W#BPCzR={tf&9OzI)&=m*R8!madl!&{3iw z{(ODTb8M5*u9`X3YcQE7(H>Vu8bWhD`)N6+?9gGPFK>V z@(g)zDMI4~e77Bhg>*X3?d}Wyt7TX} zeFOYojfV0J_un~1ijGL;c+KB`38Z>8eiPT5zg~MGsDPWRIo$^W`Jb%IqmVDhwSbdd zW@YRH<=o@xSXUa|d*bGX^RY1r2GubNLDV4J|% zF4vk~`d$tOxj%3=xzb*D_DpOAI{h^wf#mPfqxoIX{u*>{ZHuWI7zEtZ8ogu^auWiO zGY?UlZRa4D-oe3bG_zj4p#^VAs~@y;xtYZ{qdFVNX^SuoRYf1H$BPA@JF&B`&=(DthH>)edXXudW*tKWnaa^HZ_=PtlxfL^ZKgzwxyll*zt z$pMUeS*k1}@(jd>B{otvyV<)b*@4!Gh2mpGqPsz()$Jr#@J>mVB0;#|L( z|;zW&h>E`4S(>$%FhhTK4|4Ib-c18xnr%C{a*?;fx$1|^)J8P#>%*mC-U zmYZLf!E{-u&4lj%(6+ue5Sj01hq&AMWkg zXLbqtg>ZkT&4&`7@1F@?5g2iEOwsuhkIDTdH!P92HTs8Fb)cQh4 zM9FDHF)CEr_4&%1r$4LN7^RB0Y}yB0GSxmY2ttf!#fS#hUl5u*G*|`x44z z$y9s2>(`I_3%Ct(Op>u_-mjh{aYT(ZgSxgJ<%~0*@#GQDGdEzLyFN>eSqbN~BCE_F znhSbn^d^0hCcy`Xxl?AJC^(;Gl_dxLY-c`GyvKM7xKlM_nOGnI9FNiT9MdiAzEgi# zd{rFmpy88DUncK#l|MIne`AcC`pLJVdZ(Iy&s(pc8%>&L`kd2}DZQD9amg7lF~it7>}Kl?OzXE0mSYSbUHg;HVvcLU>OH zRQV@Tc+$0F+`hg@jX{sqg^Lb1VDr7?9s8^NnLr#1!Ie%{`RPq}_c)+iqA|lRm`gm3 z8K(3x1HzcTjhM;K8^-8|N;#n>X&t3srE)wew$y;pi!I|Rk&`X4Yx8on?zztZ*#Qt3 zKn)FIb2^+-C{+$+lo+wWDS9K!JUMB#v%FO3qp$UaYoym_K2a%k;|BCyDSM(rbHgM_ zuk|n06e&|q3(jYuX{JoDXgAHU+yq>wOwOq79PWC)8d}+U`6ugba20CC@aX@BWuo`< zmb=alekhqN9@mXseijfN4bUrcQ*MpW-kB$bWz;MYw0+SR-;VM%YR;D1+nGl}mnnPHepM?z+GUYDo~zCNYla;1$VEjE_f zA^dSCtPM@PKEEW#;ujZr88H1A z;R-r_sPJ_KM|kf!&QJFe2tHClD>Uxy?$gM*clYx+gdeZu0fpA}f%T%6k4n~~`m<v^Tkti$3zfH$+$UY= zIwY`@0{y_sS_8yBTd~uXe$(z;s+6n@OpY6VDz>0n6|=-z9P9UUsG?+9ce?RhVrcc+ zg#{mW-d63jIJbF#+wz!x$9mB9GXSH>VOAH7U3!w+`<>Whx7|L224Kk10%Co#fx38< zi44IRO`YF7CWSNAup4ubRq3L5_g9afPgklu_)M}xg=*{471dr~Q0TVpKuWRGqHXIV z4IigDfQMh-<)l>x*6+ArN7SnQ;FwF!%Ix^~FCM&##Fke+n{?^xL8)gUSrr}L?kC^fRWGL6<&kbp4VILt>rC_9lq8%WZ0l-oiW6-p!eD1Ip zX2!G(c?Fi*ye-JKNhZg^78gF1B!hV*G1@Z;3_)(61HVb0J)V)Ny{dL465`ShQO1O@$o=+IP+oG0gpeY_l<3j%_LsWbJ z1k|n)#!iPPs3EIu-+W-V=iVDq`I9f`;gStt+O4s+jC90=43CIogLqo^rrZnYta*$f zZ+yomJ~qNV{wd*`!6deebVZZfQzp>(iApIr&rd%7$=yBX8a^~05qqT`AMH0=K2H<* z3vP&cyX;QrXhO58BP%b#Sz;y3w%)|Pwb$TF;JTQq#v>#*hl>_KOi z=!*dIUd;qFSzANyf!Z3&`8?)GJ`&rF697c5mHDZVJIA91-~d47^X*lDGmN|Xjo_^k zdjb6WSo?NL6~88!FdoZN!)!*YCIPMm$i#q+D?&Ft_5qD30T)4)p$SZG0Uo+%-V9fd zomv(28hl^DNpI=!d&OnZamdVr6^!0^{7=tPY!jzhdR1q^<$Znke+*^re~BL+vSkQb zZq)_%XbkrL*U{DxRW{1^9qy%6%^Q|rbF2}Du17|YkkhClVmpF%Ze>P zgc&P6`tla)3jdF3+V@ea9Ph-f_Dn6AWt3ADpN~C3f*L0&$tkMK5DCCb-?;;#Y?%WP z);d)nYm`U-`iMITzWTYb3|_DzvzA=`RHSd4=ftrQ&mhm%gR`GJOAmi}w48?RED9bR zCz}V7C1d=J9j@iYS#IG>wOm33Do=dm58GRS9? zZf}1+!?ob)dxVJH5Q3}}j_Zf{W${@Al3N?gDNQM5y@~ZIf!8EW9HO9iz~#As71440 zEBB9cC)h$}xeVK~^ygGfMy0QG%3H!Ndn&RS8CEnPE9cK9zRNYUaAq^(TxjMRo4t)L zJMmJd%wM=%8(di@#rvT7Lyp=MRs>s@5Pg??ysPJa-#Cr-uVjByclykWOV#6EG{)K& ziL}+Xfw5w7m7tMmuih?O#{oOfMm`m%OHaJBW{)GzWq{72sD^f;<4A9pWL{CHL~^y8 z!>(J%>%TqW1-kHEOg-%&viTcr*(CTrAZbVcG*FnPQB?BySmz4 zfhY`emqo=W8WK1N1v&vx=54%m$= zKEt=*FMME~n|b2daPG%(14F&`X*@WRN53#eZ~CNaZ|(_=182c-1p4kfujsbgzr9L--3)Dc*I4Illaq^UyCt z?k<$c`qhzZ4u=JXGZ#;NsBO_|F&q-`8F|z*EM53RFPP?Ta`EH9kh`DY;)@QSETVV% zV4I_6l!j zDs6uQ62#NVJU$~h{ub*g!>ewtG6D8a{4afvsaE-acyvt zX)nr`iwB(G=_bJkmM+2OiY1a)yDX8$vCN`Kj3lA>t6s3KIcBR;TI#D^sI%JS+iXn$EG58iW zMKO)YWPoMRntg}={S6G1HU3h4Rbuy7fN(C&lWZIx{ef%mxM&94~?5Iu2 zr{H5$-LWSQy?C=;p4ObS0W|eb4N1zm*fuzL_9}ssq%v@J@zbj*E8o8?BxtT0H)w7Y zxgGA&)XLr`={ODU6S>)CBdRmpV4y<6kAh6G+Qlvl<N+a+h1W`<5(_o(zZ z2aAAwY9slBU^}D6={Z0Te7D=6=}Jo>enN6mf9e+5U#opsawuOG-I*(cH#_-kk>p0E zMlFY5_d>oA-e_Fw34mP=$>DK`+xAvX^`Q-#-59 z=B4}KrGtf2M}KmqQVP%+BbWcC7Ytclb?DJx#w`E3p?|Ef+HE`e^9sO^JO~khqnkF_ z?Omrw+@9ra>$>OnaNNR@qRe#i{ek=}5>m_nV6SO6%e_~iHRXKXV*hU}>OoA0U(GAS za>IrhK%6Km+Qg!}9(>GHbFe(;``;=5mzV!5ME|Qj|8LF4p4q(#Vl4N%2I~1(Bz$sF+4)9VeM|sd}+gP-AmKAJb3ENQ~_t^lLEf84(qc;m|nbw|l3pQt}HJt&> z9e_=+*ga*)xGMOvvfO$sew?KYgv)rgz4BdQBY=`9`A;rxsr)whju-IuI;|7sNb8UHe$T@&; z{OM~15>V14j0zgVr5D=CdCBW@>@4VrG<4)dT zY5s*$#aGUKIU^c#;R4hQAj^1Ip8rW=i@Ut>v$qTjR|=`WJZ@hOcKblJFzDO?VhJM2 zL(TAKBG3GNI?4nH7mQ@lzLhv`%+VlnMEKvCv9}j~b(=Uho|{zX&UT#cAbb3ql;S}a z+QL$EX@g^bmOi4zTp*0&LI;6RJw|Ol#CbxIM#SNd zpu+^iXO^D%cad|1fRTRMy9m*wUj6M{o~8x?=j zKwGdG-FTCPnH*_~6IeF+>Z@CZl3eMx%1Wm}+qtn(QPi8wHxh{%1veaCn#j4c5(;uV zP7|{Ia)DS%ZwLad1p7x26L5LTFxgx`kP61@b)_|d+o9QHpFlNU7CSw3!AXQAv!Iv%ThVm> zI7?zPxE|8Ti8hhlXuidznCl8`qijXBFz;o&ibCY4K&K-x15Z&8TA7I2gzJEn_U=RgY-&uf4d}3iw%DFq^S#_1Mi(7?m9?o;A>lOd2gq&8U zpy6ZkA;TcYK0WbRB34U#adj95qh}C=Inr;ci;|XnsnvXWuVj%0uT*iE*)5F+{GLuy z`|VTXdlc67mIx2I#^kPKyV@ff7xSeB;KHDx+mkjlwVdsG*E<9`ROjN^F}mL5YoH^3 zYf<1L`aY*UFsWJmRM7e=uJ1#^&^x^3vg||mN{Q*lzxYZT-AWvLI4dU3Aj$f@4VTrvZ!-Z&rDGBBxeG}gn zG5Qo}`to3FrM9uT*QZf%d-nY@M6c|eGlM>J+dJfVLZo)iAUHrOdQ4buRs7gjGI;lp zwVr%R^W(ZINkY>Vsg%(MltQusF_d{!r#GcW0$LbqNDAo9>zA zrxO}7?QJI&Z4H?);2sm!OT1p}Jlg%nDHVg%zWZsD-Hz1vO9FeN5264eGBT4 zcrA2?y+-myc7(Hj6x0!98UxWCYE0v8%zSV3u1td2@zu?oZhnEvKuSm*==Gkn_gC`4 zJGfq@#uVQf3SijuFU}0amdw<+o>6$eXKBK{FT-H=9jjpL_^ZfPMp+BIfO~0bD^Uv~ zeo~mjl30Z&`9F;DV1+F&ko|L6J?F=Xjd*1=XKUWu8v04ZeL)4jshFuO4a^?Xm-6Cm zn&8UxR^=<3BPT9%Q^ql{AqtU^$p6WustMLA6v*=!v;=`BbPhlpVV%FD~>In7p6n???WZy&kC-ezo!X{d(#r+`L;UGVW zFRPvo$XRMxBOYsAiu7AM_xrg&Q)h4q{!^&KzwR@=ndGk)c@q2meFIZvEAsSFO|O2W zgZsky7#B}6)uQjh_Wh#h>I$_~g-N7Ci>TWafi^}x=k)b4ea;_i1DSnrf`cOj08r+L zN-fU}!h-IaV2)Ce~Z6*3$g`CyynrxzYBHX^j~>ae7EeF1j6bA9FQm*o#3 zs5(~@1?{SmF0z*$?cOSuKeG&gsr{$@%XTxC}lrQ6_|<{}gl^ z+)YRVI*0R;&x2BwOx%;>HvzoCHs>hos96i2F^OmQf(k4WsqnI(sVvObp7!3RA>Kns zm$#9lev-I!z-WHavX&}^DC;h#DaVT|I4QHnvMe_D=`8g#f6`wbjhaM~cMJDPa0u|N z+m`X~wnZ6~P2!8I)-32eJ*iXgEm{mC|fOkJUIlvt6Uh?;c!MSmu%($}nyd@49V za^c@e;g_}-R%}>0Z|6`V=9x9|ylu)^_*++UU7E5jL@zN-XQ_g>>~@E-%c#+KL@Kg_(V>=f7<6oAE&C0RpY#ZG@ws$JZ|O#IynOLt3kNN<)c0AP`5wG7!1Z4Fnp*x0iGEFbjhG-|puih94mG4LzB?5dkQQA)tSi_%B>!>|sjRX`} z`MdZVC_x=3ZksIC8C(+Ji~>!oZX)bh|@kfLsLqYDcRRbma61RY&(+1-Bh#7Q1BaOz}X zQbpsRbz>&%)%L|IPE?hBftHCpo-2K#%>1BtYu&K|fE=|T+g!nl*XH)m7*G%CZb<&7BnT5R^TgwdYzY$Ch?m@B* z@PF)EevzYFHr&&k@ZIoC&qmGw5G7{xjjA3KE)*;wbJfW3NO6s_ zu)>r`k5T4Kn)*EGola~*78i4_x{fX+VW%Hxq61HhPbE1zM%hMDw-ki0Ns9_t+TM-} zoBCQJJAwf=W$9qCc&UqF?PmVS`8P*3c!}`EFS2k%J}}Y+>ymV5C9g!x%tJRGG%IZ| zug{s@S;)1)JQe5{o@gA;@~!uEl7uVJFH+_-#8$ljD?!QB9@L5HmhoPd9umK7pQ4Ln zCY^jrMezlAO<$~Jo+!n8YT`Q?Y;XkVLB ztTi+bua>_qRPp1DkAu`|2U)}7B7yI11pE;O6-=fsBQecMa{LlXBaHoVjSjU;LQKsIPu`PU5rQB&&( zrt5F`c7hG$NUIe=50@-<8e!i9LaMDTO%jUmvKjB;w>jwd`_W$xS{s$KQR&}}jOG1U zvy+U&y%?yF;#yPXQYPrP`>&M(!Ad+ED|y;N9@pidDb|e9j$4XnGiFN7=ep+44pf^O zSt9I$GPSq2Ynlun?z!}``z>@}*7jpgwgv3kE!|P4piipH*9V}H)f&dHra*N-GmA#F zy8^m}%iPXfhr2H%@_V+%d54|-{TJO1^jwW`@v>M8PAv!eolarA%@EJuwwzWioe(=0 z9`rC~dAFw}xM5UVy1*|LQ~KLn)_gu!yE-$I-QQM4sWL6XOho_Cn0)L$1)ponrtE6` zndE!>K`_}i=28q=H7T`|_>o~eu{4jgaPSYk;hIQ9yE30kfByZw!@6do$;is{^}Y^m zuD)buvi;bmAJyMz3|olYm^r{2A}_t0%DLpl)m|&PY2whYL=DOO|(c+cXBUYIgl2l7(=XNt+%r#qyxG#TgmZS1$7&A>&mU4v7Smg+= z1OgFEFgQ`_jN>M>UhH%bAzIzI1Zo(6X%_kFQ*7hcYESv>LH#0Y%d0nL4waR?tr1p7 zgsm2ZHj2oT^BN_x7q5KbHb8^b=U%Eu>EFNKm~E%FWXlmNERlIlDytDf_D!g z!||wu!Uwc5Q&LJrQNI-r+85t4z3BVpRAKA}(__mp6O9|D7gRSSK$38c4mVIZ{)XP! z69MpXvd}&K$N?eb-PMrUp1qd`Tn8NKx6KMMMNPL{(JMh&%zRT>$`ds3f?WYv#d-c; zceIa&E8tLE-jtlBUU&hKF$He?|DY(54ZGq-ZX@#m?9~rkGxlqOBMRE!+C?0<;5G^Q zzt7`}6aHrQ)*IS;0>lgDWxc<<9-qam5yemG7yH_-Nc14 zlTi>1{wtAq{Fe}lci~oK^olQ6ik+5wQ&ed|?$g{(fCpOoA)qe-uLki>LpNo6Buva# zucdRnkuC{4yVOV&{(rjr_IN1Q?r-~G+fqqWDUwu7C25yN8B9?Lg>uX>BxJ@UjPux) z$oZV22|1sWF^Cx@Ikla`m~qG{$03Jt7>4({huV8T`+0u9-}}Cw&-=&wr@H65?`vIa zt!rKDTHo(d2h}4*rrT3-XP^jQ1^L69zd3Jd^Vpm6LF(cU{6tF2DCM=Rg!_ydyKpsBknnqMb)`gL4ZOjV!K`kd&8m}JX0)})D)YXddYC}9ST$sP@p9Jl~oJ?9Nx3+W_f3L=%-`9t% zgiAXBjA&$eGaAN7%&-5|Xh>!5q1ph6e&w478P65xGvJ zJ5A~fOx`tC9l0I~MQ{JGA3aRYN!rJSg#3#6?Zzmd40+Q^yJ2%RTjWcr`A$!mxXdrX zL=2ngho-vb|M@bzJW)0)=S$sBqsCblM1+xE0|jLg2mlJmy@FM$mQYCjPZcftnI$GY z&CJg!n{QbG3D}q17!@=c?&O6KGl++8JQtH&tA;%eZS-iN_s*?WO~h~Y<+C*KRJuZowF|_d{Y;{A2`|Z zb#+n2PE3cLcu{Q>a>De&_jwJnn)A9%%f4tA3Z~K`V*H(TYYpg?5i!Zk^);AgRMtYY)ikMK7M0>QdCusJdQGUro@wSCdQQHG~QD6FfmQ8w16r)BK+=5 z(1%h?3{z!E0W1NHINy@F{%u%j0-DL(HaQ6fdx zXHJuJDT(+RK|6j8ZBntu!(gLFWamS@m7M~!0#dV~2;m0!M0>>4J4CIDF;&gBY-Q!K z;pafF_$58&`z3cxin-$Ca;@w_>s`PSS+~8xSX62$ckVZI;SeOBzq^HG*(FriD{sbU z-nGL@2hHgRpfnyQ`jyTVWydnCQc|<)eFxiad6B2oP{gaM)Ta3xZK<6Je5Ea_)P=d3zgR6*15&3 zzKK7q7ULT(|I`1~h7Bqx$0>Pma_t;3#ham6DkMR{s`0Z++O{cxTPOo%OEXETt^un&K`$PRk-3Km^o1)+RO~rk^$IN0r z3Acy#$6fZq)%Y@7wN`F#U2ENQ_4msI zItqrC4L!TlJ@puSz+A04`b||9&K&r@Qhmz)uE?L* z{)Z{1ZyH}Q&M|Y|OsUfM%)cn%vwqF~rnOT)ajt{(%3A>k1c#%Gvk=N1p{v720fL3P zyasXgjx+F9HC}DE=b@Xsnp!>7DDPQ63RWTeGc(^HvXq}zMeeyLVF*h-Mj3wfwpzF` zZC|;`_S9p?gO+`nr<{U^KMVVIR*8&Re@IK7*04YO&xu0}TAcl54OspfTHTH0>V)&bU1rvW!o-6fI~(a zADQI5qQ9c}L*pTIPbs}hxb3$d0l_LMF!g7}Cwe?)@NG*8qUqs?=AMf94xfK&6^CDn zb0Db(vP?4y;2U5TXCBV3H`F`YJd`j_Z5q(E)z$2{Ep0DeHi|Q|pe)U$meEKnSF4x7p z%u*RVgb~I~-1Ysh*&X~aHKGA!qR~N6hR9tJlQ`&kQ z-t?sBs`2ikMdvnIZ0RjgS&VF@51$Tt*0T~KUNd5EHrYKzhU0n7jUAY$^e1jrB=jTE zU3ZcpqwHRx^KzY$|MS(LLP>@GSX;H@X+inIM1LeIc9(FR?meM7<_phYY&8GauqTKC z?nqk3#d*R$boh{+lRaSNrT}-ns}~@W@evlo&qEhq2#3ZMY}tu=vRJpGS;;fIc>X=4 zR1`SSOEr81zD;eQ*29=CPz{)qerFT)0Zy0AIsUATvaUJrI5<2Qnmjw6pmcXpLig-b zsfI|wsPkS~q$FxBHMhclsn};a#0mQnb_&xm1dPUI-^kd?k4$B%+Guet&6?t$hej%+ z{O%@%Y$=`c$EI5WPlcxI##PNaVf25yv=KVIRfMp&;v5HrGDfky_bR}>&k)S~3d)6x zuFE{t4`$ssNHa3nwVf0Zk|UFQBW;UUO6X$$B=qinc#~^{kc^1R=9uhIMf+b&DK;od zNtz4lc#sR)#(heaeM>&JF3)gcP*}6Yg^H^T|7Jwa(%L^&}4lk+>P7NpKezN zZbVO19#F)ceGAH;td)+dv|Zcwy0zpU)?Ha+RqMGBa>JqB6a`i5uOm1%kIfU+RXJZX z`DLQCb^IE0dtqn(H-Gf)b(iS=;{e|B7T53X$WE8rq=b`!d!HZ+=}!i0)BV4{xwK1z z%dwyn*Uv4jMVUE;It5Pd{OhBKRISEpx2bXtFCc zH`~BhJ32x$llJ=r{r$N;X@pnu%#JlyBo ztl^Q*&p{K^~G;=|FrVS@qHfeg>?$kakgmq3%BZU zYekS;1Y9^=^DxdBM3~tUE3bUz#X|b^(N%9t6;N|o3~CTx#czmj`uVUt`yGF#T*=)& zB`k7!jvgux;hk;VJUmCCVwciT=xxD(#`Yorm+ff-msmc39QghQ@Ye+3Z{(T)_r^Cp zBrtHw%KSmI)x&?4@D;j78KWi!r35|=8z@bHgs=b!;gmCS`jg7~srBs(z+_H>sQ=glyU2xW<_jnNuM7%zm>(T)&Zjg&WkN#u)){iyr(>NuR&k+kq zsDK^#*BiCzKnl4OW=(W&Z|C3|Lw;@Xlb3W|OnBiyw@?vL3bLUzNc+6jO${W?J_MR9 zKV3_6GJ{$0(F~0GN2O9R2y_b77cWE+we5=jg*|w2JUaZStH@!LDBHN)Wa)uiy)RRZ znxiIK8iMM zx>j0bQsX{aymB`|hIZiCJ_;QWY;c5rzDDiRX#y+wN-nG?(p#ruS0ouUMB`e zTc@(&n1aQ&^WZq43mXOwUJErifES`Q;kT`oIHcHcQZdZmdT%iXmp}2+@6SC99p^F2 zo<0Gv;AW{daMGn2D_a}KVH6$m|KIe0<4J&u4Al%7;ZB&2>a}4I_Mn6umxj?dh1s1~ zoSfbg|7PLhlB?+zb@~@I!)x@1EjYdLlx0ISK;2^7a9iatM*GbhV_&P+@VK1(Q@|c= z8Y^#&*0zedaSkRxVPbmk4Yo*?KC%0FRFB*4>1&7P*b{BL^JwFxb%CQNtnvX79Yr%^ zsvQ)OX%;SwydNl1uZR+_FEqPMub<<_#JDX+#(P7qK?7RvWgB>nBIn3)5t%>gy3;66 z&qMOFyyox%ff7LK)nmTcqD zJH0a9reC&>iW4P`n)I$L->=x`smu-w9j?_((C}y{Vc@u?LD}Tofpgu0{V`2A?|Fb6 z;22ydMDGRiJs9|L%2LgvI2ZWn@y#$2)AbR1NPB9t_C5gk=d8hO)&k}56}K6J-81+x z`sImp5u!FV`9ONo=Ra0_qWsm!A51_6A67a-EN!ty9Cu;H%9XKYTj9on>wz7&WrdMpKGQwma4Pt^Uj3;XMcLW2|) z=nM}zXd+D9h?D5#5Hkneq2c<2RT{c=a5}%!3Q>-uF8~>Xk*C8Fh$ohQFY9QEvo zHwz-rs&G}Hl}p>}M0aP!EwWe+rLA83c{}VguscKO4*G>Bd#x$2@h1QAO#!(6yQ=y< z*O>?j%&TMLWU&-Y~)<^{~U6nb_s%mOizfV2`_cHQjkilQsgAHiRJ2N{;e zm{Aa_?RZS^Hb`AW1F%>WO~dn-w>38KO{BlxG=5@KM#_fw4-kF_Nuyh|eTTmQr432L6i9P0g=Q)PlC`^RC=V4P%QEf!R!Q~S1A<3{5jiuTUFEC z+3vgH8yQ9LhKcde=Xlh=eFL~<$|S;IoSXp!9-IVVhQCaA?-6V%dnn#x&OpxZQMI&u zcKEA5BJ?Kgk%^_|g6VNh2;B~cYD65z?16lrb%>s2;y12qgqeRW zI{WmJgsp`Oop&mGjB&x{jZj+=4ZY!Rd?xHc;3ETS2&wBABlW4Kd$V?sk{kIF6*ZDP zdO-n_=7I=){dutYq?x%4PA>>-+!R7l1xW6#>&?A~yyY>!mOs!cQ%fc6KAA#goHX2X zTQ(Z{(!UDr%S0SyG|u$v1Hoi|=XIgWMwi9dWCtl64P5%Dp`3cG=bBS*+j~y3`L7Z> z>(Su&!g9E`ODB8%GFYcqOj8Qbx_T@!O z%g^xE=ABMaUt-k)X(GheQ#QQrrQBu-q7|P~*E4BEg6oR2%D&R@jhh2#TRw;U%?`8% z%ZLqFHxc1lF?CZq;0f2CXPs|ukV4pui9C|70a)pDaK|JL2t;zkrrD1bX|B{)NQx2T z&SKQdpE|Sm&LzBuL?YWOZvo=E&ThzxNpw()HmTz^_3|rJSa9#4YSFz`hR=NJfzte- z`R(QS3(`$j4Z<3~45+CvCXL$oWe-u70Ei177JtXGAV0$gek%)NTq*)~ldy+zp`?Oy zr51dsyY6ZcOk}#H9A8U$SDZw>x)T<0|<4Vr5X?q$+BY*qz**P`oy?#MZWaFt)> zf@c#>X}qH}g-6X*i3G5{N;q)J;EH7^BOPYzWz!l{Y?r}B`rOrC9TOc+C7H0QT(t8V z(w*jdE+v;n>;J%Rr}4%`cxuJTNKBkRNW!=v`{I_{RJ|gDp|}Vrs+83@qcXz`Ig=a+#nM!2m53|wQ)eC)k0~6Ux%DeTtYS%Ctg(@xVf#N zZlTKoFgkDX2gG_y=o3Jdu7|iI6WxbE=NEb01XYUFzp3Nmug?xZy>-SuK!AT{#F#GV zhI5p0!A&PSO=+Me9J@@yswYY5w9@P5+$1L+E2;dZOoTZ;DS}CUfBE#ia?|7d{uoJf z`8BBvaUnmFb2d$Zx{mh?whx`n`6+JIFOaE+u3!+HPiWG3F6XSqs05S(kga(_w!*Xf zE%N7CsIc;?OmnuBHY~!o%35Uy;f_!R8cg+BA#BU{w_Q7eR>|t9RJNe2?QV1t9&=p_ zT@cZArB=4)@85e(g;y$A(f6uvWhm8w#60qW-vrgRMFNPmE*vzbkT%tU1OOOgRx9C( z(|yABkN+wGsOwHd3#i&A!djY-0(j)tB$X;?^fw zj0-LVg-gi3Wpsh5bfS~7dUaHbLMc$$WVN$p1TE*6xPHLG19%C(`orLgOSPIkE0l!~ z;|F}M2#|w{x^eXBfoiBYvMmeL!za5Vo2U5#rfIzRS*^ohKWd}2D<3JzNPeHAyO=5k zaJE`=Mxe6X(-_%dzjOI8rc`KmuVZy5sarNKTKR>t3UIP1d-X!(*>kO#8ploEI04cQ zm3?l5fW)Cqu)`0v(&uN^^mVNkkOJ3_oL*t~u6dCo00%`QV3Em;(a9StwCk9o(U#*( zzdTGJJiTPvW~%EDj47T=fQQBvbXNk5689UuCLP;Q@vg0L`&Q<$ zq56QKO%EcF`1EnHvk|W+oocEHk<|e8#?Jfz@a+esN7&Y?tywnkZA}_Gvge2Psnu?& z;xlV*WZvl!;x!5jr9U2gXlkvln7f!Kk!*SkB$i>Xh*Do2?dW$Iivq|7lA=|uG~x>8 zD5QWpbPqMk&;FsUso*?R7wtIQpa2M3<{Q^n2Q&`?ti|OEPXH}Seff-J`RpueE!KOr z(VP828vj9){pq9k4BTuUhDuzai>^5+)reVQBfn8Nkmtpvn*oW#l#BO*3)CCvlcepM zz6E4eZG7s;YcI5#Jl-dWqR;1+5}Fcl{dPf$?MB=vn>f!k${DYQOG270OV5%U_b^Z> zP4+R{tvu&Jh^ZwJ(g_;(g(HIvT82|Rvt)Dt(b%(YX?52DEprS{rhcDkLeb7%5k7ZKQyPvgfU>CW zY)_M?T&nnV3EIUj=HrywYJs`}EuRD3(P+|#CjmiIqvw}XN3HBjhMp`yosRGerM;{3 zMdeHNSp0k8p1be2$d&=Nn+{pw-`}^=54q>`ELO>m?Q9K9?YJsUfSR4TZ(*Jsi-vk+ zC3m#0_5BPSp%PNel+s~0?rwzz{ekR=k^|%76H>=&iXwYu~nD1UuPapqvI3pAk z4cRPjZN4mR=stu4SH!!oaqm@g^!jwu=Fl<9$soyjVcxi%IdiY+RqUkdxNTCuf*h>5 z!RIuw*t$qf)d5|nyw5kY7KVv3zqhH#9CO!G|A7QIbljHW=d*{)tE}2oxIJ0 zy?A|21Ma!^)Yj(5xR*Bp%tsG-&A2Q(h7{2q7vRpX?F4xrnN_|`%jHu}pm-m2&N5PQ zfViogJ4`Zd=X6sk{R5(F#hK4R_b*FP?pN0UPYLMr_f7g_k12A8J1g5d{%QXG(FZ0R47B*0vN@zC?&ICJ1iauY5ai- z7-ZZ_<~S5ixHa~|cv~x9ddFALTFhOXX#vWGSMnr$a27(C)5I-miuf839ZXK8f2Jd* zsO_?~X`Wv6+J$-HbP(Gb9ERD#4Nrplxn7N+Y1t47=t@57lC!__4ztyn7;@4X&@v3B zDeu6Vv(-Iiw$d*sy54T^GP544wv6Puln|qyM+M|j=^YGEcHWkiCa9H{Xj-*pe!CL# zw_wuCu4?fuBFTiNAFbma<272>>~t;-$i9 zh}GI3?+5CAC#Zlb9$Kq&J=Yee4sSps+J4gn99y7NU$-4>&tucvx@Wl%4GoE+9pi^5I(9&fB0-_M+A~nToskN%sSg5#9k;%{ z)ESpjbOq|)Sa4hk%o>zC$Bl_Yq8H&NYx)N}>C7+nBZRSVA=w3{c|79Gh3Z<)4h4yw zHB<6oO$5H?aka|ZOkmg4C1|P!dKm^P;j%k3Z7<|Jx)%M@&4Cs^+)x17Au*h)BMn-D zwAy6X5MsW}o3ZFeJ-mnx&G?Fm^UL=WEN54WZPSMUt&z}>EWa@5I|%&oH$=xRy}ZZK z()~HyzvtvHH1xci(>R_i$OM_))Mk?=KX-D@>!R}+`YR_*kzxpO1U+9FbZlYYR3O5* z`K-Q!USjH=1hvUioM}e;W>WIn#>k${}(9_|)Hm#kk2*H6rPR%Z>v?m=N(sv&!=M zI4Zg!wCzkF6YQnOqWnQe6sV^G2b=e_D(pc|M=_|sqQJ1fB=_+9+)lXWf)sO$zlkE6 z1lFq=sU#{pb@6-x$KZecsc%Prw-qtRA@TJB;W55FyKTtU<_KCGaE}CTH;Xe+56hbX zv|)s&GS^^wGyxMK3~JlhhIa5?hfI|NySnoJqYYH2>!Va=de@^_)S%WLCHrL5O=M~& zn#}N_{+PtHC8`#OT@v9@0_XWZ*Mb8Vvk)>hbt!^H$Zoc^iJlM3Yi=}+pKp~SxmDvrVJ6fS^>k)rx3K>rS)sJ47(K^ z&~35)BccdK_s#|B0Is)JCe)GUR`ENUuO&OC5S{`;S-^D3#ia%6u+ORut08~G5@CR+ z6m;Rafe+^wO|_si1^pQC5jTm(Re@gagfesne>bh$jE zW+AonD^{waZ;a<2Q-KE{K|r;?E^h0R?c-mN^R1UXE{}uEWBk+RlflWO5&#k~9A|e3 zl~SePcbbfb(~dGM&Wj=&nmVW|46)v>YNXG>u_Qu)nMQT93OM8fxkKWtE;=kd^8n%+ zv~7Nnw|M{pOn7y9ZzbkP*&~gqY18CyI1Y7IS@KUQ{=53FwAk>KAMbELrBHen(d@aE z7P*EpOp&u6icC`QS$<+izYrs_>S##bSd&fm@Un(h{>)(=fx4GfSpj84n_5A?isoJF z)VR>N6-%%Cms%!IQqFC_UBDz$Rt{Yh77~_O;{GCANmYT_Kbq=Fz9{W!u9zF&Iu@ka z$zP>-$5E>3KInk^GW0dorO=VBN!Kj$gp;CcL$fxXx!$YQGNx-jOfqLZZ@x*pX|%2d zJGwLs#Nnfk@H{@<=+wkVS|Yx0PUt*mJ;lBdEH*Fd_pr}8jpX1M)D8DEyj#jZ>}yCp zO885l|5B3{p5>mj>Eh3misOcD+kv_>Z4wb%}1`2Wi|(r zW6@c^V)Wkd4Y9tEJtjGb2ma4(7|Uj~c7$Ty%qiQh_D&&C^bNvC(aN`M(M$CByp2L? z_8R~r2|>NfXUv-c)#l>veB_N3!#v8ila%$4REmq9bJJB3=q(*yM-?CmzfbdJ$NN*E z?{?R0qY5?mwKORKcWacRDT$z6JC=)7_2@N5_AMk2`0(@;{kCrLhl>f@@)CnH-?*Q) z^|Uf*G7_L=gAvNYas&zAJ~^+2b+@miYDTucaN-r?%g=zO>eUuCR#zo9syUZtuP)1| zo-#^ei6{Hdx2m^h@vR&VUT9s6W#$f@SYO_#ZjH<2VjZ@A-VyrIFFgTGx5d3ohjj0e zM<%)**M+X?H_K<+YOtDG@+1qiB)c!o2S-1QP%N^MUn0e|)=U}r{W-qes#90kX^vaq zI(5wxXg0B@h<@R)`J=?t^_io?bG-^;Ai#v}mOf zi8!a$`C-X*D2QTBH-eJ7ObNaZt??g~0NK~CTL`$nNcReg4iGuD`+Z{PE;bZU!K02#pRMKgZ1h#d61WI~5m8&w_$x3Xk9P{l?8!KRLd^!GP-)eD4OJ|k1 z*S_rKjj?5lN&x=I=A**sDr~PXXM6C!4h-t<(Yxc$FZ9zBGw1J|LAMw+uPae%w&1i| zNheNA8|4&hpkPDM6*8;~)kVRkKVnO-K&i>jfJQYh#8s#E@!;yhoMcMl8zRwG-w&k_ zsVF@pD?foiy(D(GO@G(b%PCzipPmx2O3{4efUz22iEYVw4GGB>i^yW9B?e6y6BFb< zOdMkKY)Rb$5K_Qz$-Ftn67NlHt|rPw=G}OH(b-~2t$Z6eBTLxYod4MJ%QG3-1XFAm z1}MW&+{o~UHvO1*-zQ_%PbIeYXk3mpNY`+GhfT%Li?;kiU-!{3KjD;glXUL|kDX45 z`5pAv+gK=DVp}D?>zv;#X55bimH_aY8zS>93km*GA0I^n?x@b#yW_^O_IVvh934jr zrp+fgtT|@KCj&J#j!cc)<Jpm%{?tB@_2)VP<6Z-@bu?8LmXvIrJ8G7i11uVQ z(WU_M9rXawp)(MjJ7`UHutCx1&RXhY3SLHAXd@;?$7QUH{PA0hDs13OC!4ETd%k#j z0dvKTOI|z}3~!Qp{sKQwpkDThrzm>mLI4pvrzWBW(|*JNF>=soJpi!aSv!JwHM?%W zK>i@QfRyMw@`osJB?xr&XTZF$@tuIaE#$}Dd;TZk!v8p~_-`JBSS5dh0-FE$QR@K{ z6k~uUCrq#21uvCdbIL-o3P^L+c%I9EjUR>vwIiGe$3GF5O=!}skp!&8b8bg4TDOsj zAM?l6;#~y4KcSOsNHEbVyaNZ!6%l5E$pE%!cpDk6K~6^{ftY$deA*2zepg#ETEU-O zXh2AAVKw3yzuYxfayy3R5ArHI5vCB4?_9~@H+zvwEVF*vZ{I?z?UKskUb&qqxR=Vb zggppc??zRzc?=`5FXP4Q+ZF{djZ1wid*5_g06;jx6too%^!k*@ zmbn0La(G6$gc=t1%|X}YeHgK~e zaxmp(-o^AETW$DsgKfJ3^*%1j)2qqLA{gqwlCz82cwA~M>Ph>q<=Ri7b^|Zt$9Ya; zVnz#?fo+As+cO~P`bl@`SK=YvZC=Db-S28|p=hy;cFg-H{ zYD*PkJyR5EKjx`_L{6nGwCW=RC~Gk36EdxXjKHr?|)wK4z}B~v0~k}@a&n$kp8@! zyMmBzrdk@clHOL+H9&9E{W@PM=0;Y%9}zKgZW)Zn77%FD>3MYUM|Y-+N;7t#g*;4B zpx&&;pu4_)UGUyGDd1iKh-2L5Bad2#g?ja%C=$bm*3!u&<#YFCyN6Q$Hr(QkoO}$y zgrX|!LV}Gt@)07WJ}!jyR()^~lCe*Z$7qcSBBj;_Gm`t*hcC24Ck?_C_$ zR_LH}KsAZMou!|G;kKgxD7^Oon(9$$N-6>RB2x4bY=60D|4`Ubp1xJ?f+0CdLT=$0 z=wa7_*u$uE8E;>ObwW44z*hqwsCe>G40*yzZ`f|mBJz>JN~PX zCpN-2_1wDkr~v4wxT$s9UqEzQS;gAqo$-K1QtZK5V7@J|?PXy3BwP2Km#C{n z$1Xd@*~2S;Nudm%-n-g3^!3kC#9x0JFY_ejYt5?>b1qFt9e{!zV%vxunWaZonXrn3 z!Z?>CyduJ0MtBTct{&lg($)gx0m@%`Hufb%NVH?%D;jEE!sbWWH``b;~|hO+S2p`XL**EK27-s z53itGhaXj`$<-GMvX?aGKx1_C#_PRqu?9tiZSg1Dn8%iMiiHt}@jQpNobG;u*@&~W z89syDW+pDYPj^`XA_HrX*wAy9*1B(3AfhDD^?l7wGKKpG43pMuMIXB7+qXDbH}{e> zgO{1@3s^|vUkS1=%;|{)puKz|w6s(#id8hReVZcj3ga6lV)WbXQfJzc)h(W zr&#nl+wLgJXC&#D6udhS+)%Yze2izKVHg05cZH^l&cN*DH9K&{`zN$f%Ziv}{Nwu` z^HvymhJ#3V`)xCs)MvzdJeTS6rn1 - $ - $ - $ - $ -) - -target_include_directories(qir-runtime-unittests PUBLIC - "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2" - ${public_includes} - ${common_includes} - "${PROJECT_SOURCE_DIR}/lib/QIR" - "${PROJECT_SOURCE_DIR}/lib/QSharpFoundation" - "${PROJECT_SOURCE_DIR}/lib/QSharpCore" - "${PROJECT_SOURCE_DIR}/lib/Tracer" -) -target_link_libraries(qir-runtime-unittests ${CMAKE_DL_LIBS}) -target_compile_definitions(qir-runtime-unittests PRIVATE EXPORT_QIR_API) -install(TARGETS qir-runtime-unittests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-runtime-unittests) diff --git a/src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp b/src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp deleted file mode 100644 index c9203659756..00000000000 --- a/src/Qir/Runtime/unittests/QirOutputHandlingTests.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -#include - -#include "catch.hpp" - -#include "QirRuntime.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "QirOutputHandling.hpp" -#include "OutputStream.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -TEST_CASE("QIR: TupleOutHandle", "[qir][out_handl][tuple_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // tuple_start_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__tuple_start_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_TUPLE_START) + QOH_REC_DELIMITER)); - } - - // tuple_end_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__tuple_end_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_TUPLE_END) + QOH_REC_DELIMITER)); - } - - // start + end - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__tuple_start_record_output(); - __quantum__rt__tuple_end_record_output(); - } - REQUIRE(actualStrStream.str() == - (string(QOH_REC_TUPLE_START) + QOH_REC_DELIMITER + string(QOH_REC_TUPLE_END) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: ArrayOutHandle", "[qir][out_handl][array_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // array_start_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_start_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_ARRAY_START) + QOH_REC_DELIMITER)); - } - - // array_end_record_output - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_end_record_output(); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_ARRAY_END) + QOH_REC_DELIMITER)); - } - - // start + end - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_start_record_output(); - __quantum__rt__array_end_record_output(); - } - REQUIRE(actualStrStream.str() == - (string(QOH_REC_ARRAY_START) + QOH_REC_DELIMITER + string(QOH_REC_ARRAY_END) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: ResultOutHandle", "[qir][out_handl][result_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__result_record_output(__quantum__rt__result_get_zero()); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_RESULT_ZERO) + QOH_REC_DELIMITER)); - } - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__result_record_output(__quantum__rt__result_get_one()); - } - REQUIRE(actualStrStream.str() == (string(QOH_REC_RESULT_ONE) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: BoolOutHandle", "[qir][out_handl][bool_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__bool_record_output(false); - } - - REQUIRE(actualStrStream.str() == (string(QOH_REC_FALSE) + QOH_REC_DELIMITER)); - } - - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__bool_record_output(true); - } - - REQUIRE(actualStrStream.str() == (string(QOH_REC_TRUE) + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: i64OutHandle", "[qir][out_handl][i64_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - auto i64Value = GENERATE(42LL, -2LL, 0x7FFFFFFFFFFFFFFFLL, 0x8000000000000000LL, 0x3333333333333333LL); - // 9223372036854775807 -9223372036854775808 3689348814741910323 - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__integer_record_output(i64Value); - } - - std::stringstream expectedStrStream; - expectedStrStream << i64Value; - - REQUIRE(actualStrStream.str() == (string(QOH_REC_PREFIX) + expectedStrStream.str() + QOH_REC_DELIMITER)); - } -} - -TEST_CASE("QIR: doubleOutHandle", "[qir][out_handl][double_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - auto value = GENERATE(0.0, 1.0, -2.0, 3.14159, -6.28, 6.67E+23, NAN, INFINITY); - // 6.67e+23 nan inf - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__double_record_output(value); - } - - std::stringstream expectedStrStream; - expectedStrStream << value; - - REQUIRE(actualStrStream.str() == (string(QOH_REC_PREFIX) + expectedStrStream.str() + QOH_REC_DELIMITER)); - } -} - - -// Group/mixed. -TEST_CASE("QIR: MixedOutHandle", "[qir][out_handl][tuple_record_output]") -{ - unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // clang-format off - { - ostringstream actualStrStream; - { - OutputStream::ScopedRedirector qOStreamRedirector(actualStrStream); - __quantum__rt__array_start_record_output(); - __quantum__rt__tuple_start_record_output(); - __quantum__rt__double_record_output(6.67E+23); - __quantum__rt__integer_record_output(42LL); - __quantum__rt__bool_record_output(true); - __quantum__rt__result_record_output(__quantum__rt__result_get_one()); - __quantum__rt__tuple_end_record_output(); - - __quantum__rt__tuple_start_record_output(); - __quantum__rt__double_record_output(-10.123); - __quantum__rt__integer_record_output(-2049LL); - __quantum__rt__bool_record_output(false); - __quantum__rt__result_record_output(__quantum__rt__result_get_zero()); - __quantum__rt__tuple_end_record_output(); - __quantum__rt__array_end_record_output(); - } - - std::stringstream expectedStrStream; - expectedStrStream << - QOH_REC_ARRAY_START << QOH_REC_DELIMITER << - QOH_REC_TUPLE_START << QOH_REC_DELIMITER << - QOH_REC_PREFIX << (double)6.67E+23 << QOH_REC_DELIMITER << - QOH_REC_PREFIX << 42LL << QOH_REC_DELIMITER << - QOH_REC_TRUE << QOH_REC_DELIMITER << - QOH_REC_RESULT_ONE << QOH_REC_DELIMITER << - QOH_REC_TUPLE_END << QOH_REC_DELIMITER << - - QOH_REC_TUPLE_START << QOH_REC_DELIMITER << - QOH_REC_PREFIX << (double)-10.123 << QOH_REC_DELIMITER << - QOH_REC_PREFIX << -2049LL << QOH_REC_DELIMITER << - QOH_REC_FALSE << QOH_REC_DELIMITER << - QOH_REC_RESULT_ZERO << QOH_REC_DELIMITER << - QOH_REC_TUPLE_END << QOH_REC_DELIMITER << - QOH_REC_ARRAY_END << QOH_REC_DELIMITER; - - REQUIRE(actualStrStream.str() == expectedStrStream.str()); - // actual: "RESULT\tARRAY_START\nRESULT\tTUPLE_START\nRESULT\t6.67e+23\nRESULT\t42\nRESULT\ttrue\nRESULT\t1\nRESULT\tTUPLE_END\n" - // "RESULT\tTUPLE_START\nRESULT\t-10.123\nRESULT\t-2049\nRESULT\tfalse\nRESULT\t0\nRESULT\tTUPLE_END\n" - // "RESULT\tARRAY_END\n\xcd\xcd\xcd\xcd\xcd..\xee\xfe\xee\xfe" - } - // clang-format on -} diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp deleted file mode 100644 index 6b29ac9e5cf..00000000000 --- a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp +++ /dev/null @@ -1,1075 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "catch.hpp" - -#include -#include // for memcpy -#include -#include -#include - -#include "QirTypes.hpp" -#include "qsharp__foundation__qis.hpp" -#include "qsharp__core__qis.hpp" -#include "QirRuntime.hpp" - -#include "QirContext.hpp" -#include "SimulatorStub.hpp" - -using namespace Microsoft::Quantum; - -static constexpr bool forseNewInstance = true; - -struct ResultsReferenceCountingTestQAPI : public SimulatorStub -{ - int lastId = -1; - const int maxResults; // TODO: Use unsigned type. - std::vector allocated; - - static int GetResultId(Result r) - { - return static_cast(reinterpret_cast(r)); - } - - ResultsReferenceCountingTestQAPI(int maxRes) : maxResults(maxRes), allocated((size_t)maxRes, false) - { - } - - Result Measure(long, PauliId[], long, QubitIdType[]) override - { - assert(this->lastId < this->maxResults); - this->lastId++; - this->allocated.at((size_t)(this->lastId)) = true; - return reinterpret_cast(this->lastId); - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - void ReleaseResult(Result result) override - { - const int id = GetResultId(result); - INFO(id); - REQUIRE(this->allocated.at((size_t)id)); - this->allocated.at((size_t)id).flip(); - } - bool AreEqualResults(Result r1, Result r2) override - { - return (r1 == r2); - } - - bool HaveResultsInFlight() const - { - for (const auto b : this->allocated) - { - if (b) - { - return true; - } - } - return false; - } -}; -TEST_CASE("Results: comparison and reference counting", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(3); - QirExecutionContext::Scoped qirctx(qapi.get()); - - Result r1 = qapi->Measure(0, nullptr, 0, nullptr); // we don't need real qubits for this test - Result r2 = qapi->Measure(0, nullptr, 0, nullptr); - REQUIRE(__quantum__rt__result_equal(r1, r1)); - REQUIRE(!__quantum__rt__result_equal(r1, r2)); - - // release result that has never been shared, the test QAPI will verify double release - __quantum__rt__result_update_reference_count(r2, -1); - - // share a result a few times - __quantum__rt__result_update_reference_count(r1, 2); - - Result r3 = qapi->Measure(0, nullptr, 0, nullptr); - - // release shared result, the test QAPI will verify double release - __quantum__rt__result_update_reference_count(r1, -3); // one release for shared and for the original allocation - - REQUIRE(qapi->HaveResultsInFlight()); // r3 should be still alive - __quantum__rt__result_update_reference_count(r3, -1); - - REQUIRE(!qapi->HaveResultsInFlight()); // no leaks -} - -TEST_CASE("Arrays: one dimensional", "[qir_support]") -{ - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), 5); - - memcpy(a->buffer, "Hello", 5); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(a, 4) == 'o'); - REQUIRE(__quantum__rt__array_get_dim(a) == 1); - REQUIRE(__quantum__rt__array_get_size_1d(a) == 5); - - QirArray* b = new QirArray(1, sizeof(char)); - *__quantum__rt__array_get_element_ptr_1d(b, 0) = '!'; - - QirArray* ab = __quantum__rt__array_concatenate(a, b); - REQUIRE(__quantum__rt__array_get_size_1d(ab) == 6); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(ab, 4) == 'o'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(ab, 5) == '!'); - - __quantum__rt__array_update_reference_count(a, -1); - __quantum__rt__array_update_reference_count(b, -1); - __quantum__rt__array_update_reference_count(ab, -1); -} - -TEST_CASE("Arrays: multiple dimensions", "[qir_support]") -{ - const size_t count = (size_t)(5 * 3 * 4); // 60 - QirArray* a = __quantum__rt__array_create(sizeof(int), 3, (int64_t)5, (int64_t)3, (int64_t)4); - REQUIRE(__quantum__rt__array_get_dim(a) == 3); - REQUIRE(__quantum__rt__array_get_size(a, 0) == 5); - REQUIRE(__quantum__rt__array_get_size(a, 1) == 3); - REQUIRE(__quantum__rt__array_get_size(a, 2) == 4); - - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 0, 0, 1))) == 1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 0, 1, 0))) == 4); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 4, 2, 3))) == 59); - - QirArray* b = __quantum__rt__array_copy(a, true /*force*/); - *(reinterpret_cast(__quantum__rt__array_get_element_ptr(b, 1, 2, 3))) = 42; - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 2, 3))) == 23); - - __quantum__rt__array_update_reference_count(a, -1); - __quantum__rt__array_update_reference_count(b, -1); -} - -TEST_CASE("Arrays: copy elision", "[qir_support]") -{ - QirArray* copy = __quantum__rt__array_copy(nullptr, true /*force*/); - CHECK(copy == nullptr); - - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), 5); - // the `a` array contains garbage but for this test we don't care - - // no aliases for the array, copy should be elided unless enforced - copy = __quantum__rt__array_copy(a, false /*force*/); - CHECK(a == copy); - __quantum__rt__array_update_reference_count(copy, -1); - - // single alias for the array, but copy enforced - copy = __quantum__rt__array_copy(a, true /*force*/); - CHECK(a != copy); - __quantum__rt__array_update_reference_count(copy, -1); - - // existing aliases for the array -- cannot elide copy - __quantum__rt__array_update_alias_count(a, 1); - copy = __quantum__rt__array_copy(a, false /*force*/); - CHECK(a != copy); - __quantum__rt__array_update_reference_count(copy, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: empty", "[qir_support]") -{ - QirArray* b = __quantum__rt__array_create(sizeof(int), 3, (int64_t)4, (int64_t)0, (int64_t)3); - REQUIRE(__quantum__rt__array_get_dim(b) == 3); - REQUIRE(__quantum__rt__array_get_size(b, 0) == 4); - REQUIRE(__quantum__rt__array_get_size(b, 1) == 0); - REQUIRE(__quantum__rt__array_get_size(b, 2) == 3); - REQUIRE(b->buffer == nullptr); - __quantum__rt__array_update_reference_count(b, -1); - - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), 0); - REQUIRE(__quantum__rt__array_get_dim(a) == 1); - REQUIRE(__quantum__rt__array_get_size_1d(a) == 0); - REQUIRE(a->buffer == nullptr); - - QirArray* a1 = __quantum__rt__array_copy(a, true /*force*/); - REQUIRE(__quantum__rt__array_get_dim(a1) == 1); - REQUIRE(__quantum__rt__array_get_size_1d(a1) == 0); - REQUIRE(a1->buffer == nullptr); - __quantum__rt__array_update_reference_count(a1, -1); - - QirArray* c = __quantum__rt__array_create_1d(sizeof(char), 5); - memcpy(c->buffer, "hello", 5); - QirArray* ac = __quantum__rt__array_concatenate(a, c); - REQUIRE(__quantum__rt__array_get_size_1d(ac) == 5); - QirArray* ca = __quantum__rt__array_concatenate(c, a); - REQUIRE(__quantum__rt__array_get_size_1d(ca) == 5); - - __quantum__rt__array_update_reference_count(a, -1); - __quantum__rt__array_update_reference_count(ac, -1); - __quantum__rt__array_update_reference_count(ca, -1); - __quantum__rt__array_update_reference_count(c, -1); -} - -TEST_CASE("Arrays: check the slice range", "[qir_support]") -{ - const int64_t dim0 = 5; - const int64_t dim1 = 6; - QirArray* a = __quantum__rt__array_create(sizeof(int), 2, dim0, dim1); - QirArray* slice = nullptr; - - // invalid range - CHECK_THROWS(quantum__rt__array_slice(a, 0, {0, 0, 0}, forseNewInstance)); - - // violated bounds - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 1, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 1, dim1}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 1, dim0 - 1}, forseNewInstance)); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -1, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -1, 0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -1, -1}, forseNewInstance)); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, 3, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {0, 3, dim1 + 2}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {-1, 3, dim0 - 1}, forseNewInstance)); - - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0, -3, dim0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 1, {dim1, -3, 0}, forseNewInstance)); - CHECK_THROWS(quantum__rt__array_slice(a, 0, {dim0 - 1, -3, -3}, forseNewInstance)); - - // empty range should produce empty array - slice = quantum__rt__array_slice(a, 0, {dim0 - 1, 1, 0}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - __quantum__rt__array_update_reference_count(slice, -1); - - slice = quantum__rt__array_slice(a, 1, {0, -1, dim0 - 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 0); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: slice of 1D array", "[qir_support]") -{ - const int64_t dim = 5; - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), dim); - memcpy(a->buffer, "01234", 5); - QirArray* slice = nullptr; - - // even if slice results in a single value, it's still an array - slice = quantum__rt__array_slice(a, 0, {1, 2 * dim, dim}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 1); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '1'); - __quantum__rt__array_update_reference_count(slice, -1); - - // if the range covers the whole array, it's effectively a copy - slice = quantum__rt__array_slice(a, 0, {0, 1, dim - 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '0'); - REQUIRE(*__quantum__rt__array_get_element_ptr(slice, 4) == '4'); - __quantum__rt__array_update_reference_count(slice, -1); - - // disconnected slice (also check that the end of range can be above bounds as long as the generated sequence is - // within them) - slice = quantum__rt__array_slice(a, 0, {0, 4, dim + 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 2); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '0'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 1) == '4'); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: reversed slice of 1D array", "[qir_support]") -{ - const int64_t dim = 5; - QirArray* a = __quantum__rt__array_create_1d(sizeof(char), dim); - memcpy(a->buffer, "01234", 5); - QirArray* slice = nullptr; - - // even if slice results in a single value, it's still an array - slice = quantum__rt__array_slice(a, 0, {1, -dim, 0}, forseNewInstance); - // Range{1, -dim, 0} == Range{1, -5, 0} == { 1 }. - // slice == char[1] == { '1' }. - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 1); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '1'); - __quantum__rt__array_update_reference_count(slice, -1); // slice == dangling pointer. - - // reversed slices are alwayes disconnected - slice = quantum__rt__array_slice(a, 0, {dim - 1, -2, 0}, forseNewInstance); - // Range{dim - 1, -2, 0} == Range{4, -2, 0} == {4, 2, 0}. - // slice == char[3] == {'4', '2', '0'}. - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 3); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 0) == '4'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 1) == '2'); - REQUIRE(*__quantum__rt__array_get_element_ptr_1d(slice, 2) == '0'); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: slice of 3D array", "[qir_support]") -{ - const int32_t dims = 3; - const int64_t dim0 = 5; - const int64_t dim1 = 3; - const int64_t dim2 = 4; - - QirArray* a = __quantum__rt__array_create(sizeof(int), dims, dim0, dim1, dim2); - QirArray* slice = nullptr; - - const size_t count = (size_t)(dim0 * dim1 * dim2); // 60 - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, dim0 - 1, dim1 - 1, dim2 - 1))) == - count - 1); - - // if the range covers the whole dimension, it's effectively a copy - slice = quantum__rt__array_slice(a, 1, {0, 1, dim1 - 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 3))) == 59); - __quantum__rt__array_update_reference_count(slice, -1); - - // if the range consists of a single point, the slice still has the same dimensions - slice = quantum__rt__array_slice(a, 1, {1, 2 * dim1, dim1}, forseNewInstance); // items with second index = 1 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 4); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 0, 3))) == 55); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on 0 dimension - slice = quantum__rt__array_slice(a, 0, {1, 1, 3}, forseNewInstance); // items with first index = 1, 2 or 3 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 3); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 2, 2, 3))) == 47); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on last dimension, expected result: - // indexes -- values - // 000 001 | 010 011 | 020 021 -- [ 1 2 | 5 6 | 9 10] - // 100 101 | 110 111 | 120 121 -- [13 14 | 17 18 | 21 22] - // ... [25 ... ] - // ... [37 ... ] - // 400 401 | 410 411 | 420 421 -- [49 50 | 53 54 | 57 58] - slice = quantum__rt__array_slice(a, 2, {1, 1, 2}, forseNewInstance); // items with last index = 1 or 2 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 2, 1))) == 10); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 1, 1))) == 18); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 1))) == 58); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on sparse range in 0 dimension (also check that the end of range can be above bounds as long as the - // generated sequence is within them) - slice = quantum__rt__array_slice(a, 0, {0, 3, dim0}, forseNewInstance); // items with first index = 0 or 3 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 2); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 0); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 2, 3))) == 47); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on sparse range in the middle dimension - slice = quantum__rt__array_slice(a, 1, {0, 2, 2}, forseNewInstance); // items with second index = 0 or 2 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 2); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 0); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 1, 3))) == 59); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on sparse range in the last dimension - // indexes -- values - // 000 001 | 010 011 | 020 021 -- [01 03 | 05 07 | 09 11] - // 100 101 | 110 111 | 120 121 -- [13 15 | 17 19 | 21 23] - // ... -- [25 ... ] - // ... -- [37 ... ] - // 400 401 | 410 411 | 420 421 -- [49 51 | 53 55 | 57 59] - slice = - quantum__rt__array_slice(a, 2, {1, 2, 3}, forseNewInstance); // items with last index = 1 or 3 (all odd numbers) - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 2, 1))) == 11); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 1, 0))) == 17); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 1))) == 59); - __quantum__rt__array_update_reference_count(slice, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: reversed slice of 3D array", "[qir_support]") -{ - const int32_t dims = 3; - const int64_t dim0 = 5; - const int64_t dim1 = 3; - const int64_t dim2 = 4; - - QirArray* a = __quantum__rt__array_create(sizeof(int), dims, dim0, dim1, dim2); - QirArray* slice = nullptr; - - const size_t count = (size_t)(dim0 * dim1 * dim2); // 60 - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, dim0 - 1, dim1 - 1, dim2 - 1))) == - count - 1); - - // if the range consists of a single point, the slice still has the same dimensions - slice = quantum__rt__array_slice(a, 1, {1, -dim1, 0}, forseNewInstance); // items with second index = 1 - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == 1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 4); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 0, 3))) == 55); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on dim0, expect the result to look like: - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [36 - 47] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [24 - 35] - // 200 201 202 203 | 210 211 212 213 | 220 221 222 223 -- [12 - 23] - slice = quantum__rt__array_slice(a, 0, {dim0 - 2, -1, 1}, forseNewInstance); - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == 3); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 36); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 2, 2, 3))) == 23); - __quantum__rt__array_update_reference_count(slice, -1); - - // slice on last dimension, expect the result to look like: - // indexes -- values - // 000 001 | 010 011 | 020 021 -- [03 01 | 07 05 | 11 09] - // 100 101 | 110 111 | 120 121 -- [15 13 | 19 17 | 23 21] - // ... -- [27 ... ] - // ... -- [39 ... ] - // 400 401 | 410 411 | 420 421 -- [51 49 | 55 53 | 59 57] - slice = quantum__rt__array_slice(a, 2, {dim2 - 1, -2, 0}, - forseNewInstance); // items with last index 3, 1 (all odd numbers) - REQUIRE(__quantum__rt__array_get_dim(slice) == dims); - REQUIRE(__quantum__rt__array_get_size(slice, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(slice, 1) == dim1); - REQUIRE(__quantum__rt__array_get_size(slice, 2) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 0, 0))) == 3); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 0, 2, 1))) == 9); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 1, 1, 0))) == 19); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(slice, 4, 2, 1))) == 57); - __quantum__rt__array_update_reference_count(slice, -1); - __quantum__rt__array_update_reference_count(a, -1); -} - -TEST_CASE("Arrays: project of 3D array", "[qir_support]") -{ - const int32_t dims = 3; - const int64_t dim0 = 5; - const int64_t dim1 = 3; - const int64_t dim2 = 4; - - QirArray* a = __quantum__rt__array_create(sizeof(int), dims, dim0, dim1, dim2); - QirArray* project = nullptr; - - const size_t count = (size_t)(dim0 * dim1 * dim2); // 60 - std::vector data(count, 0); - for (size_t i = 0; i < count; i++) - { - data[i] = (int)i; - } - // indexes -- values - // 000 001 002 003 | 010 011 012 013 | 020 021 022 023 -- [0 - 11] - // 100 101 102 103 | 110 111 112 113 | 120 121 122 123 -- [12 - 23] - // ... [24 - 35] - // ... [36 - 47] - // 400 401 402 403 | 410 411 412 413 | 420 421 422 423 -- [48 - 59] - memcpy(a->buffer, reinterpret_cast(data.data()), count * sizeof(int)); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, 1, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(a, dim0 - 1, dim1 - 1, dim2 - 1))) == - count - 1); - - // project on 0 dimension, expected result: - // indexes -- values - // 00 01 02 03 | 10 11 12 13 | 20 21 22 23 -- [12 - 23] - project = __quantum__rt__array_project(a, 0, 1); // items with first index = 1 - REQUIRE(__quantum__rt__array_get_dim(project) == dims - 1); - REQUIRE(__quantum__rt__array_get_size(project, 0) == dim1); - REQUIRE(__quantum__rt__array_get_size(project, 1) == dim2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 0, 0))) == 12); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 1, 1))) == 17); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 2, 3))) == 23); - __quantum__rt__array_update_reference_count(project, -1); - - // project on last dimension, expected result: - // indexes -- values - // 00 | 01 | 02 -- [02 06 10] - // 10 | 11 | 12 -- [14 18 22] - // ... -- [26 30 34] - // ... -- [38 42 46] - // 40 | 41 | 42 -- [50 54 58] - project = __quantum__rt__array_project(a, 2, 2); // items with last index = 2 - REQUIRE(__quantum__rt__array_get_dim(project) == dims - 1); - REQUIRE(__quantum__rt__array_get_size(project, 0) == dim0); - REQUIRE(__quantum__rt__array_get_size(project, 1) == dim1); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 0, 0, 0))) == 2); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 1, 1, 2))) == 18); - REQUIRE(*(reinterpret_cast(__quantum__rt__array_get_element_ptr(project, 4, 2, 2))) == 58); - __quantum__rt__array_update_reference_count(project, -1); - - __quantum__rt__array_update_reference_count(a, -1); -} - -std::unordered_map& AllocatedStrings(); -TEST_CASE("Strings: reuse", "[qir_support]") -{ - QirString* a = __quantum__rt__string_create("abc"); - QirString* b = __quantum__rt__string_create("abc"); - QirString* c = __quantum__rt__string_create("xyz"); - - REQUIRE(a == b); - REQUIRE(a->refCount == 2); - REQUIRE(a != c); - REQUIRE(c->refCount == 1); - - __quantum__rt__string_update_reference_count(a, -1); - REQUIRE(b->str.compare("abc") == 0); - - __quantum__rt__string_update_reference_count(b, -1); - __quantum__rt__string_update_reference_count(c, -1); - - REQUIRE(AllocatedStrings().empty()); -} - -TEST_CASE("Strings: concatenate", "[qir_support]") -{ - QirString* a = __quantum__rt__string_create("abc"); - QirString* b = __quantum__rt__string_create("xyz"); - QirString* abExpected = __quantum__rt__string_create("abcxyz"); - - QirString* ab = __quantum__rt__string_concatenate(a, b); - REQUIRE(ab == abExpected); - - QirString* aa = __quantum__rt__string_concatenate(a, a); - REQUIRE(aa->str.compare("abcabc") == 0); - - __quantum__rt__string_update_reference_count(a, -1); - __quantum__rt__string_update_reference_count(b, -1); - __quantum__rt__string_update_reference_count(abExpected, -1); - __quantum__rt__string_update_reference_count(ab, -1); - __quantum__rt__string_update_reference_count(aa, -1); - - REQUIRE(AllocatedStrings().empty()); -} - -TEST_CASE("Strings: conversions from built-in types", "[qir_support]") -{ - std::vector strings; - - strings.push_back(__quantum__rt__int_to_string(0)); - REQUIRE(strings.back()->str == std::string("0")); - - strings.push_back(__quantum__rt__int_to_string(42)); - REQUIRE(strings.back()->str == std::string("42")); - - strings.push_back(__quantum__rt__int_to_string(-42)); - REQUIRE(strings.back()->str == std::string("-42")); - - strings.push_back(__quantum__rt__double_to_string(4.2)); - REQUIRE(strings.back()->str == std::string("4.20000000000000018")); // platform dependent? - - strings.push_back(__quantum__rt__double_to_string(42.0)); - REQUIRE(strings.back()->str == std::string("42.0")); - - strings.push_back(__quantum__rt__double_to_string(1e-9)); - REQUIRE(strings.back()->str == std::string("0.000000001")); - - strings.push_back(__quantum__rt__double_to_string(0.0)); - REQUIRE(strings.back()->str == std::string("0.0")); - - strings.push_back(__quantum__rt__double_to_string(-42.0)); - REQUIRE(strings.back()->str == std::string("-42.0")); - - strings.push_back(__quantum__rt__double_to_string(-0.0)); - REQUIRE(strings.back()->str == std::string("-0.0")); - - strings.push_back(__quantum__rt__bool_to_string(false)); - REQUIRE(strings.back()->str == std::string("false")); - - strings.push_back(__quantum__rt__bool_to_string(true)); - REQUIRE(strings.back()->str == std::string("true")); - - // strings, created by conversions are reused for each type - strings.push_back(__quantum__rt__int_to_string(0)); - REQUIRE(std::count(strings.begin(), strings.end(), strings.back()) == 2); - - strings.push_back(__quantum__rt__double_to_string(42.0)); - REQUIRE(std::count(strings.begin(), strings.end(), strings.back()) == 2); - - strings.push_back(__quantum__rt__bool_to_string(1)); - REQUIRE(std::count(strings.begin(), strings.end(), strings.back()) == 2); - - for (QirString* qstr : strings) - { - __quantum__rt__string_update_reference_count(qstr, -1); - } - - REQUIRE(AllocatedStrings().empty()); -} - -TEST_CASE("Strings: conversions from custom qir types", "[qir_support]") -{ - QirString* qstr1 = quantum__rt__range_to_string({0, 1, 42}); - REQUIRE(qstr1->str == std::string("0..42")); - - QirString* qstr2 = quantum__rt__range_to_string({0, 3, 42}); - REQUIRE(qstr2->str == std::string("0..3..42")); - - __quantum__rt__string_update_reference_count(qstr1, -1); - __quantum__rt__string_update_reference_count(qstr2, -1); - - REQUIRE(AllocatedStrings().empty()); -} - -struct QubitTestQAPI : public SimulatorStub -{ - int lastId = -1; // TODO: Use unsigned type. - const int maxQubits; // TODO: Use unsigned type. - std::vector allocated; - - static QubitIdType GetQubitId(QubitIdType q) - { - return q; - } - - QubitTestQAPI(int maxQbits) : maxQubits(maxQbits), allocated((size_t)maxQbits, false) - { - } - - QubitIdType AllocateQubit() override - { - assert(this->lastId < this->maxQubits); - this->lastId++; - this->allocated.at((size_t)(this->lastId)) = true; - return static_cast(this->lastId); - } - void ReleaseQubit(QubitIdType qubit) override - { - const QubitIdType id = GetQubitId(qubit); - INFO(id); - REQUIRE(this->allocated.at(static_cast(id))); - this->allocated.at(static_cast(id)).flip(); - } - std::string QubitToString(QubitIdType qubit) override - { - const QubitIdType id = GetQubitId(qubit); - return std::to_string(id); - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - - bool HaveQubitsInFlight() const - { - for (const auto b : this->allocated) - { - if (b) - { - return true; - } - } - return false; - } -}; -TEST_CASE("Qubits: allocate, release, dump", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(4); - QirExecutionContext::Scoped qirctx(qapi.get()); - QirString* qstr = nullptr; - - QUBIT* q = __quantum__rt__qubit_allocate(); - qstr = __quantum__rt__qubit_to_string(q); - REQUIRE(qstr->str == std::string("0")); - __quantum__rt__string_update_reference_count(qstr, -1); - __quantum__rt__qubit_release(q); - REQUIRE(!qapi->HaveQubitsInFlight()); - - QirArray* qs = __quantum__rt__qubit_allocate_array(3); - REQUIRE(qs->ownsQubits); - REQUIRE(qs->count == 3); - REQUIRE(qs->itemSizeInBytes == sizeof(void*)); - - QUBIT* last = *reinterpret_cast(__quantum__rt__array_get_element_ptr_1d(qs, 2)); - qstr = __quantum__rt__qubit_to_string(last); - REQUIRE(qstr->str == std::string("3")); - __quantum__rt__string_update_reference_count(qstr, -1); - - QirArray* copy = __quantum__rt__array_copy(qs, true /*force*/); - REQUIRE(!copy->ownsQubits); - - __quantum__rt__qubit_release_array(qs); // The `qs` is a dangling pointer from now on. - REQUIRE(!qapi->HaveQubitsInFlight()); - - __quantum__rt__array_update_reference_count(copy, -1); -} - -QirTupleHeader* FlattenControlArrays(QirTupleHeader* nestedTuple, int depth); -struct ControlledCallablesTestSimulator : public SimulatorStub -{ - intptr_t lastId = -1; - QubitIdType AllocateQubit() override - { - return static_cast(++this->lastId); - } - void ReleaseQubit(QubitIdType /*qubit*/) override - { - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } -}; -TEST_CASE("Unpacking input tuples of nested callables (case2)", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - QUBIT* target = __quantum__rt__qubit_allocate(); - QirArray* controlsInner = __quantum__rt__qubit_allocate_array(3); - QirArray* controlsOuter = __quantum__rt__qubit_allocate_array(2); - - PTuple inner = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*Qubit*/ void*)); - TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); - innerWithControls->controls = controlsInner; - *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/* QirArray* */ void*)) = target; - - PTuple outer = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*QirTupleHeader*/ void*)); - TupleWithControls* outerWithControls = TupleWithControls::FromTuple(outer); - outerWithControls->controls = controlsOuter; - outerWithControls->innerTuple = innerWithControls; - - QirTupleHeader* unpacked = FlattenControlArrays(outerWithControls->GetHeader(), 2 /*depth*/); - QirArray* combined = *(reinterpret_cast(unpacked->AsTuple())); - REQUIRE(5 == combined->count); - REQUIRE(!combined->ownsQubits); - REQUIRE(target == *reinterpret_cast(unpacked->AsTuple() + sizeof(/*QirArrray*/ void*))); - - unpacked->Release(); - __quantum__rt__array_update_reference_count(combined, -1); - __quantum__rt__tuple_update_reference_count(outer, -1); - __quantum__rt__tuple_update_reference_count(inner, -1); - - // release the original resources - __quantum__rt__qubit_release_array(controlsOuter); // The `controlsOuter` is a dangling pointer from now on. - __quantum__rt__qubit_release_array(controlsInner); // The `controlsInner` is a dangling pointer from now on. - __quantum__rt__qubit_release(target); -} - -TEST_CASE("Unpacking input tuples of nested callables (case1)", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - QUBIT* target = __quantum__rt__qubit_allocate(); - QirArray* controlsInner = __quantum__rt__qubit_allocate_array(3); - QirArray* controlsOuter = __quantum__rt__qubit_allocate_array(2); - - PTuple args = __quantum__rt__tuple_create(sizeof(/*Qubit*/ void*) + sizeof(int)); - *reinterpret_cast(args) = target; - *reinterpret_cast(args + sizeof(/*Qubit*/ void*)) = 42; - - PTuple inner = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*Tuple*/ void*)); - TupleWithControls* innerWithControls = TupleWithControls::FromTuple(inner); - innerWithControls->controls = controlsInner; - *reinterpret_cast(innerWithControls->AsTuple() + sizeof(/* QirArray* */ void*)) = args; - - PTuple outer = __quantum__rt__tuple_create(sizeof(/* QirArray* */ void*) + sizeof(/*QirTupleHeader*/ void*)); - TupleWithControls* outerWithControls = TupleWithControls::FromTuple(outer); - outerWithControls->controls = controlsOuter; - outerWithControls->innerTuple = innerWithControls; - - QirTupleHeader* unpacked = FlattenControlArrays(outerWithControls->GetHeader(), 2 /*depth*/); - QirArray* combined = *(reinterpret_cast(unpacked->AsTuple())); - REQUIRE(5 == combined->count); - REQUIRE(!combined->ownsQubits); - - QirTupleHeader* unpackedArgs = - QirTupleHeader::GetHeader(*reinterpret_cast(unpacked->AsTuple() + sizeof(/* QirArray* */ void*))); - REQUIRE(target == *reinterpret_cast(unpackedArgs->AsTuple())); - REQUIRE(42 == *reinterpret_cast(unpackedArgs->AsTuple() + sizeof(/*Qubit*/ void*))); - - unpacked->Release(); - __quantum__rt__array_update_reference_count(combined, -1); - __quantum__rt__tuple_update_reference_count(outer, -1); - __quantum__rt__tuple_update_reference_count(inner, -1); - __quantum__rt__tuple_update_reference_count(args, -1); - - // release the original resources - __quantum__rt__qubit_release_array(controlsOuter); // The `controlsOuter` is a dangling pointer from now on. - __quantum__rt__qubit_release_array(controlsInner); // The `controlsInner` is a dangling pointer from now on. - __quantum__rt__qubit_release(target); -} - -TEST_CASE("Allocation tracking for arrays", "[qir_support]") -{ - QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); - - QirArray* bounce = __quantum__rt__array_create_1d(1, 1); - __quantum__rt__array_update_reference_count(bounce, -1); - CHECK_THROWS(__quantum__rt__array_update_reference_count(bounce, 1)); - - QirArray* releaseTwice = __quantum__rt__array_create_1d(1, 1); - __quantum__rt__array_update_reference_count(releaseTwice, -1); - CHECK_THROWS(__quantum__rt__array_update_reference_count(releaseTwice, -1)); - - QirArray* maybeLeaked = __quantum__rt__array_create_1d(1, 1); - CHECK_THROWS(QirExecutionContext::Deinit()); - - __quantum__rt__array_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(QirExecutionContext::Deinit()); -} - -TEST_CASE("Allocation tracking for tuples", "[qir_support]") -{ - QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); - - PTuple bounce = __quantum__rt__tuple_create(1); - __quantum__rt__tuple_update_reference_count(bounce, -1); - CHECK_THROWS(__quantum__rt__tuple_update_reference_count(bounce, 1)); - - PTuple releaseTwice = __quantum__rt__tuple_create(1); - __quantum__rt__tuple_update_reference_count(releaseTwice, -1); - CHECK_THROWS(__quantum__rt__tuple_update_reference_count(releaseTwice, -1)); - - PTuple maybeLeaked = __quantum__rt__tuple_create(1); - CHECK_THROWS(QirExecutionContext::Deinit()); - - __quantum__rt__tuple_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(QirExecutionContext::Deinit()); -} - -static void NoopCallableEntry(PTuple, PTuple, PTuple) -{ -} -TEST_CASE("Allocation tracking for callables", "[qir_support]") -{ - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - - QirExecutionContext::Init(nullptr /*don't need a simulator*/, true /*track allocations*/); - - QirCallable* bounce = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - __quantum__rt__callable_update_reference_count(bounce, -1); - CHECK_THROWS(__quantum__rt__callable_update_reference_count(bounce, 1)); - - QirCallable* releaseTwice = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - __quantum__rt__callable_update_reference_count(releaseTwice, -1); - CHECK_THROWS(__quantum__rt__callable_update_reference_count(releaseTwice, -1)); - - QirCallable* maybeLeaked = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - CHECK_THROWS(QirExecutionContext::Deinit()); - - __quantum__rt__callable_update_reference_count(maybeLeaked, -1); - CHECK_NOTHROW(QirExecutionContext::Deinit()); -} - -TEST_CASE("Callables: copy elision", "[qir_support]") -{ - QirExecutionContext::Scoped qirctx(nullptr, true); - t_CallableEntry entries[4] = {NoopCallableEntry, nullptr, nullptr, nullptr}; - - QirCallable* original = - __quantum__rt__callable_create(entries, nullptr /*capture callbacks*/, nullptr /*capture tuple*/); - - QirCallable* self = __quantum__rt__callable_copy(original, false); - CHECK(self == original); - - QirCallable* other1 = __quantum__rt__callable_copy(original, true); - CHECK(other1 != original); - - __quantum__rt__callable_update_alias_count(original, 1); - QirCallable* other2 = __quantum__rt__callable_copy(original, false); - CHECK(other2 != original); - __quantum__rt__callable_update_alias_count(original, -1); - - __quantum__rt__callable_update_reference_count(original, -1); - __quantum__rt__callable_update_reference_count(self, -1); - __quantum__rt__callable_update_reference_count(other1, -1); - __quantum__rt__callable_update_reference_count(other2, -1); -} - -TEST_CASE("Tuples: copy elision", "[qir_support]") -{ - PTuple original = __quantum__rt__tuple_create(1 /*size in bytes*/); - - PTuple self = __quantum__rt__tuple_copy(original, false); - CHECK(self == original); - - PTuple other1 = __quantum__rt__tuple_copy(original, true); - CHECK(other1 != original); - - __quantum__rt__tuple_update_alias_count(original, 1); - PTuple other2 = __quantum__rt__tuple_copy(original, false); - CHECK(other2 != original); - __quantum__rt__tuple_update_alias_count(original, -1); - - __quantum__rt__tuple_update_reference_count(original, -1); - __quantum__rt__tuple_update_reference_count(self, -1); - __quantum__rt__tuple_update_reference_count(other1, -1); - __quantum__rt__tuple_update_reference_count(other2, -1); -} - -// Adjoints for R and Exp are implemented by qis, so let's check they at least do the angle invertion in adjoints. -struct AdjointsTestSimulator : public SimulatorStub -{ - QubitIdType lastId = -1; - double rotationAngle = 0.0; - double exponentAngle = 0.0; - - QubitIdType AllocateQubit() override - { - return ++this->lastId; - } - void ReleaseQubit(QubitIdType /*qubit*/) override - { - } - Result UseZero() override - { - return reinterpret_cast(0); - } - Result UseOne() override - { - return reinterpret_cast(1); - } - - void R(PauliId, QubitIdType, double theta) override - { - this->rotationAngle += theta; - } - void Exp(long count, PauliId* paulis, QubitIdType*, double theta) override - { - this->exponentAngle += theta; - - // check that paulis were unpacked correctly (this assumes that the tests always invoke with the same axes) - REQUIRE(count == 2); - CHECK(paulis[0] == PauliId_Z); - CHECK(paulis[1] == PauliId_Y); - } - void ControlledR(long, QubitIdType*, PauliId, QubitIdType, double theta) override - { - this->rotationAngle += theta; - } - void ControlledExp(long, QubitIdType*, long count, PauliId* paulis, QubitIdType*, double theta) override - { - this->exponentAngle += theta; - - // check that paulis were unpacked correctly (this assumes that the tests always invoke with the same axes) - REQUIRE(count == 2); - CHECK(paulis[0] == PauliId_Z); - CHECK(paulis[1] == PauliId_Y); - } -}; -TEST_CASE("Adjoints of R should use inverse of the angle", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - const double angle = 0.42; - - QUBIT* target = __quantum__rt__qubit_allocate(); - QirArray* ctrls = __quantum__rt__qubit_allocate_array(2); - - __quantum__qis__r__body(PauliId_Y, angle, target); - __quantum__qis__r__adj(PauliId_Y, angle, target); - QirRTuple args = {PauliId_X, angle, target}; - __quantum__qis__r__ctl(ctrls, &args); - __quantum__qis__r__ctladj(ctrls, &args); - - __quantum__rt__qubit_release_array(ctrls); // The `ctrls` is a dangling pointer from now on. - __quantum__rt__qubit_release(target); - - REQUIRE(qapi->rotationAngle == Approx(0).epsilon(0.0001)); -} - -TEST_CASE("Adjoints of Exp should use inverse of the angle", "[qir_support]") -{ - std::unique_ptr qapi = std::make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get()); - - const double angle = 0.42; - - QirArray* targets = __quantum__rt__qubit_allocate_array(2); - QirArray* ctrls = __quantum__rt__qubit_allocate_array(2); - QirArray* axes = __quantum__rt__array_create_1d(1 /*element size*/, 2 /*count*/); - axes->buffer[0] = PauliId_Z; - axes->buffer[1] = PauliId_Y; - - __quantum__qis__exp__body(axes, angle, targets); - __quantum__qis__exp__adj(axes, angle, targets); - QirExpTuple args = {axes, angle, targets}; - __quantum__qis__exp__ctl(ctrls, &args); - __quantum__qis__exp__ctladj(ctrls, &args); - - __quantum__rt__array_update_reference_count(axes, -1); - __quantum__rt__qubit_release_array(ctrls); // The `ctrls` is a dangling pointer from now on. - __quantum__rt__qubit_release_array(targets); // The `targets` is a dangling pointer from now on. - - REQUIRE(qapi->exponentAngle == Approx(0).epsilon(0.0001)); -} diff --git a/src/Qir/Runtime/unittests/QubitManagerTests.cpp b/src/Qir/Runtime/unittests/QubitManagerTests.cpp deleted file mode 100644 index 4099e7fff79..00000000000 --- a/src/Qir/Runtime/unittests/QubitManagerTests.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include - -#include "catch.hpp" - -#include "QubitManager.hpp" - -using namespace Microsoft::Quantum; - -TEST_CASE("Simple allocation and release of one qubit", "[QubitManagerBasic]") -{ - std::unique_ptr qm = std::make_unique(); - QubitIdType q = qm->Allocate(); - qm->Release(q); -} - -TEST_CASE("Allocation and reallocation of one qubit", "[QubitManagerBasic]") -{ - std::unique_ptr qm = std::make_unique(1, false); - REQUIRE(qm->GetFreeQubitCount() == 1); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - QubitIdType q = qm->Allocate(); - REQUIRE(q == 0); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 1); - REQUIRE_THROWS(qm->Allocate()); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 1); - qm->Release(q); - REQUIRE(qm->GetFreeQubitCount() == 1); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - QubitIdType q0 = qm->Allocate(); - REQUIRE(q0 == 0); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 1); - qm->Release(q0); - REQUIRE(qm->GetFreeQubitCount() == 1); - REQUIRE(qm->GetAllocatedQubitCount() == 0); -} - -TEST_CASE("Qubit Status", "[QubitManagerBasic]") -{ - std::unique_ptr qm = std::make_unique(2, false); - - QubitIdType q0 = qm->Allocate(); - REQUIRE(qm->IsValidQubit(q0)); - REQUIRE(qm->IsExplicitlyAllocatedQubit(q0)); - REQUIRE(!qm->IsDisabledQubit(q0)); - REQUIRE(!qm->IsFreeQubitId(q0)); - REQUIRE(qm->GetAllocatedQubitCount() == 1); - - qm->Disable(q0); - REQUIRE(qm->IsValidQubit(q0)); - REQUIRE(!qm->IsExplicitlyAllocatedQubit(q0)); - REQUIRE(qm->IsDisabledQubit(q0)); - REQUIRE(!qm->IsFreeQubitId(q0)); - REQUIRE(qm->GetDisabledQubitCount() == 1); - - qm->Release(q0); - REQUIRE(!qm->IsFreeQubitId(q0)); - REQUIRE(qm->GetFreeQubitCount() == 1); - - QubitIdType q1 = qm->Allocate(); - REQUIRE(q0 != q1); - - REQUIRE(qm->IsValidQubit(q1)); - REQUIRE(qm->IsExplicitlyAllocatedQubit(q1)); - REQUIRE(!qm->IsDisabledQubit(q1)); - REQUIRE(!qm->IsFreeQubitId(q1)); - - REQUIRE(qm->GetAllocatedQubitCount() == 1); - REQUIRE(qm->GetDisabledQubitCount() == 1); - REQUIRE(qm->GetFreeQubitCount() == 0); - - qm->Release(q1); - REQUIRE(qm->IsFreeQubitId(q1)); -} - -TEST_CASE("Qubit Counts", "[QubitManagerBasic]") -{ - constexpr int totalQubitCount = 100; - constexpr int disabledQubitCount = 29; - constexpr int extraQubitCount = 43; - static_assert(extraQubitCount <= totalQubitCount); - static_assert(disabledQubitCount <= totalQubitCount); - // We don't want capacity to be extended at first... - static_assert(extraQubitCount + disabledQubitCount <= totalQubitCount); - - std::unique_ptr qm = std::make_unique(totalQubitCount, true); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - REQUIRE(qm->GetDisabledQubitCount() == 0); - - QubitIdType* qubits = new QubitIdType[disabledQubitCount]; - qm->Allocate(qubits, disabledQubitCount); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount - disabledQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == disabledQubitCount); - REQUIRE(qm->GetDisabledQubitCount() == 0); - - qm->Disable(qubits, disabledQubitCount); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount - disabledQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); - - qm->Release(qubits, disabledQubitCount); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount - disabledQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); - delete[] qubits; - - qubits = new QubitIdType[extraQubitCount]; - qm->Allocate(qubits, extraQubitCount); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount - disabledQubitCount - extraQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == extraQubitCount); - REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); - - qm->Release(qubits, extraQubitCount); - REQUIRE(qm->GetQubitCapacity() == totalQubitCount); - REQUIRE(qm->GetFreeQubitCount() == totalQubitCount - disabledQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); - delete[] qubits; - - qubits = new QubitIdType[totalQubitCount]; - qm->Allocate(qubits, totalQubitCount); - REQUIRE(qm->GetQubitCapacity() > totalQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == totalQubitCount); - REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); - - qm->Release(qubits, totalQubitCount); - REQUIRE(qm->GetQubitCapacity() > totalQubitCount); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); - delete[] qubits; -} - -TEST_CASE("Allocation of released qubits when reuse is encouraged", "[QubitManagerBasic]") -{ - std::unique_ptr qm = std::make_unique(2, false); - REQUIRE(qm->GetFreeQubitCount() == 2); - QubitIdType q0 = qm->Allocate(); - QubitIdType q1 = qm->Allocate(); - REQUIRE(q0 == 0); // Qubit ids should be in order - REQUIRE(q1 == 1); - REQUIRE_THROWS(qm->Allocate()); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 2); - - qm->Release(q0); - QubitIdType q0a = qm->Allocate(); - REQUIRE(q0a == 0); // It was the only one available - REQUIRE_THROWS(qm->Allocate()); - - qm->Release(q1); - qm->Release(q0a); - REQUIRE(qm->GetFreeQubitCount() == 2); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - - QubitIdType q0b = qm->Allocate(); - QubitIdType q1a = qm->Allocate(); - REQUIRE(q0b == 0); // By default reuse is encouraged, LIFO is used - REQUIRE(q1a == 1); - REQUIRE_THROWS(qm->Allocate()); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 2); - - qm->Release(q0b); - qm->Release(q1a); -} - -TEST_CASE("Extending capacity", "[QubitManager]") -{ - std::unique_ptr qm = std::make_unique(1, true); - - QubitIdType q0 = qm->Allocate(); - REQUIRE(q0 == 0); - QubitIdType q1 = qm->Allocate(); // This should double capacity - REQUIRE(q1 == 1); - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE(qm->GetAllocatedQubitCount() == 2); - - qm->Release(q0); - QubitIdType q0a = qm->Allocate(); - REQUIRE(q0a == 0); - QubitIdType q2 = qm->Allocate(); // This should double capacity again - REQUIRE(q2 == 2); - REQUIRE(qm->GetFreeQubitCount() == 1); - REQUIRE(qm->GetAllocatedQubitCount() == 3); - - qm->Release(q1); - qm->Release(q0a); - qm->Release(q2); - REQUIRE(qm->GetFreeQubitCount() == 4); - REQUIRE(qm->GetAllocatedQubitCount() == 0); - - QubitIdType* qqq = new QubitIdType[3]; - qm->Allocate(qqq, 3); - REQUIRE(qm->GetFreeQubitCount() == 1); - REQUIRE(qm->GetAllocatedQubitCount() == 3); - qm->Release(qqq, 3); - delete[] qqq; - REQUIRE(qm->GetFreeQubitCount() == 4); - REQUIRE(qm->GetAllocatedQubitCount() == 0); -} - -TEST_CASE("Restricted Area", "[QubitManager]") -{ - std::unique_ptr qm = std::make_unique(3, false); - - QubitIdType q0 = qm->Allocate(); - REQUIRE(q0 == 0); - - qm->StartRestrictedReuseArea(); - - // Allocates fresh qubit - QubitIdType q1 = qm->Allocate(); - REQUIRE(q1 == 1); - qm->Release(q1); // Released, but cannot be used in the next segment. - - qm->NextRestrictedReuseSegment(); - - // Allocates fresh qubit, q1 cannot be reused - it belongs to a differen segment. - QubitIdType q2 = qm->Allocate(); - REQUIRE(q2 == 2); - qm->Release(q2); - - QubitIdType q2a = qm->Allocate(); // Getting the same one as the one that's just released. - REQUIRE(q2a == 2); - qm->Release(q2a); // Released, but cannot be used in the next segment. - - qm->NextRestrictedReuseSegment(); - - // There's no qubits left here. q0 is allocated, q1 and q2 are from different segments. - REQUIRE_THROWS(qm->Allocate()); - - qm->EndRestrictedReuseArea(); - - // Qubits 1 and 2 are available here again. - QubitIdType* qqq = new QubitIdType[2]; - qm->Allocate(qqq, 2); - // OK to destruct qubit manager while qubits are still allocated. - REQUIRE_THROWS(qm->Allocate()); - delete[] qqq; -} - -TEST_CASE("Nested Restricted Areas", "[QubitManager]") -{ - constexpr int n = 10; - std::unique_ptr qm = std::make_unique(n, false); - for (int i = 0; i < n; i++) - { - QubitIdType q = qm->Allocate(); // Reuse qubit from previous area - qm->Release(q); // Put a free qubit in the innermost area - qm->StartRestrictedReuseArea(); - } - REQUIRE(qm->GetFreeQubitCount() == n); - // First new allocation will be served from the previous area and the next allocation - // will check entire list up to the outermost area. But after that allocation will - // jump right into outermost area. So allocation isn't O(1), but is amortized O(1). - QubitIdType* qubits = new QubitIdType[n]; - qm->Allocate(qubits, n); // Check that we still can allocate - REQUIRE(qm->GetFreeQubitCount() == 0); - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(qubits, n); - for (int i = 0; i < n; i++) - { - qm->EndRestrictedReuseArea(); - } - qm->Allocate(qubits, n); - qm->Release(qubits, n); - REQUIRE(qm->GetFreeQubitCount() == n); - delete[] qubits; -} - -TEST_CASE("Nested Restricted Areas With Qubits", "[QubitManager]") -{ - constexpr int n = 100; - - std::unique_ptr qm = std::make_unique(n, false); - QubitIdType* qubits = new QubitIdType[n]; - qm->Allocate(qubits, n); // Allocate All - REQUIRE(qm->GetFreeQubitCount() == 0); - - for (int i = 0; i < n; i++) - { - qm->StartRestrictedReuseArea(); - qm->Release(qubits[i]); // Put a free qubit in this area - } - REQUIRE(qm->GetFreeQubitCount() == n); // There're n free qubits, one in each area - qm->Allocate(qubits, n); // And we can allocate them all - - for (int i = 0; i < n; i++) - { - qm->EndRestrictedReuseArea(); - qm->Release(qubits[i]); // Release out of order - } - - REQUIRE(qm->GetFreeQubitCount() == n); // There're n free qubits still - qm->Allocate(qubits, n); // And we can allocate them. - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(qubits, n); - REQUIRE(qm->GetFreeQubitCount() == n); - delete[] qubits; -} - -TEST_CASE("Many Nested Restricted Areas", "[QubitManager]") -{ - constexpr int n = 10000; - std::unique_ptr qm = std::make_unique(1, false); - REQUIRE(qm->GetFreeQubitCount() == 1); - - // Allocate a lot of nested areas - for (int i = 0; i < n; i++) - { - qm->StartRestrictedReuseArea(); - } - - REQUIRE(qm->GetFreeQubitCount() == 1); - QubitIdType q = qm->Allocate(); - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(q); - REQUIRE(qm->GetFreeQubitCount() == 1); - - for (int i = 0; i < n; i++) - { - qm->EndRestrictedReuseArea(); - } - - REQUIRE(qm->GetFreeQubitCount() == 1); - q = qm->Allocate(); - REQUIRE_THROWS(qm->Allocate()); // Reached capacity - qm->Release(q); - REQUIRE(qm->GetFreeQubitCount() == 1); -} diff --git a/src/Qir/Runtime/unittests/ToffoliTests.cpp b/src/Qir/Runtime/unittests/ToffoliTests.cpp deleted file mode 100644 index 08cc0847cf1..00000000000 --- a/src/Qir/Runtime/unittests/ToffoliTests.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include - -#include "catch.hpp" - -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" - -using namespace Microsoft::Quantum; - -static Result MZ(IQuantumGateSet* iqa, QubitIdType q) -{ - PauliId pauliZ[1] = {PauliId_Z}; - QubitIdType qs[1] = {q}; - return iqa->Measure(1, pauliZ, 1, qs); -} - -TEST_CASE("Basis vector", "[toffoli]") -{ - std::unique_ptr sim = CreateToffoliSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - constexpr int n = 1000; - std::vector qubits; - qubits.reserve(n); - for (int i = 0; i < n; i++) - { - QubitIdType q = sim->AllocateQubit(); - qubits.push_back(q); - if ((i & 0x1) == 1) - { - iqa->X(q); - } - } - - long sum = 0; - for (QubitIdType q : qubits) - { - if (sim->GetResultValue(MZ(iqa, q)) == Result_One) - { - sum++; - } - } - REQUIRE(sum == n / 2); -} - -TEST_CASE("Controlled X", "[toffoli]") -{ - std::unique_ptr sim = CreateToffoliSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q[4]; - q[0] = sim->AllocateQubit(); - q[1] = sim->AllocateQubit(); - q[2] = sim->AllocateQubit(); - q[3] = sim->AllocateQubit(); - - // qubits state: |0000> - iqa->ControlledX(1, &q[0], q[1]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[1])) == Result_Zero); - iqa->ControlledX(2, &q[0], q[2]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_Zero); - iqa->ControlledX(3, &q[0], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_Zero); - - iqa->X(q[0]); - - // qubits state: |1000> - iqa->ControlledX(2, &q[0], q[2]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_Zero); - iqa->ControlledX(3, &q[0], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[3])) == Result_Zero); - iqa->ControlledX(1, &q[0], q[2]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[2])) == Result_One); - - // qubits state: |1010> - iqa->ControlledX(3, &q[0], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[3])) == Result_Zero); - - iqa->X(q[1]); - - // qubits state: |1110> - iqa->ControlledX(2, &q[1], q[3]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[3])) == Result_One); - - // qubits state: |1111> - iqa->ControlledX(3, &q[1], q[0]); - REQUIRE(sim->GetResultValue(MZ(iqa, q[0])) == Result_Zero); -} - -TEST_CASE("Measure and assert probability", "[toffoli]") -{ - std::unique_ptr sim = CreateToffoliSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - const int count = 3; - QubitIdType qs[count]; - for (int i = 0; i < count; i++) - { - qs[i] = sim->AllocateQubit(); - } - - PauliId zzz[count] = {PauliId_Z, PauliId_Z, PauliId_Z}; - PauliId ziz[count] = {PauliId_Z, PauliId_I, PauliId_Z}; - - // initial state is |000> - IDiagnostics* idig = dynamic_cast(sim.get()); - REQUIRE(sim->GetResultValue(iqa->Measure(count, zzz, count, qs)) == Result_Zero); - REQUIRE(sim->GetResultValue(iqa->Measure(count, ziz, count, qs)) == Result_Zero); - REQUIRE(idig->Assert(count, zzz, qs, sim->UseZero(), "")); - REQUIRE(idig->AssertProbability(count, zzz, qs, 1.0, 0.01, "")); - - // set state to: |010> - iqa->X(qs[1]); - REQUIRE(sim->GetResultValue(iqa->Measure(count, zzz, count, qs)) == Result_One); - REQUIRE(sim->GetResultValue(iqa->Measure(count, ziz, count, qs)) == Result_Zero); - REQUIRE(idig->Assert(count, zzz, qs, sim->UseOne(), "")); - REQUIRE(idig->AssertProbability(count, ziz, qs, 1.0, 0.01, "")); - - // set state to: |111> - iqa->X(qs[0]); - iqa->X(qs[2]); - REQUIRE(sim->GetResultValue(iqa->Measure(count, zzz, count, qs)) == Result_One); - REQUIRE(sim->GetResultValue(iqa->Measure(count, ziz, count, qs)) == Result_Zero); - REQUIRE(idig->Assert(count, ziz, qs, sim->UseZero(), "")); - REQUIRE(idig->AssertProbability(count, zzz, qs, 0.0, 0.01, "")); -} diff --git a/src/Qir/Runtime/unittests/TracerTests.cpp b/src/Qir/Runtime/unittests/TracerTests.cpp deleted file mode 100644 index dc68a41576b..00000000000 --- a/src/Qir/Runtime/unittests/TracerTests.cpp +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include -#include - -#include "catch.hpp" - -#include "CoreTypes.hpp" -#include "tracer.hpp" - -using namespace Microsoft::Quantum; - -TEST_CASE("Layering distinct single-qubit operations of non-zero durations", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); // L(0,3) should be created - CHECK(0 == tr->TraceSingleQubitOp(2, 2, q1)); // add the op into L(0,3) - CHECK(0 == tr->TraceSingleQubitOp(3, 1, q2)); // add the op into L(0,3) - CHECK(1 == tr->TraceSingleQubitOp(4, 3, q2)); // create new layer L(3,3) - CHECK(2 == tr->TraceSingleQubitOp(5, 4, q2)); // long op! create new layer L(6,4) - CHECK(1 == tr->TraceSingleQubitOp(6, 2, q1)); // add the op into L(3,3) - CHECK(0 == tr->TraceSingleQubitOp(7, 1, q3)); // add the op into L(0,3) - CHECK(2 == tr->TraceSingleQubitOp(8, 4, q3)); // long op! but fits into existing L(6,4) - CHECK(3 == tr->TraceSingleQubitOp(9, 5, q1)); // long op! add the op into L(10,5) - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 4); - CHECK(layers[0].startTime == 0); - CHECK(layers[0].operations.size() == 4); - CHECK(layers[1].startTime == 3); - CHECK(layers[1].operations.size() == 2); - CHECK(layers[2].startTime == 6); - CHECK(layers[2].operations.size() == 2); - CHECK(layers[3].startTime == 10); - CHECK(layers[3].operations.size() == 1); -} - -TEST_CASE("Layering single-qubit operations of zero duration", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); // L(0,3) should be created - CHECK(0 == tr->TraceSingleQubitOp(2, 0, q1)); // add the op into L(0,3) - CHECK(INVALID == tr->TraceSingleQubitOp(3, 0, q3)); // pending zero op (will remain orphan) - CHECK(INVALID == tr->TraceSingleQubitOp(4, 0, q2)); // pending zero op - CHECK(INVALID == tr->TraceSingleQubitOp(5, 0, q2)); // another pending zero op - CHECK(0 == tr->TraceSingleQubitOp(6, 1, q2)); // add the op into L(0,3) together with the pending ones - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 1); - CHECK(layers[0].operations.size() == 5); -} - -TEST_CASE("Layering distinct controlled single-qubit operations", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - QubitIdType q6 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceMultiQubitOp(1, 1, 1 /*nFirst*/, &q1 /*first*/, 1 /*nSecond*/, &q2 /*second*/)); - CHECK(0 == tr->TraceMultiQubitOp(2, 2, 0 /*nFirst*/, nullptr /*first*/, 1 /*nSecond*/, &q2 /*second*/)); - // q2 now is at the limit of the layer duration - - QubitIdType qs12[2] = {q1, q2}; - CHECK(1 == tr->TraceMultiQubitOp(3, 1, 0 /*nFirst*/, nullptr /*first*/, 2 /*nSecond*/, qs12 /*second*/)); - CHECK(1 == tr->TraceMultiQubitOp(4, 1, 1 /*nFirst*/, &q2 /*first*/, 1 /*nSecond*/, &q3 /*second*/)); - // because of q2, both ops should have been added to a new layer, which now "catches" q1, q2, q3 - - CHECK(0 == tr->TraceMultiQubitOp(5, 0, 1 /*nFirst*/, &q4 /*first*/, 1 /*nSecond*/, &q5 /*second*/)); - CHECK(0 == tr->TraceSingleQubitOp(6, 1, q6)); - // these ops should fall through into the first layer (notice no special handling of duration zero) - - CHECK(1 == tr->TraceMultiQubitOp(7, 1, 1 /*nFirst*/, &q1 /*first*/, 1 /*nSecond*/, &q6 /*second*/)); - CHECK(1 == tr->TraceMultiQubitOp(8, 1, 1 /*nFirst*/, &q3 /*first*/, 1 /*nSecond*/, &q4 /*second*/)); - // because of q1 and q3, thiese ops should be added into the second layer, which now has all but q5 - - CHECK(0 == tr->TraceSingleQubitOp(9, 1, q5)); - // should fall through to the first layer - - QubitIdType qs46[2] = {q4, q6}; - CHECK(1 == tr->TraceMultiQubitOp(10, 1, 2 /*nFirst*/, qs46 /*first*/, 1 /*nSecond*/, &q5 /*second*/)); - // because of the controls, should be added into the second layer - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 2); - - CHECK(layers[0].operations.size() == 5); - const auto& ops0 = layers[0].operations; - CHECK(ops0.find(1) != ops0.end()); - CHECK(ops0.find(2) != ops0.end()); - CHECK(ops0.find(5) != ops0.end()); - CHECK(ops0.find(6) != ops0.end()); - CHECK(ops0.find(9) != ops0.end()); - - CHECK(layers[1].operations.size() == 5); - const auto& ops1 = layers[1].operations; - CHECK(ops1.find(3) != ops1.end()); - CHECK(ops1.find(4) != ops1.end()); - CHECK(ops1.find(7) != ops1.end()); - CHECK(ops1.find(8) != ops1.end()); - CHECK(ops1.find(10) != ops1.end()); -} - -// TODO: add multi-qubit ops -TEST_CASE("Operations with same id are counted together", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - // All of these ops should fit into a single layer L(0,3) - tr->TraceSingleQubitOp(1, 1, q1); - tr->TraceSingleQubitOp(2, 2, q1); - tr->TraceSingleQubitOp(1, 1, q2); - tr->TraceSingleQubitOp(2, 1, q2); - tr->TraceSingleQubitOp(1, 1, q2); - tr->TraceSingleQubitOp(3, 2, q3); - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 1); - CHECK(layers[0].operations.size() == 3); - const auto& ops = layers[0].operations; - CHECK(ops.find(1)->second == 3); - CHECK(ops.find(2)->second == 2); - CHECK(ops.find(3)->second == 1); -} - -TEST_CASE("Global barrier", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(2 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 4, q1)); // L(0,4) created - CHECK(0 == tr->TraceSingleQubitOp(2, 1, q4)); // added to L(0,4) - CHECK(1 == tr->InjectGlobalBarrier(42, 1)); // creates L(4,2) - - CHECK(2 == tr->TraceMultiQubitOp(3, 1, 1 /*nFirst*/, &q2 /*first*/, 1 /*nSecond*/, &q3 /*second*/)); - // the barrier shouldn't allow this op to fall through into L(0,4), so should create L(6,2) - - CHECK(INVALID == tr->TraceSingleQubitOp(4, 0, q1)); - // the barrier shouldn't allow this op to fall through into L(0,4), so should create pending op - - CHECK(2 == tr->TraceSingleQubitOp(5, 1, q1)); - // should be added into L(6,2) together with the pending op `3` - - CHECK(3 == tr->TraceSingleQubitOp(6, 3, q2)); - // long op, with no existing wide layers to host it, so should create L(8,3) - - CHECK(3 == tr->TraceSingleQubitOp(7, 3, q4)); - // long op but can be added into L(8,3), which is post the barrier - - const std::vector& layers = tr->UseLayers(); - REQUIRE(layers.size() == 4); - CHECK(layers[0].operations.size() == 2); - CHECK(layers[1].operations.size() == 0); - CHECK(layers[2].operations.size() == 3); - CHECK(layers[3].operations.size() == 2); - - const auto& ops0 = layers[0].operations; - CHECK(ops0.find(1) != ops0.end()); - CHECK(ops0.find(2) != ops0.end()); - - CHECK(42 == layers[1].barrierId); - - const auto& ops2 = layers[2].operations; - CHECK(ops2.find(3) != ops2.end()); - CHECK(ops2.find(4) != ops2.end()); - CHECK(ops2.find(5) != ops2.end()); - - const auto& ops3 = layers[3].operations; - CHECK(ops3.find(6) != ops3.end()); - CHECK(ops3.find(7) != ops3.end()); -} - -// For layering purposes, measurements behave pretty much the same as other operations -TEST_CASE("Layering measurements", "[tracer]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - - CHECK(0 == tr->GetLayerIdOfSourceMeasurement(tr->TraceSingleQubitMeasurement(1, 1, q1))); - QubitIdType qs12[2] = {q1, q2}; - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(tr->TraceMultiQubitMeasurement(2, 1, 2, qs12))); - CHECK(0 == tr->TraceSingleQubitOp(3, 1, q4)); - CHECK(0 == tr->GetLayerIdOfSourceMeasurement(tr->TraceSingleQubitMeasurement(4, 1, q3))); - QubitIdType qs23[2] = {q2, q3}; - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(tr->TraceMultiQubitMeasurement(5, 1, 2, qs23))); - CHECK(1 == tr->TraceSingleQubitOp(3, 1, q4)); -} - -TEST_CASE("Conditionals: noops", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(3 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 3, q1)); - CHECK(1 == tr->TraceSingleQubitOp(1, 3, q1)); - Result one = tr->UseOne(); - { - CTracer::FenceScope fs(tr.get(), 1, &one, 0, nullptr); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - } - { - CTracer::FenceScope fs(tr.get(), 0, nullptr, 1, &one); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - } - { - CTracer::FenceScope fs(tr.get(), 0, nullptr, 0, nullptr); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - } -} - -TEST_CASE("Conditionals: a new layer because of the fence", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r, 0, nullptr); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q2)); - } - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q3)); -} - -TEST_CASE("Conditionals: single fence", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r)); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r, 0, nullptr); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q2)); - } - - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q1)); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q3)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q3)); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q3)); -} - -TEST_CASE("Conditionals: fence from two result arrays", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q2)); - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r1, 1, &r2); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q3)); - } - - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); -} - -TEST_CASE("Conditionals: nested fence is later than parent", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q2)); - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r1, 0, nullptr); - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q3)); - { - CTracer::FenceScope fs2(tr.get(), 0, nullptr, 1, &r2); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q4)); - } - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q5)); - } - - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); -} - -TEST_CASE("Conditionals: nested fence is earlier than parent", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q2)); - CHECK(1 == tr->TraceSingleQubitOp(1, 1, q2)); - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(2 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r2, 0, nullptr); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q3)); - { - CTracer::FenceScope fs2(tr.get(), 0, nullptr, 1, &r1); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q4)); - } - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q5)); - } - CHECK(2 == tr->TraceSingleQubitOp(1, 1, q1)); -} - -TEST_CASE("Conditionals: fences and barriers", "[tracer][tracer.conditionals]") -{ - std::shared_ptr tr = CreateTracer(1 /*layer duration*/); - - QubitIdType q1 = tr->AllocateQubit(); - QubitIdType q2 = tr->AllocateQubit(); - QubitIdType q3 = tr->AllocateQubit(); - QubitIdType q4 = tr->AllocateQubit(); - QubitIdType q5 = tr->AllocateQubit(); - - CHECK(0 == tr->TraceSingleQubitOp(1, 1, q1)); - Result r1 = tr->TraceSingleQubitMeasurement(1, 1, q1); - CHECK(1 == tr->GetLayerIdOfSourceMeasurement(r1)); - - CHECK(2 == tr->InjectGlobalBarrier(42, 1)); - - Result r2 = tr->TraceSingleQubitMeasurement(1, 1, q2); - CHECK(3 == tr->GetLayerIdOfSourceMeasurement(r2)); - - { - CTracer::FenceScope fs(tr.get(), 1, &r1, 0, nullptr); - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q3)); - } - { - CTracer::FenceScope fs(tr.get(), 0, nullptr, 1, &r2); - CHECK(4 == tr->TraceSingleQubitOp(1, 1, q4)); - } - CHECK(3 == tr->TraceSingleQubitOp(1, 1, q5)); -} - -TEST_CASE("Output: to string", "[tracer]") -{ - std::unordered_map opNames = {{1, "X"}, {2, "Y"}, {3, "Z"}, {4, "b"}}; - std::shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); - - QubitIdType q1 = tr->AllocateQubit(); - tr->TraceSingleQubitOp(3, 1, q1); - tr->TraceSingleQubitOp(5, 1, q1); - tr->InjectGlobalBarrier(4, 2); - tr->TraceSingleQubitOp(3, 4, q1); - tr->TraceSingleQubitOp(2, 1, q1); - - { - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - std::string metrics = out.str(); - - std::stringstream expected; - expected << "layer_id,name,Y,Z,5" << std::endl; - expected << "0,,0,1,0" << std::endl; - expected << "1,,0,0,1" << std::endl; - expected << "2,b,0,0,0" << std::endl; - expected << "4,,0,1,0" << std::endl; - expected << "8,,1,0,0" << std::endl; - - INFO(metrics); - CHECK(metrics == expected.str()); - } - - { - std::stringstream out; - tr->PrintLayerMetrics(out, ",", false /*printZeroMetrics*/); - std::string metrics = out.str(); - - std::stringstream expected; - expected << "layer_id,name,Y,Z,5" << std::endl; - expected << "0,,,1," << std::endl; - expected << "1,,,,1" << std::endl; - expected << "2,b,,," << std::endl; - expected << "4,,,1," << std::endl; - expected << "8,,1,," << std::endl; - - INFO(metrics); - CHECK(metrics == expected.str()); - } -} - -TEST_CASE("Output: to file", "[tracer]") -{ - std::unordered_map opNames = {{1, "X"}, {2, "Y"}, {3, "Z"}, {4, "b"}}; - std::shared_ptr tr = CreateTracer(1 /*layer duration*/, opNames); - - QubitIdType q1 = tr->AllocateQubit(); - tr->TraceSingleQubitOp(3, 1, q1); - tr->TraceSingleQubitOp(5, 1, q1); - tr->InjectGlobalBarrier(4, 2); - tr->TraceSingleQubitOp(3, 4, q1); - tr->TraceSingleQubitOp(2, 1, q1); - - const std::string fileName = "tracer-test.txt"; - std::ofstream out; - out.open(fileName); - tr->PrintLayerMetrics(out, "\t", false /*printZeroMetrics*/); - out.close(); - - std::ifstream in(fileName); - std::string line; - REQUIRE(in.is_open()); - std::string metrics(std::istreambuf_iterator{in}, {}); - in.close(); - - std::stringstream expected; - expected << "layer_id\tname\tY\tZ\t5" << std::endl; - expected << "0\t\t\t1\t" << std::endl; - expected << "1\t\t\t\t1" << std::endl; - expected << "2\tb\t\t\t" << std::endl; - expected << "4\t\t\t1\t" << std::endl; - expected << "8\t\t1\t\t" << std::endl; - - INFO(metrics); - CHECK(metrics == expected.str()); -} diff --git a/src/Qir/Runtime/unittests/driver.cpp b/src/Qir/Runtime/unittests/driver.cpp deleted file mode 100644 index b90b6434bd2..00000000000 --- a/src/Qir/Runtime/unittests/driver.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -// https://github.com/catchorg/Catch2/blob/master/docs/tutorial.md diff --git a/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt index a162dc8c2f9..cfc5615a429 100644 --- a/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt +++ b/src/Qir/Samples/StandaloneInputReference/CMakeLists.txt @@ -34,9 +34,8 @@ else() set(TEST_DEPS1 "${PROJECT_SOURCE_DIR}/../../Simulation/native/build/drop") endif() -set(TEST_DEPS2 "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") set_property(TEST qir-input-reference-standalone PROPERTY ENVIRONMENT - "LD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${LD_LIBRARY_PATH}" - "PATH=${TEST_DEPS1}\;${TEST_DEPS2}\;${PATH}" - "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${TEST_DEPS2}:${DYLD_LIBRARY_PATH}" + "LD_LIBRARY_PATH=${TEST_DEPS1}:${LD_LIBRARY_PATH}" + "PATH=${TEST_DEPS1}\;${PATH}" + "DYLD_LIBRARY_PATH=${TEST_DEPS1}:${DYLD_LIBRARY_PATH}" ) diff --git a/src/Qir/Tests/.clang-tidy b/src/Qir/Tests/.clang-tidy deleted file mode 100644 index 8b9489eeabb..00000000000 --- a/src/Qir/Tests/.clang-tidy +++ /dev/null @@ -1,3 +0,0 @@ -InheritParentConfig: true -Checks: - '-cert-err58-cpp' diff --git a/src/Qir/Tests/CMakeLists.txt b/src/Qir/Tests/CMakeLists.txt index 7e8d960f2dc..c0e18d2d029 100644 --- a/src/Qir/Tests/CMakeLists.txt +++ b/src/Qir/Tests/CMakeLists.txt @@ -18,20 +18,19 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") -set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") -set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") +set(public_includes "${PROJECT_SOURCE_DIR}/../drops/include") set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") -set(runtime_lib_path "${PROJECT_SOURCE_DIR}/../Runtime/bin/$ENV{BUILD_CONFIGURATION}/bin") +# set the environment path for loading shared libs the tests are using +if(DEFINED ENV{NATIVE_SIMULATOR}) + set(simulator_lib_path $ENV{NATIVE_SIMULATOR}) +else() + set(simulator_lib_path "${PROJECT_SOURCE_DIR}/../../Simulation/Native/build/drop") +endif() include(qir_cmake_include) include(unit_test_include) -# Temporarily exclude the `FullstateSimulator` test from the sanitized (Debug) build on Linux: -if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Linux" OR NOT "${CMAKE_BUILD_TYPE}" MATCHES "Debug") # TODO(kuzminrobin, #1078): Remove this condition after the bug is fixed. add_subdirectory(FullstateSimulator) -endif() - add_subdirectory(QIR-dynamic) add_subdirectory(QIR-static) -add_subdirectory(QIR-tracer) diff --git a/src/Qir/Tests/FullstateSimulator/CMakeLists.txt b/src/Qir/Tests/FullstateSimulator/CMakeLists.txt index a304922fe12..c5d55e43c8f 100644 --- a/src/Qir/Tests/FullstateSimulator/CMakeLists.txt +++ b/src/Qir/Tests/FullstateSimulator/CMakeLists.txt @@ -1,18 +1,14 @@ -add_executable(fullstate-simulator-tests - FullstateSimulatorTests.cpp) +add_executable(fullstate-simulator-tests FullstateSimulatorTests.cpp) target_source_from_qir(fullstate-simulator-tests qsharp/obj/qsharp/qir-test-simulator.bc) target_link_libraries(fullstate-simulator-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(fullstate-simulator-tests PUBLIC ${test_includes} - ${public_includes} ) install(TARGETS fullstate-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index 8651394d4c3..877fd3a985c 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -9,442 +9,16 @@ #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" -#include "QirRuntimeApi_I.hpp" -#include "QSharpSimApi_I.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" - -using namespace Microsoft::Quantum; using namespace std; -#ifndef RIQIR_TESTING - -// The tests rely on the implementation detail of CFullstateSimulator that the qubits are nothing more but contiguously -// incremented ids. -static unsigned GetQubitId(QubitIdType q) -{ - return static_cast(static_cast(q)); -} - -static Result MZ(IQuantumGateSet* iqa, QubitIdType q) -{ - PauliId pauliZ[1] = {PauliId_Z}; - QubitIdType qs[1] = {q}; - return iqa->Measure(1, pauliZ, 1, qs); -} - -TEST_CASE("Fullstate simulator: allocate qubits", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - - QubitIdType q0 = sim->AllocateQubit(); - QubitIdType q1 = sim->AllocateQubit(); - REQUIRE(GetQubitId(q0) == 0); - REQUIRE(GetQubitId(q1) == 1); - sim->ReleaseQubit(q0); - sim->ReleaseQubit(q1); -} - -TEST_CASE("Fullstate simulator: multiple instances", "[fullstate_simulator]") -{ - std::unique_ptr sim1 = CreateFullstateSimulator(); - QubitIdType q1 = sim1->AllocateQubit(); - - std::unique_ptr sim2 = CreateFullstateSimulator(); - QubitIdType q2 = sim2->AllocateQubit(); - - REQUIRE(GetQubitId(q1) == 0); - REQUIRE(GetQubitId(q2) == 0); - - sim1->ReleaseQubit(q1); - sim2->ReleaseQubit(q2); -} - -TEST_CASE("Fullstate simulator: X and measure", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - Result r1 = MZ(iqa, q); - REQUIRE(Result_Zero == sim->GetResultValue(r1)); - REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); - - iqa->X(q); - Result r2 = MZ(iqa, q); - REQUIRE(Result_One == sim->GetResultValue(r2)); - REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); - - sim->ReleaseQubit(q); - sim->ReleaseResult(r1); - sim->ReleaseResult(r2); -} - -TEST_CASE("Fullstate simulator: X, M, reuse, M", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - Result r1 = MZ(iqa, q); - REQUIRE(Result_Zero == sim->GetResultValue(r1)); - REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); - - iqa->X(q); - Result r2 = MZ(iqa, q); - REQUIRE(Result_One == sim->GetResultValue(r2)); - REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); - - sim->ReleaseQubit(q); - sim->ReleaseResult(r1); - sim->ReleaseResult(r2); - - QubitIdType qq = sim->AllocateQubit(); - Result r3 = MZ(iqa, qq); - // Allocated qubit should always be in |0> state even though we released - // q in |1> state, and qq is likely reusing the same underlying qubit as q. - REQUIRE(Result_Zero == sim->GetResultValue(r3)); - REQUIRE(sim->AreEqualResults(r3, sim->UseZero())); - - sim->ReleaseQubit(qq); - sim->ReleaseResult(r3); -} - -TEST_CASE("Fullstate simulator: measure Bell state", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q1 = sim->AllocateQubit(); - QubitIdType q2 = sim->AllocateQubit(); - - iqa->H(q1); - iqa->ControlledX(1, &q1, q2); - - Result r1 = MZ(iqa, q1); - Result r2 = MZ(iqa, q2); - REQUIRE(sim->AreEqualResults(r1, r2)); - - sim->ReleaseQubit(q1); - sim->ReleaseQubit(q2); -} - -TEST_CASE("Fullstate simulator: ZZ measure", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q[2]; - PauliId paulis[2] = {PauliId_Z, PauliId_Z}; - - q[0] = sim->AllocateQubit(); - q[1] = sim->AllocateQubit(); - iqa->H(q[0]); - iqa->ControlledX(1, &q[0], q[1]); - Result rZero = iqa->Measure(2, paulis, 2, q); - REQUIRE(Result_Zero == sim->GetResultValue(rZero)); - - iqa->X(q[1]); - Result rOne = iqa->Measure(2, paulis, 2, q); - REQUIRE(Result_One == sim->GetResultValue(rOne)); - - iqa->X(q[1]); - iqa->ControlledX(1, &q[0], q[1]); - iqa->H(q[0]); - - sim->ReleaseQubit(q[0]); - sim->ReleaseQubit(q[1]); -} - -TEST_CASE("Fullstate simulator: assert probability", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType qs[2]; - qs[0] = sim->AllocateQubit(); - qs[1] = sim->AllocateQubit(); - iqa->X(qs[0]); - - PauliId zz[2] = {PauliId_Z, PauliId_Z}; - PauliId iz[2] = {PauliId_I, PauliId_Z}; - PauliId xi[2] = {PauliId_X, PauliId_I}; - - IDiagnostics* idig = dynamic_cast(sim.get()); - REQUIRE(idig->AssertProbability(2, zz, qs, 0.0, 1e-10, "")); - REQUIRE(idig->AssertProbability(2, iz, qs, 1.0, 1e-10, "")); - REQUIRE(idig->AssertProbability(2, xi, qs, 0.5, 1e-10, "")); - - REQUIRE(idig->Assert(2, zz, qs, sim->UseOne(), "")); - REQUIRE(idig->Assert(2, iz, qs, sim->UseZero(), "")); - REQUIRE(!idig->Assert(2, xi, qs, sim->UseZero(), "")); - REQUIRE(!idig->Assert(2, xi, qs, sim->UseOne(), "")); - - iqa->X(qs[0]); - - sim->ReleaseQubit(qs[0]); - sim->ReleaseQubit(qs[1]); -} - -TEST_CASE("Fullstate simulator: toffoli", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType qs[3]; - for (int i = 0; i < 3; i++) - { - qs[i] = sim->AllocateQubit(); - } - - iqa->X(qs[0]); - iqa->ControlledX(2, qs, qs[2]); - REQUIRE(Result_Zero == sim->GetResultValue(MZ(iqa, qs[2]))); - - iqa->X(qs[1]); - iqa->ControlledX(2, qs, qs[2]); - REQUIRE(Result_One == sim->GetResultValue(MZ(iqa, qs[2]))); - - iqa->X(qs[1]); - iqa->X(qs[0]); - - for (int i = 0; i < 3; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -TEST_CASE("Fullstate simulator: SSZ=Id", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - - bool identitySSZ = true; - for (int i = 0; i < 100 && identitySSZ; i++) - { - iqa->H(q); - iqa->S(q); - iqa->S(q); - iqa->Z(q); - iqa->H(q); - identitySSZ = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identitySSZ); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: TTSAdj=Id", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - - bool identityTTSAdj = true; - for (int i = 0; i < 100 && identityTTSAdj; i++) - { - iqa->H(q); - iqa->T(q); - iqa->T(q); - iqa->AdjointS(q); - iqa->H(q); - identityTTSAdj = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identityTTSAdj); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: TTAdj=Id", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - - bool identityTTadj = true; - for (int i = 0; i < 100 && identityTTadj; i++) - { - iqa->H(q); - iqa->T(q); - iqa->AdjointT(q); - iqa->H(q); - identityTTadj = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identityTTadj); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: R", "[fullstate_simulator]") -{ - constexpr double pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062; - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - QubitIdType q = sim->AllocateQubit(); - bool identity = true; - for (int i = 0; i < 100 && identity; i++) - { - iqa->H(q); - iqa->R(PauliId_X, q, 0.42); - iqa->R(PauliId_Y, q, 0.17); - iqa->T(q); - iqa->R(PauliId_Z, q, -pi / 4.0); - iqa->R(PauliId_Y, q, -0.17); - iqa->R(PauliId_X, q, -0.42); - iqa->H(q); - identity = (Result_Zero == sim->GetResultValue(MZ(iqa, q))); - } - REQUIRE(identity); - - sim->ReleaseQubit(q); -} - -TEST_CASE("Fullstate simulator: exponents", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - const int n = 5; - - QubitIdType qs[n]; - for (int i = 0; i < n; i++) - { - qs[i] = sim->AllocateQubit(); - } - - PauliId paulis[3] = {PauliId_X, PauliId_Y, PauliId_Z}; - iqa->Exp(2, paulis, qs, 0.42); - iqa->ControlledExp(2, qs, 3, paulis, &qs[2], 0.17); - iqa->ControlledExp(2, qs, 3, paulis, &qs[2], -0.17); - iqa->Exp(2, paulis, qs, -0.42); - - // not crashes? consider it passing - REQUIRE(true); - - for (int i = 0; i < n; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -TEST_CASE("Fullstate simulator: get qubit state of Bell state", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - IQuantumGateSet* iqa = dynamic_cast(sim.get()); - - const int n = 3; - static double norm = 0.0; - - QubitIdType qs[n]; - for (int i = 0; i < n; i++) - { - qs[i] = sim->AllocateQubit(); - } - - iqa->H(qs[0]); - iqa->ControlledX(1, &qs[0], qs[1]); - // 1/sqrt(2)(|00> + |11>)x|0> - - dynamic_cast(sim.get())->GetState( - [](const char* idxStr, double re, double im) - { - norm += re * re + im * im; - size_t idx = 0; - for (size_t i = 0; idxStr[i] != '\0'; ++i) - { - idx |= (idxStr[i] == '1' ? 1u : 0u) << i; - } - REQUIRE(idx < 4); - switch (idx) - { - case 0: - case 3: - REQUIRE((1 / sqrt(2.0) == Approx(re).epsilon(0.0001))); - REQUIRE(im == 0.0); - break; - default: - REQUIRE(re == 0.0); - REQUIRE(im == 0.0); - break; - } - return idx < 3; // the last qubit is in separable |0> state - }); - REQUIRE(1.0 == Approx(norm).epsilon(0.0001)); - norm = 0.0; - - iqa->Y(qs[2]); - // 1/sqrt(2)(|00> + |11>)xi|1> - - dynamic_cast(sim.get())->GetState( - [](const char* idxStr, double re, double im) - { - norm += re * re + im * im; - size_t idx = 0; - for (size_t i = 0; idxStr[i] != '\0'; ++i) - { - idx |= (idxStr[i] == '1' ? 1u : 0u) << i; - } - switch (idx) - { - case 4: - case 7: - REQUIRE(re == 0.0); - REQUIRE(1 / sqrt(2.0) == Approx(im).epsilon(0.0001)); - break; - default: - REQUIRE(re == 0.0); - REQUIRE(im == 0.0); - break; - } - return true; // get full state - }); - REQUIRE(1.0 == Approx(norm).epsilon(0.0001)); - norm = 0.0; - - iqa->Y(qs[2]); - iqa->ControlledX(1, &qs[0], qs[1]); - iqa->H(qs[0]); - - for (int i = 0; i < n; i++) - { - sim->ReleaseQubit(qs[i]); - } -} - -extern "C" int Microsoft__Quantum__Testing__QIR__InvalidRelease__Interop(); // NOLINT -TEST_CASE("QIR: Simulator rejects unmeasured, non-zero release", "[fullstate_simulator]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); - REQUIRE_THROWS(Microsoft__Quantum__Testing__QIR__InvalidRelease__Interop()); -} -#endif - extern "C" int Microsoft__Quantum__Testing__QIR__MeasureRelease__Interop(); // NOLINT TEST_CASE("QIR: Simulator accepts measured release", "[fullstate_simulator]") { -#ifndef RIQIR_TESTING - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); -#endif - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__MeasureRelease__Interop()); } -extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(bool); // NOLINT +extern "C" int Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(); // NOLINT TEST_CASE("QIR: invoke all standard Q# gates against the fullstate simulator", "[fullstate_simulator]") { -#ifndef RIQIR_TESTING - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); - bool includeExp = true; -#else - bool includeExp = false; -#endif - - REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop(includeExp)); + REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Test_Simulator_QIS__Interop()); } diff --git a/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs index 081ec83dbe7..95060828d74 100644 --- a/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs +++ b/src/Qir/Tests/FullstateSimulator/qsharp/qir-test-simulator.qs @@ -33,7 +33,7 @@ namespace Microsoft.Quantum.Testing.QIR } @EntryPoint() - operation Test_Simulator_QIS(includeExp : Bool) : Int + operation Test_Simulator_QIS() : Int { mutable res = 0; set res = InvokeAllVariants(X); @@ -57,25 +57,23 @@ namespace Microsoft.Quantum.Testing.QIR set res = InvokeAllVariants(R(PauliX, 0.42, _)); if (res != 0) { return 60 + res; } - if includeExp { - use (targets, ctls) = (Qubit[2], Qubit[2]) - { - let theta = 0.42; - Exp([PauliX, PauliY], theta, targets); - Adjoint Exp([PauliX, PauliY], theta, targets); - if (M(targets[0]) != Zero) { set res = 1; } + use (targets, ctls) = (Qubit[2], Qubit[2]) + { + let theta = 0.42; + Exp([PauliX, PauliY], theta, targets); + Adjoint Exp([PauliX, PauliY], theta, targets); + if (M(targets[0]) != Zero) { set res = 1; } - H(ctls[0]); - H(ctls[1]); - Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); - Adjoint Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); - H(ctls[0]); - H(ctls[1]); - if (M(targets[0]) != Zero) { set res = 2; } - ResetAll(targets + ctls); - } - if (res != 0) { return 70 + res; } + H(ctls[0]); + H(ctls[1]); + Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); + Adjoint Controlled Exp(ctls, ([PauliX, PauliY], theta, targets)); + H(ctls[0]); + H(ctls[1]); + if (M(targets[0]) != Zero) { set res = 2; } + ResetAll(targets + ctls); } + if (res != 0) { return 70 + res; } use qs = Qubit[3] { diff --git a/src/Qir/Tests/QIR-dynamic/CMakeLists.txt b/src/Qir/Tests/QIR-dynamic/CMakeLists.txt index 0a205dd9f58..da7bb8245bd 100644 --- a/src/Qir/Tests/QIR-dynamic/CMakeLists.txt +++ b/src/Qir/Tests/QIR-dynamic/CMakeLists.txt @@ -1,29 +1,22 @@ -set(TEST_FILES - qsharp/obj/qsharp/qir-test-random.bc -) +set(TEST_FILES qsharp/obj/qsharp/qir-test-random.bc) #============================================================================== # This executable target links test code against the dynamic libraries rather than the explicit # static QIR/RT libs (qir will statically link in the bridge via transitivity of target_link_libraries). # -add_executable(qir-dynamic-tests - qir-driver.cpp -) +add_executable(qir-dynamic-tests qir-driver.cpp) foreach(file ${TEST_FILES}) target_source_from_qir(qir-dynamic-tests ${file}) endforeach() target_link_libraries(qir-dynamic-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(qir-dynamic-tests PUBLIC ${test_includes} - ${public_includes} ) install(TARGETS qir-dynamic-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") diff --git a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp index 210bee0dcf2..557c7647404 100644 --- a/src/Qir/Tests/QIR-dynamic/qir-driver.cpp +++ b/src/Qir/Tests/QIR-dynamic/qir-driver.cpp @@ -9,10 +9,6 @@ #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" -#include "SimFactory.hpp" -#include "QirContext.hpp" -#include "OutputStream.hpp" - extern "C" { int64_t Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); // NOLINT @@ -38,15 +34,8 @@ extern "C" void Microsoft__Quantum__Testing__QIR__AssertMeasProbMessageTest__Interop(const char*); // NOLINT } // extern "C" -using namespace Microsoft::Quantum; - TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") { -#ifndef RIQIR_TESTING - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; -#endif - const int64_t ret1 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); const int64_t ret2 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); const int64_t ret3 = Microsoft__Quantum__Testing__QIR__QuantumRandomNumberGenerator__Interop(); @@ -59,148 +48,8 @@ TEST_CASE("QIR: Generate a random number with full state simulator", "[qir]") CHECK(ret2 != ret3); } -#ifndef RIQIR_TESTING -static bool FileExists(const char* filePath) -{ - return std::ifstream(filePath).operator bool(); -} - -TEST_CASE("QIR: DumpMachine", "[qir][DumpMachine]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // Dump to the std::cout: - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpMachineTest__Interop(); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to empty string location (std::cout): - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__Interop(""); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to a file: - const char* filePath = "DumpMachineTest.log"; - - // Remove the `filePath`, if exists. - if (FileExists(filePath)) - { - CHECK(0 == remove(filePath)); - } - - REQUIRE(!FileExists(filePath)); - - // Dump the machine state to that `filePath`: - Microsoft__Quantum__Testing__QIR__DumpMachineToFileTest__Interop(filePath); - - // Make sure the file has been created. - REQUIRE(FileExists(filePath)); - - // If we got here then the test has succeeded, we don't need the file. - // Otherwise (test fails) we don't get here, and the file is kept for the subsequent analysis. - // Remove the file, ignore the failure: - (void)remove(filePath); -} - - -TEST_CASE("QIR: DumpRegister", "[qir][DumpRegister]") -{ - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; - - // Dump to the std::cout: - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpRegisterTest__Interop(); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to empty string location (std::cout): - { - std::ostringstream outStrStream; - { - // Redirect the output from std::cout to outStrStream: - OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__Interop(""); - } // qOStreamRedirector goes out of scope. - - REQUIRE(outStrStream.str().size() != 0); - } - - - // Dump to a file: - const char* filePath = "DumpRegisterTest.log"; - - // Remove the `filePath` if exists. - if (FileExists(filePath)) - { - CHECK(0 == remove(filePath)); - } - - REQUIRE(!FileExists(filePath)); - - // Dump to that `filePath`: - Microsoft__Quantum__Testing__QIR__DumpRegisterToFileTest__Interop(filePath); - - // Make sure the file has been created. - REQUIRE(FileExists(filePath)); - - // If we got here then the test has succeeded, we don't need the file. - // Otherwise (test fails) we don't get here, and the file is kept for the subsequent analysis. - // Remove the file, ignore the failure: - (void)remove(filePath); -} - -static void AssertMeasMessageTest(void (*funcPtr)(const char*)) -{ - const char* const testStr = "Testing the Assertion Failure Message"; - std::ostringstream outStrStream; - - // Redirect the output from std::cout to outStrStream: - Microsoft::Quantum::OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - // Log something (to the redirected output): - REQUIRE_THROWS(funcPtr(testStr)); // Returns with exception caught. Leaks any instances allocated (in .ll) - // from the moment of a call to the moment of the exception throw. - // TODO: Extract into a separate .cpp compiled with leak detection off. - REQUIRE(outStrStream.str() == (std::string(testStr) + "\n")); -} -#endif - TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") { -#ifndef RIQIR_TESTING - std::unique_ptr sim = CreateFullstateSimulator(); - QirExecutionContext::Scoped contextReleaser{sim.get()}; -#endif - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasAlloc1OKTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasProbAlloc1HalfProbTest__Interop()); @@ -211,10 +60,4 @@ TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertBellPairMeasurementsAreCorrectTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertMeasMixedBasesTest__Interop()); REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__AssertGHZMeasurementsTest__Interop()); - -#ifndef RIQIR_TESTING - AssertMeasMessageTest(Microsoft__Quantum__Testing__QIR__AssertMeasMessageTest__Interop); - AssertMeasMessageTest(Microsoft__Quantum__Testing__QIR__AssertMeasProbMessageTest__Interop); -#endif - } // TEST_CASE("QIR: AssertMeasurement", "[qir][AssertMeasurement]") diff --git a/src/Qir/Tests/QIR-static/CMakeLists.txt b/src/Qir/Tests/QIR-static/CMakeLists.txt index 6d38a5a1cce..0bf9fa112e0 100644 --- a/src/Qir/Tests/QIR-static/CMakeLists.txt +++ b/src/Qir/Tests/QIR-static/CMakeLists.txt @@ -1,5 +1,4 @@ set(TEST_FILES - qir-test-noqsharp.ll qsharp/qir/qir-gen.ll ) @@ -10,7 +9,6 @@ add_executable(qir-static-tests qir-driver.cpp qir-test-math.cpp qir-test-strings.cpp - qir-test-ouput.cpp qir-test-other.cpp ) @@ -19,18 +17,14 @@ foreach(file ${TEST_FILES}) endforeach() target_link_libraries(qir-static-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.QSharp.Foundation - -lMicrosoft.Quantum.Qir.QSharp.Core + "-L${simulator_lib_path}" + -lMicrosoft.Quantum.Simulator.Runtime ) target_include_directories(qir-static-tests PUBLIC ${test_includes} - ${common_includes} ${public_includes} ) -# target_compile_definitions(qir-static-tests PRIVATE EXPORT_QIR_API) install(TARGETS qir-static-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") add_unit_test(qir-static-tests) diff --git a/src/Qir/Common/Include/FloatUtils.hpp b/src/Qir/Tests/QIR-static/FloatUtils.hpp similarity index 100% rename from src/Qir/Common/Include/FloatUtils.hpp rename to src/Qir/Tests/QIR-static/FloatUtils.hpp diff --git a/src/Qir/Tests/QIR-static/qir-driver.cpp b/src/Qir/Tests/QIR-static/qir-driver.cpp index 16dc58ccdf0..d7b57cb151c 100644 --- a/src/Qir/Tests/QIR-static/qir-driver.cpp +++ b/src/Qir/Tests/QIR-static/qir-driver.cpp @@ -9,26 +9,17 @@ #include #include -#include "CoreTypes.hpp" -#include "QirContext.hpp" -#include "QirTypes.hpp" -#include "QirRuntime.hpp" -#include "QirRuntimeApi_I.hpp" -#include "SimFactory.hpp" -#include "SimulatorStub.hpp" - #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" +#include "qir_stdlib.h" + +class QUBIT; + // Identifiers exposed externally: extern "C" void __quantum__qis__k__body(QUBIT* q); // NOLINT extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q); // NOLINT -// Used by a couple test simulators. Catch's REQUIRE macro doesn't deal well with static class members so making it -// into a global constant. -constexpr int RELEASED = -1; - -using namespace Microsoft::Quantum; using namespace std; /* @@ -57,9 +48,6 @@ extern "C" int64_t Microsoft__Quantum__Testing__QIR__Test_Arrays__Interop( // NO Array* array, int64_t index, int64_t val); TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") { -#ifndef RIQIR_TESTING - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); -#endif constexpr int64_t n = 5; int64_t values[n] = {0, 1, 2, 3, 4}; auto array = Array{n, values}; @@ -69,266 +57,21 @@ TEST_CASE("QIR: Using 1D arrays", "[qir][qir.arr1d]") } extern "C" void Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__Interop(); // NOLINT -struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - // no intelligent reuse, we just want to check that QIR releases all qubits - vector qubits; // released, or |0>, or |1> states (no entanglement allowed) - vector results = {0, 1}; // released, or Zero(0) or One(1) - - uint64_t GetQubitId(QubitIdType qubit) const - { - const uint64_t id = (uint64_t)qubit; - REQUIRE(id < this->qubits.size()); - - return id; - } - - uint8_t GetResultId(Result result) const - { - const uint8_t id = (uint8_t)(uintptr_t)result; - REQUIRE(id < this->results.size()); - - return id; - } - - QubitIdType AllocateQubit() override - { - qubits.push_back(0); - return static_cast(this->qubits.size() - 1); - } - - void ReleaseQubit(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // no double-release - this->qubits[id] = RELEASED; - } - - void X(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->qubits[id] = 1 - this->qubits[id]; - } - - Result Measure([[maybe_unused]] long numBases, PauliId* /* bases */, long /* numTargets */, - QubitIdType targets[]) override - { - assert(numBases == 1 && "QubitsResultsTestSimulator doesn't support joint measurements"); - - const uint64_t id = GetQubitId(targets[0]); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->results.push_back(this->qubits[id]); - return reinterpret_cast(this->results.size() - 1); - } - - bool AreEqualResults(Result r1, Result r2) override - { - uint8_t i1 = GetResultId(r1); - uint8_t i2 = GetResultId(r2); - REQUIRE(this->results[i1] != RELEASED); - REQUIRE(this->results[i2] != RELEASED); - - return (results[i1] == results[i2]); - } - - void ReleaseResult(Result result) override - { - uint8_t i = GetResultId(result); - REQUIRE(this->results[i] != RELEASED); // no double release - this->results[i] = RELEASED; - } - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; TEST_CASE("QIR: allocating and releasing qubits and results", "[qir][qir.qubit][qir.result]") { -#ifndef RIQIR_TESTING - unique_ptr sim = make_unique(); - QirExecutionContext::Scoped qirctx(sim.get(), true /*trackAllocatedObjects*/); -#endif - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__QIR__TestQubitResultManagement__Interop()); - -#ifndef RIQIR_TESTING - // check that all qubits have been released - for (size_t id = 0; id < sim->qubits.size(); id++) - { - INFO(std::string("unreleased qubit: ") + std::to_string(id)); - CHECK(sim->qubits[id] == RELEASED); - } -#endif - - // check that all results, allocated by measurements have been released - // TODO: enable after https://github.com/microsoft/qsharp-compiler/issues/780 is fixed - // for (size_t id = 2; id < sim->results.size(); id++) - // { - // INFO(std::string("unreleased results: ") + std::to_string(id)); - // CHECK(sim->results[id] == RELEASED); - // } -} - -#ifndef RIQIR_TESTING - -#ifdef _WIN32 -// A non-sensical function that creates a 3D array with given dimensions, then projects on the index = 1 of the -// second dimension and returns a function of the sizes of the dimensions of the projection and a the provided value, -// that is written to the original array at [1,1,1] and then retrieved from [1,1]. -// Thus, all three dimensions must be at least 2. -extern "C" int64_t TestMultidimArrays(char value, int64_t dim0, int64_t dim1, int64_t dim2); -TEST_CASE("QIR: multidimensional arrays", "[qir][qir.arrMultid]") -{ -#ifndef RIQIR_TESTING - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); -#endif - - REQUIRE(42 + (2 + 8) / 2 == TestMultidimArrays(42, 2, 4, 8)); - REQUIRE(17 + (3 + 7) / 2 == TestMultidimArrays(17, 3, 5, 7)); -} -#else // not _WIN32 -// TODO: The bridge for variadic functions is broken on Linux! -#endif - -// Manually authored QIR to test dumping range [0..2..6] into string and then raising a failure with it -extern "C" void TestFailWithRangeString(int64_t start, int64_t step, int64_t end); -TEST_CASE("QIR: Report range in a failure message", "[qir][qir.range]") -{ -#ifndef RIQIR_TESTING - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); -#endif - - bool failed = false; - try - { - TestFailWithRangeString(0, 5, 42); // Returns with exception. Leaks the instances created from the moment of - // call to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - } - catch (const std::exception& e) - { - failed = true; - REQUIRE(std::string(e.what()) == "0..5..42"); - } - REQUIRE(failed); } -#endif // RIQIR_TESTING - // TestPartials subtracts the second argument from the first and returns the result. extern "C" int64_t Microsoft__Quantum__Testing__QIR__TestPartials__Interop(int64_t, int64_t); // NOLINT TEST_CASE("QIR: Partial application of a callable", "[qir][qir.partCallable]") { -#ifndef RIQIR_TESTING - QirExecutionContext::Scoped qirctx(nullptr, true /*trackAllocatedObjects*/); -#endif - const int64_t res = Microsoft__Quantum__Testing__QIR__TestPartials__Interop(42, 17); REQUIRE(res == 42 - 17); } // The Microsoft__Quantum__Testing__QIR__TestFunctors__Interop tests needs proper semantics of X and M, and nothing // else. The validation is done inside the test and it would throw in case of failure. -#ifndef RIQIR_TESTING -struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub -{ - std::vector qubits; - - uint64_t GetQubitId(QubitIdType qubit) const - { - const uint64_t id = (uint64_t)qubit; - REQUIRE(id < this->qubits.size()); - return id; - } - - QubitIdType AllocateQubit() override - { - this->qubits.push_back(0); - return static_cast(this->qubits.size() - 1); - } - - void ReleaseQubit(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); - this->qubits[id] = RELEASED; - } - - void X(QubitIdType qubit) override - { - const uint64_t id = GetQubitId(qubit); - REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive - this->qubits[id] = 1 - this->qubits[id]; - } - - void ControlledX(long numControls, QubitIdType controls[], QubitIdType qubit) override - { - for (long i = 0; i < numControls; i++) - { - const uint64_t id = GetQubitId(controls[i]); - REQUIRE(this->qubits[id] != RELEASED); - if (this->qubits[id] == 0) - { - return; - } - } - X(qubit); - } - - Result Measure([[maybe_unused]] long numBases, PauliId* /* bases */, long /* numTargets */, - QubitIdType targets[]) override - { - assert(numBases == 1 && "FunctorsTestSimulator doesn't support joint measurements"); - - const uint64_t id = GetQubitId(targets[0]); - REQUIRE(this->qubits[id] != RELEASED); - return reinterpret_cast(this->qubits[id]); - } - - bool AreEqualResults(Result r1, Result r2) override - { - // those are bogus pointers but it's ok to compare them _as pointers_ - return (r1 == r2); - } - - void ReleaseResult(Result /*result*/) override - { - } // the results aren't allocated by this test simulator - - Result UseZero() override - { - return reinterpret_cast(0); - } - - Result UseOne() override - { - return reinterpret_cast(1); - } -}; -static FunctorsTestSimulator* g_ctrqapi = nullptr; -static int g_cKCalls = 0; -static int g_cKCallsControlled = 0; -extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__Interop(); // NOLINT -extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__Interop(); // NOLINT -extern "C" void __quantum__qis__k__body(QUBIT* q) // NOLINT -{ - g_cKCalls++; - g_ctrqapi->X(reinterpret_cast(q)); -} -extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q) // NOLINT -{ - g_cKCallsControlled++; - g_ctrqapi->ControlledX((long)(controls->count), reinterpret_cast(controls->buffer), - reinterpret_cast(q)); -} -#else extern "C" void __quantum__qis__x__body(QUBIT* q); // NOLINT extern "C" void __quantum__qis__x__ctl(QirArray* controls, QUBIT* q); // NOLINT extern "C" void Microsoft__Quantum__Testing__QIR__TestFunctors__Interop(); // NOLINT @@ -341,25 +84,9 @@ extern "C" void __quantum__qis__k__ctl(QirArray* controls, QUBIT* q) // NOLINT { __quantum__qis__x__ctl(controls, q); } -#endif + TEST_CASE("QIR: application of nested controlled functor", "[qir][qir.functor]") { -#ifndef RIQIR_TESTING - unique_ptr qapi = make_unique(); - QirExecutionContext::Scoped qirctx(qapi.get(), true /*trackAllocatedObjects*/); - g_ctrqapi = qapi.get(); -#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctors__Interop()); - -#ifndef RIQIR_TESTING - const int cKCalls = g_cKCalls; - const int cKCallsControlled = g_cKCallsControlled; -#endif CHECK_NOTHROW(Microsoft__Quantum__Testing__QIR__TestFunctorsNoArgs__Interop()); -#ifndef RIQIR_TESTING - CHECK(g_cKCalls - cKCalls == 3); - CHECK(g_cKCallsControlled - cKCallsControlled == 5); - - g_ctrqapi = nullptr; -#endif } diff --git a/src/Qir/Tests/QIR-static/qir-test-math.cpp b/src/Qir/Tests/QIR-static/qir-test-math.cpp index 9f30099fe99..d9df0d68c83 100644 --- a/src/Qir/Tests/QIR-static/qir-test-math.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-math.cpp @@ -6,7 +6,6 @@ #include "catch.hpp" -#include "qsharp__foundation_internal.hpp" #include "FloatUtils.hpp" extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Math__SqrtTest__Interop(); // NOLINT @@ -106,170 +105,9 @@ TEST_CASE("QIR: Math.Exponent.builtin", "[qir.math][qir.Math.Exponent.builtin]") TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") { -#ifndef RIQIR_TESTING - // Test that the Q# random number generator is a wrapper around the C++ generator: - size_t times = 1000; - while (--times) - { - const int64_t qsRndNum = Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop( - std::numeric_limits::min(), std::numeric_limits::max()); - const int64_t cppRndNum = Quantum::Qis::Internal:: - GetLastGeneratedRandomI64(); // This call must be done - // _after_ the - // Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(). - REQUIRE(qsRndNum == cppRndNum); - } - - // Make sure the correct exception is thrown if min > max: - REQUIRE_THROWS_AS(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(10, 5), std::runtime_error); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - - // Check the exception string: - try - { - (void)Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(10, 5); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - } - catch (std::runtime_error const& exc) - { - REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); - } -#endif - // Test equal minimum and maximum: for (int64_t num : {-5, 0, 3}) { REQUIRE(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(num, num) == num); } - - // clang-format off - // There is a strong difference in the opinions about how the random number generator must be tested. - // More or less agreed-upon items are: - // * The test must be 100% deterministic, i.e. must not fail, even with a very low probability. - // Otherwise it will bother the unrelated CI builds/test-runs, that are done a lot. - // * The test must not be platform-, compiler-, etc. dependent. - // Otherwise it can break upon migration to a newer compiler, OS update, new OS added to the test runs, etc. - // - // The code below is platform-dependent, can also depend on the compiler version. - // Commenting out for now. - - // #ifdef __APPLE__ - // std::vector expectedLargeNumbers( { - // -2160833387943730151 /*0xe2032d7b74cf6419*/, 7375078072468444798 /*0x66598a6e9c41167e*/, 7708428399011769513 /*0x6af9d6c9b3a12ca9*/, - // -8929332642100591101 /*0x8414a3458a4b4603*/, 9131959130339861073 /*0x7ebb3c72234ae251*/, 2129461186021157660 /*0x1d8d5daa93c5eb1c*/, - // -4466415676527644493 /*0xc2041a9b355570b3*/, 2654403080104352464 /*0x24d65589a8529ad0*/, 3948910203829515833 /*0x36cd58e87d2c7639*/, - // 3600951923571138577 /*0x31f926b221b7a011*/, -7454003569285620820 /*0x988e0f3f2a3c9bac*/, -2896776822558058671 /*0xd7cc94d3e1d79751*/, - // -2510694579170103717 /*0xdd2838951d112e5b*/, -8679035075952589054 /*0x878ddf94f8b0c702*/, -8480296875123573728 /*0x8a4feeec30677c20*/, - - // -8613430109842542716 /*0x8876f2f3752e6f84*/, 2140032717197149199 /*0x1db2ec6afc40040f*/, -917262003397267527 /*0xf3453b11598aefb9*/, - // -3734430349428794203 /*0xcc2ca3620fdbdca5*/, 5134567830016493736 /*0x4741a63cbf4808a8*/, -8243723698983337761 /*0x8d9868f90fa100df*/, - // 5560736588152128922 /*0x4d2bb4770253459a*/, 50526560201835791 /* 0xb381a3888d850f*/, 1288735234894005209 /*0x11e281de3d6303d9*/, - // 3656101241126025060 /*0x32bd14b53c2e7764*/, 872395409727236160 /* 0xc1b5f00c4792840*/, 7628415731883617240 /*0x69dd93b0e9ecabd8*/, - // -1986081594003691539 /*0xe47005501e77e7ed*/, 7532118334194327900 /*0x688775b7d3dc215c*/, -4186893097968929306 /*0xc5e52ae91706f5e6*/ - // } ); - // std::vector expectedSmallNumbers( { 1, 4, 2, 4, 5, -2, -4, 9, 4, -4, -9, 9, -1, 5, 7, - // -8, 0, 2, 5, 0, -1, 1, 9, -3, 5, -8, -9, 6, -1, 6 } ); - // #else - // std::vector expectedLargeNumbers( { - // -5906760355100746824 /*0xae06f8c09cdc1bb8*/, -5720189720460620649 /*0xb09dcdc1901a8c97*/, -439612500227010677 /*0xf9e62ec29d25cf8b*/, - // -4480907261563067469 /*0xc1d09e962310a3b3*/, 8861952245290091527 /*0x7afbfa9d4cfdc407*/, 8955350353842143311 /*0x7c47cbb307ee004f*/, - // -6280323296958344769 /*0xa8d7cf3c6a3011bf*/, 3137151747734999458 /*0x2b8966e4aa3d91a2*/, 4939508655077151009 /*0x448ca8f37ed75121*/, - // 6238374286314258160 /*0x5693285c6fb13ef0*/, -6040247118112373857 /*0xac2cbb3fa955c39f*/, -6824740380414679031 /*0xa149a6c8751e6809*/, - // -3380739839894412592 /*0xd11533070d1522d0*/, 7062538648911045657 /*0x62032d7b74cf6419*/, -1848293964386331010 /*0xe6598a6e9c41167e*/, - - // -1514943637843006295 /*0xeaf9d6c9b3a12ca9*/, 294039394754184707 /* 0x414a3458a4b4603*/, -91412906514914735 /*0xfebb3c72234ae251*/, - // -7093910850833618148 /*0x9d8d5daa93c5eb1c*/, 4756956360327131315 /*0x42041a9b355570b3*/, -6568968956750423344 /*0xa4d65589a8529ad0*/, - // -5274461833025259975 /*0xb6cd58e87d2c7639*/, -5622420113283637231 /*0xb1f926b221b7a011*/, 1769368467569154988 /*0x188e0f3f2a3c9bac*/, - // 6326595214296717137 /*0x57cc94d3e1d79751*/, 6712677457684672091 /*0x5d2838951d112e5b*/, 544336960902186754 /* 0x78ddf94f8b0c702*/, - // 743075161731202080 /* 0xa4feeec30677c20*/, 609941927012233092 /* 0x876f2f3752e6f84*/, -7083339319657626609 /*0x9db2ec6afc40040f*/ - // } ); - // #ifdef _WIN32 - // std::vector expectedSmallNumbers( { -7, 7, 0, -4, 9, -8, -6, 2, 10, -2, -2, 5, -6, 7, -6, - // -8, -7, 7, 1, 0, -7, -4, -4, -5, 9, 6, 9, 8, 0, 10 } ); - // #else - // std::vector expectedSmallNumbers( { -7, 10, -10, 2, 1, -9, 3, -2, 7, 9, -2, 3, 9, -3, -5, - // -1, 6, 4, 1, -4, -7, 2, 1, -7, -7, -10, 1, 4, -2, 9 } ); - // #endif // #ifdef _WIN32 - // #endif // #ifdef __APPLE__ - - // // Use const seed (and 100%-predictable sequence of pseudo-random numbers): - // Quantum::Qis::Internal::RandomizeSeed(false); - - // size_t times = 30; - // std::vector actualNumbers; - // // Get the actual pseudo-random numbers: - // actualNumbers.reserve(times); - // while (times--) - // { - // actualNumbers.emplace_back(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(-10, 10)); - // } - - // // Compare the actual numbers with the expected ones: - // for (auto iterExp = expectedSmallNumbers.begin(), iterAct = actualNumbers.begin(); - // iterExp != expectedSmallNumbers.end(); ++iterExp, ++iterAct) - // { - // REQUIRE(*iterExp == *iterAct); - // } - - - // // Repeat for large numbers: - // times = 30; - // actualNumbers.clear(); - // while (times--) - // { - // actualNumbers.emplace_back( - // Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomInt__Interop(std::numeric_limits::min(), - // std::numeric_limits::max())); - // } - - // for (auto iterExp = expectedLargeNumbers.begin(), iterAct = actualNumbers.begin(); - // iterExp != expectedLargeNumbers.end(); ++iterExp, ++iterAct) - // { - // REQUIRE(*iterExp == *iterAct); - // } - - // clang-format on - } // TEST_CASE("QIR: Math.DrawRandomInt", "[qir.math][qir.Math.DrawRandomInt]") - -#ifndef RIQIR_TESTING -TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") -{ - // Test that the Q# random number generator is a wrapper around the C++ generator: - size_t times = 1000; - while (--times) - { - const double qsRndNum = Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop( - std::numeric_limits::min(), std::numeric_limits::max()); - const double cppRndNum = Quantum::Qis::Internal:: - GetLastGeneratedRandomDouble(); // This call must be done - // _after_ the - // Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop(). - REQUIRE(Close(qsRndNum, cppRndNum)); - } - - // Make sure the correct exception is thrown if min > max: - REQUIRE_THROWS_AS(Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop(10.0, 5.0), - std::runtime_error); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - - // Check the exception string: - try - { - (void)Microsoft__Quantum__Testing__QIR__Math__TestDrawRandomDouble__Interop(10.0, 5.0); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. - } - catch (std::runtime_error const& exc) - { - REQUIRE(0 == strcmp(exc.what(), Quantum::Qis::Internal::excStrDrawRandomVal)); - } -} // TEST_CASE("QIR: Math.DrawRandomDouble", "[qir.math][qir.Math.DrawRandomDouble]") -#endif diff --git a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll b/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll deleted file mode 100644 index a2b465ea7bd..00000000000 --- a/src/Qir/Tests/QIR-static/qir-test-noqsharp.ll +++ /dev/null @@ -1,48 +0,0 @@ -%Range = type { i64, i64, i64 } -%Array = type opaque -%String = type opaque - -@EmptyRange = internal constant %Range { i64 0, i64 1, i64 -1 } -declare %Array* @__quantum__rt__array_create(i32, i32, ...) -declare i64 @__quantum__rt__array_get_size(%Array*, i32) -declare i8* @__quantum__rt__array_get_element_ptr(%Array*, ...) -declare i32 @__quantum__rt__array_get_dim(%Array*) -declare %Array* @__quantum__rt__array_project(%Array*, i32, i64) -declare void @__quantum__rt__array_update_reference_count(%Array*, i32) - -; manually authored test for multi-dimensional arrays (Q# doesn't support multi-dimentional arrays yet) -define i64 @TestMultidimArrays(i8 %val, i64 %dim0, i64 %dim1, i64 %dim2) -{ - %.ar = call %Array* (i32, i32, ...) @__quantum__rt__array_create(i32 1, i32 3, i64 %dim0, i64 %dim1, i64 %dim2) - %elem_ptr = call i8* (%Array*, ...) @__quantum__rt__array_get_element_ptr(%Array* %.ar, i64 1, i64 1, i64 1) - store i8 %val, i8* %elem_ptr - %.project = call %Array* @__quantum__rt__array_project(%Array* %.ar, i32 1, i64 1) - %project_dims = call i32 @__quantum__rt__array_get_dim(%Array* %.project) - %project_dim0 = call i64 @__quantum__rt__array_get_size(%Array* %.project, i32 0) - %project_dim1 = call i64 @__quantum__rt__array_get_size(%Array* %.project, i32 1) - %project_elem_ptr = call i8* (%Array*, ...) @__quantum__rt__array_get_element_ptr(%Array* %.project, i64 1, i64 1) - %project_val = load i8, i8* %project_elem_ptr - %val64 = sext i8 %project_val to i64 - - call void @__quantum__rt__array_update_reference_count(%Array* %.ar, i32 -1) - call void @__quantum__rt__array_update_reference_count(%Array* %.project, i32 -1) - - %t1 = add i64 %project_dim0, %project_dim1 - %t2 = sext i32 %project_dims to i64 - %av = udiv i64 %t1, %t2 - %t3 = add i64 %av, %val64 - ret i64 %t3 -} - -; manually authored test for dumping range into a string and raising a failure with it -declare %String* @__quantum__rt__range_to_string(%Range) -declare void @__quantum__rt__fail(%String*) -define void @TestFailWithRangeString(i64 %start, i64 %step, i64 %end){ - %re = load %Range, %Range* @EmptyRange - %r0 = insertvalue %Range %re, i64 %start, 0 - %r1 = insertvalue %Range %r0, i64 %step, 1 - %r2 = insertvalue %Range %r1, i64 %end, 2 - %str = call %String* @__quantum__rt__range_to_string(%Range %r2) - call void @__quantum__rt__fail(%String* %str) ; Leaks the `%str`. TODO: Extract into a separate file compiled with leak check off. - ret void -} diff --git a/src/Qir/Tests/QIR-static/qir-test-other.cpp b/src/Qir/Tests/QIR-static/qir-test-other.cpp index 406accf2435..9454f18ef5e 100644 --- a/src/Qir/Tests/QIR-static/qir-test-other.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-other.cpp @@ -3,26 +3,14 @@ #include "catch.hpp" -extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop(); // NOLINT -extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop(); // NOLINT -extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntFailTest__Interop(); // NOLINT - -#ifndef RIQIR_TESTING -TEST_CASE("QIR: Other.PauliArrayAsIntFail", "[qir.Other][qir.Other.PauliArrayAsIntFail]") -{ - REQUIRE_THROWS(Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntFailTest__Interop()); - // Returns with exception. Leaks the instances created from the moment of call - // to the moment of exception throw. - // TODO: Extract into a separate file compiled with leaks check off. -} -#endif +extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop(); // NOLINT +extern "C" uint64_t Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop(); // NOLINT TEST_CASE("QIR: Other.PauliArrayAsInt", "[qir.Other][qir.Other.PauliArrayAsInt]") { REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Other__PauliArrayAsIntTest__Interop()); } - TEST_CASE("QIR: Other.Parity", "[qir.Other][qir.Other.Parity]") { REQUIRE(0 == Microsoft__Quantum__Testing__QIR__Other__ParityTest__Interop()); diff --git a/src/Qir/Tests/QIR-static/qir-test-ouput.cpp b/src/Qir/Tests/QIR-static/qir-test-ouput.cpp deleted file mode 100644 index ece08cb50f2..00000000000 --- a/src/Qir/Tests/QIR-static/qir-test-ouput.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include - -#include "catch.hpp" - -#include "OutputStream.hpp" - -extern "C" void Microsoft__Quantum__Testing__QIR__Out__MessageTest__Interop(const char[]); // NOLINT - -TEST_CASE("QIR: Out.Message", "[qir.Out][qir.Out.Message]") -{ - const std::string testStr1 = "Test String 1"; - const std::string testStr2 = "Test String 2"; - - std::ostringstream outStrStream; - - { - // Redirect the output from std::cout to outStrStream: - Microsoft::Quantum::OutputStream::ScopedRedirector qOStreamRedirector(outStrStream); - - // Log something (to the redirected output): - Microsoft__Quantum__Testing__QIR__Out__MessageTest__Interop(testStr1.c_str()); - Microsoft__Quantum__Testing__QIR__Out__MessageTest__Interop(testStr2.c_str()); - - } // Recover the output stream. - - REQUIRE(outStrStream.str() == (testStr1 + "\n" + testStr2 + "\n")); -} diff --git a/src/Qir/Tests/QIR-tracer/CMakeLists.txt b/src/Qir/Tests/QIR-tracer/CMakeLists.txt deleted file mode 100644 index e19d93f587e..00000000000 --- a/src/Qir/Tests/QIR-tracer/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -set(TEST_FILES - qsharp/obj/qsharp/tracer-qir.bc -) - -#============================================================================== -# The executable target for QIR tests triggers the custom actions to compile ll files -# -add_executable(qir-tracer-tests - qir-tracer-driver.cpp - tracer-config.cpp -) - -foreach(file ${TEST_FILES}) - target_source_from_qir(qir-tracer-tests ${file}) -endforeach() - -target_link_libraries(qir-tracer-tests PUBLIC - "-L${runtime_lib_path}" - -lMicrosoft.Quantum.Qir.Runtime - -lMicrosoft.Quantum.Qir.Tracer -) - -target_include_directories(qir-tracer-tests PUBLIC - ${test_includes} - ${public_includes} - "${PROJECT_SOURCE_DIR}/../Runtime/lib/Tracer" # TODO: Remove this when tracer api is put into public headers. -) - -install(TARGETS qir-tracer-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-tracer-tests) diff --git a/src/Qir/Tests/QIR-tracer/generate.py b/src/Qir/Tests/QIR-tracer/generate.py deleted file mode 100644 index 20354e3da9a..00000000000 --- a/src/Qir/Tests/QIR-tracer/generate.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -import sys, os, platform, subprocess, datetime, shutil - -# ============================================================================= -# Generates QIR files for all *.qs files in this folder -# Accepts arguments: -# path to qsc.exe (absolute or rely on Path env) -# -# For example: "generate.py qsc.exe" or "generate.py c:\qsharp-compiler\qsc.exe" -# ============================================================================= - -# ============================================================================= -def log(message): - now = datetime.datetime.now() - current_time = now.strftime("%H:%M:%S") - print(current_time + ": " + message) -# ============================================================================= - -if __name__ == '__main__': - # this script is executed as script - root_dir = os.path.dirname(os.path.abspath(__file__)) - - # parameters - qsc = sys.argv[1] # argv[0] is the name of this script file - - # find all qs files in this folder - files_to_process = "" - output_file = "tracer-qir" - for file in os.listdir(root_dir): - (file_name, ext) = os.path.splitext(file) - if ext == ".qs": - files_to_process = files_to_process + " " + file - - # Compile as a lib so all functions are retained and don't have to workaround the current limitations of - # @EntryPoint attribute. - command = (qsc + " build --qir qir --input " + files_to_process + " --proj " + output_file) - log("Executing: " + command) - subprocess.run(command, shell = True) - - # copy the generated file into tracer's input files - generated_file = os.path.join(root_dir, "qir", output_file) + ".ll" - build_input_file = os.path.join(root_dir, output_file) + ".ll" - shutil.copyfile(generated_file, build_input_file) - diff --git a/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp b/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp deleted file mode 100644 index d82d05935b3..00000000000 --- a/src/Qir/Tests/QIR-tracer/qir-tracer-driver.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - -#include "QirContext.hpp" -#include "tracer-config.hpp" -#include "tracer.hpp" - -using namespace std; -using namespace Microsoft::Quantum; - -namespace TracerUser -{ - -TEST_CASE("Invoke each intrinsic from Q# core once", "[qir-tracer]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/, g_operationNames); - QirExecutionContext::Scoped qirctx(tr.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__Interop()); - const vector& layers = tr->UseLayers(); - - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - INFO(out.str()); - - // TestCoreIntrinsics happens to produce 24 layers right now and we are not checking whether that's expected -- as - // testing of layering logic is better done by unit tests. - CHECK(layers.size() == 24); -} - -TEST_CASE("Conditional execution on measurement result", "[qir-tracer]") -{ - shared_ptr tr = CreateTracer(1 /*layer duration*/, g_operationNames); - QirExecutionContext::Scoped qirctx(tr.get(), true /*trackAllocatedObjects*/); - - REQUIRE_NOTHROW(Microsoft__Quantum__Testing__Tracer__TestMeasurements__Interop()); - - std::stringstream out; - tr->PrintLayerMetrics(out, ",", true /*printZeroMetrics*/); - INFO(out.str()); - CHECK(tr->UseLayers().size() == 5); -} -} // namespace TracerUser diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs deleted file mode 100644 index b6b198858bb..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-conditionals.qs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Testing.Tracer { - open Microsoft.Quantum.Intrinsic; - - // Private helper operations. - operation Delay(op : (Qubit => Unit), arg : Qubit, aux : Unit) : Unit { - op(arg); - } - - @EntryPoint() - operation TestMeasurements() : Unit { - use qs = Qubit[6]; - T(qs[0]); // layer 0 - let r0 = M(qs[0]); // layer 1 - T(qs[1]); // layer 0 - CNOT(qs[1], qs[2]); // layer 1 - let qs12 = [qs[1], qs[2]]; - let r12 = Measure([PauliY, PauliX], qs12); // layer 2 - - ApplyIfElseIntrinsic(r0, Delay(X, qs[3], _), Delay(Y, qs[3], _)); // layers 2, 3 - ApplyIfElseIntrinsic(r12, Delay(Z, qs[4], _), Delay(S, qs[4], _)); // layer 3, 4 - Rx(4.2, qs[5]); // layer 0 - } -} \ No newline at end of file diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs deleted file mode 100644 index 84e57ae32e3..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-core.qs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Core { - - @Attribute() - newtype Attribute = Unit; - - @Attribute() - newtype Inline = Unit; - - @Attribute() - newtype EntryPoint = Unit; - - function Length<'T> (array : 'T[]) : Int { body intrinsic; } - - function RangeStart (range : Range) : Int { body intrinsic; } - - function RangeStep (range : Range) : Int { body intrinsic; } - - function RangeEnd (range : Range) : Int { body intrinsic; } - - function RangeReverse (range : Range) : Range { body intrinsic; } -} - -namespace Microsoft.Quantum.Targeting { - - @Attribute() - newtype TargetInstruction = String; -} \ No newline at end of file diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs deleted file mode 100644 index d110ff10eaf..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-intrinsics.qs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Testing.Tracer { - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Tracer; - - @EntryPoint() - operation TestCoreIntrinsics() : Unit { - use qs = Qubit[3]; - - X(qs[0]); - Y(qs[0]); - Z(qs[1]); - H(qs[1]); - CNOT(qs[1], qs[2]); - Rx(0.3, qs[0]); - Ry(0.4, qs[1]); - Rz(0.5, qs[2]); - //SWAP(qs[0], qs[2]); - S(qs[1]); - T(qs[2]); - - Barrier(42, 0); - - Adjoint X(qs[0]); - Adjoint Y(qs[0]); - Adjoint Z(qs[1]); - Adjoint H(qs[1]); - Adjoint CNOT(qs[1], qs[2]); - Adjoint Rx(0.3, qs[0]); - Adjoint Ry(0.4, qs[1]); - Adjoint Rz(0.5, qs[2]); - //Adjoint SWAP(qs[0], qs[2]); - Adjoint S(qs[1]); - Adjoint T(qs[2]); - - use c = Qubit() { - Controlled X([c], (qs[0])); - Controlled Y([c], (qs[0])); - Controlled Z([c], (qs[1])); - Controlled H([c], (qs[1])); - Controlled Rx([c], (0.3, qs[0])); - Controlled Ry([c], (0.4, qs[1])); - Controlled Rz([c], (0.5, qs[2])); - //Controlled SWAP([c], (qs[0], qs[2])); - Controlled S([c], (qs[1])); - Controlled T([c], (qs[2])); - } - - use cc = Qubit[2] { - Controlled X(cc, (qs[0])); - Controlled Y(cc, (qs[0])); - Controlled Z(cc, (qs[1])); - Controlled H(cc, (qs[1])); - Controlled Rx(cc, (0.3, qs[0])); - Controlled Ry(cc, (0.4, qs[1])); - Controlled Rz(cc, (0.5, qs[2])); - //Controlled SWAP(cc, (qs[0], qs[2])); - Controlled S(cc, (qs[1])); - Controlled T(cc, (qs[2])); - } - } -} diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj b/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj deleted file mode 100644 index f8677e7b9a4..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-qir.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - Exe - net6.0 - True - false - false - - - diff --git a/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs b/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs deleted file mode 100644 index f07b57c4304..00000000000 --- a/src/Qir/Tests/QIR-tracer/qsharp/tracer-target.qs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.Instructions { - - // We'll use TargetInstruction attribute to suppress Q#'s compiler decoration of names for the generated callbacks. - open Microsoft.Quantum.Targeting; - - @TargetInstruction("single_qubit_op") - operation single_qubit_op(op_id: Int, duration: Int, qb : Qubit) : Unit { - body intrinsic; - } - - @TargetInstruction("multi_qubit_op") - operation multi_qubit_op(op_id: Int, duration: Int, qbs : Qubit[]) : Unit { - body intrinsic; - } - - @TargetInstruction("single_qubit_op_ctl") - operation single_qubit_op_ctl(op_id: Int, duration: Int, ctl : Qubit[], qb : Qubit) : Unit { - body intrinsic; - } - - @TargetInstruction("multi_qubit_op_ctl") - operation multi_qubit_op_ctl(op_id: Int, duration: Int, ctl : Qubit[], qbs : Qubit[]) : Unit { - body intrinsic; - } - - @TargetInstruction("single_qubit_measure") - operation single_qubit_measure(op_id: Int, duration: Int, qb : Qubit) : Result { - body intrinsic; - } - - @TargetInstruction("joint_measure") - operation joint_measure(op_id: Int, duration: Int, qbs : Qubit[]) : Result { - body intrinsic; - } - - @TargetInstruction("apply_conditionally") - operation apply_conditionally( - measurementResults : Result[], resultsValues : Result[], - onEqualOp : (Unit => Unit) , onNonEqualOp : (Unit => Unit)) : Unit { - body intrinsic; - } - - // Operations, used in Hadamard frame tracking - @Inline() - operation Tz(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(11, 1, qb); } - adjoint (...) { single_qubit_op(11, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(12, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(12, 1, ctls, qb); } - } - - @Inline() - operation Tx(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(13, 1, qb); } - adjoint (...) { single_qubit_op(13, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(14, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(14, 1, ctls, qb); } - } - - - @Inline() - operation Sz(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(15, 1, qb); } - adjoint (...) { single_qubit_op(15, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(16, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(16, 1, ctls, qb); } - } - - @Inline() - operation Sx(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { single_qubit_op(17, 1, qb); } - adjoint (...) { single_qubit_op(17, 1, qb); } - controlled (ctls, ...) { single_qubit_op_ctl(18, 1, ctls, qb); } - controlled adjoint (ctls, ...) { single_qubit_op_ctl(18, 1, ctls, qb); } - } - - @Inline() - operation Mz(qb : Qubit) : Result { - body (...) { return single_qubit_measure(100, 1, qb); } - } - - @Inline() - operation Mx(qb : Qubit) : Result { - body (...) { return single_qubit_measure(101, 1, qb); } - } - - @Inline() - operation Mzz(qubits : Qubit[]) : Result { - body (...) { return joint_measure(102, 1, qubits); } - } - - @Inline() - operation Mxz(qubits : Qubit[]) : Result { - body (...) { return joint_measure(103, 1, qubits); } - } - - @Inline() - operation Mzx(qubits : Qubit[]) : Result { - body (...) { return joint_measure(104, 1, qubits); } - } - - @Inline() - operation Mxx(qubits : Qubit[]) : Result { - body (...) { return joint_measure(105, 1, qubits); } - } -} - -namespace Microsoft.Quantum.Tracer { - - open Microsoft.Quantum.Targeting; - - @TargetInstruction("inject_barrier") - operation Barrier(id : Int, duration : Int) : Unit { - body intrinsic; - } -} - -namespace Microsoft.Quantum.Intrinsic { - - open Microsoft.Quantum.Core; - open Microsoft.Quantum.Instructions as Phys; - open Microsoft.Quantum.Targeting; - - @Inline() - operation X(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(0, 1, qb); } - adjoint self; - controlled (ctls, ...) { - if Length(ctls) == 1 { Phys.single_qubit_op_ctl(1, 1, ctls, qb); } - else { Phys.single_qubit_op_ctl(2, 1, ctls, qb); } - } - } - - operation CNOT(control : Qubit, target : Qubit) : Unit - is Adj + Ctl { - body (...) { Controlled X([control], target); } - adjoint self; - controlled (ctls, ...) { Controlled X(ctls + [control], target); } - } - - @Inline() - operation Y(qb : Qubit) : Unit - is Adj + Ctl{ - body (...) { Phys.single_qubit_op(3, 1, qb); } - adjoint self; - controlled (ctls, ...) { - if Length(ctls) == 1 { Phys.single_qubit_op_ctl(4, 1, ctls, qb); } - else { Phys.single_qubit_op_ctl(5, 1, ctls, qb); } - } - } - - @Inline() - operation Z(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(6, 1, qb); } - adjoint self; - controlled (ctls, ...) { - if Length(ctls) == 1 { Phys.single_qubit_op_ctl(7, 1, ctls, qb); } - else { Phys.single_qubit_op_ctl(8, 1, ctls, qb); } - } - } - - @Inline() - operation H(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(9, 1, qb); } - adjoint self; - controlled (ctls, ...) { Phys.single_qubit_op_ctl(10, 1, ctls, qb); } - } - - @Inline() - operation T(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.Tz(qb); } - adjoint (...) { Adjoint Phys.Tz(qb); } - controlled (ctls, ...) { Controlled Phys.Tz(ctls, qb); } - controlled adjoint (ctls, ...) { Controlled Adjoint Phys.Tz(ctls, qb); } - } - - @Inline() - operation S(qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.Sz(qb); } - adjoint (...) { Adjoint Phys.Sz(qb); } - controlled (ctls, ...) { Controlled Phys.Sz(ctls, qb); } - controlled adjoint (ctls, ...) { Controlled Adjoint Phys.Sz(ctls, qb); } - } - - @Inline() - operation Rx(theta : Double, qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(19, 1, qb); } - adjoint (...) { Phys.single_qubit_op(19, 1, qb); } - controlled (ctls, ...) { Phys.single_qubit_op_ctl(20, 1, ctls, qb); } - controlled adjoint (ctls, ...) { Phys.single_qubit_op_ctl(20, 1, ctls, qb); } - } - - @Inline() - operation Ry(theta : Double, qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(21, 1, qb); } - adjoint (...) { Phys.single_qubit_op(21, 1, qb); } - controlled (ctls, ...) { Phys.single_qubit_op_ctl(22, 1, ctls, qb); } - controlled adjoint (ctls, ...) { Phys.single_qubit_op_ctl(22, 1, ctls, qb); } - } - - @Inline() - operation Rz(theta : Double, qb : Qubit) : Unit - is Adj + Ctl { - body (...) { Phys.single_qubit_op(23, 1, qb); } - adjoint (...) { Phys.single_qubit_op(23, 1, qb); } - controlled (ctls, ...) { Phys.single_qubit_op_ctl(24, 1, ctls, qb); } - controlled adjoint (ctls, ...) { Phys.single_qubit_op_ctl(24, 1, ctls, qb); } - } - - @Inline() - operation M(qb : Qubit) : Result { - body (...) { return Phys.Mz(qb); } - } - - @Inline() - operation Measure(paulis : Pauli[], qubits : Qubit[]) : Result { - body (...) { - mutable res = One; - mutable haveY = false; - // Measurements that involve PauliY or PauliI - for i in 0..Length(paulis)-1 { - if paulis[i] == PauliY or paulis[i] == PauliI { - set haveY = true; - } - } - if haveY { set res = Phys.joint_measure(106, 1, qubits); } - - // More than two qubits (but no PauliY or PauliI) - elif Length(paulis) > 2 { set res = Phys.joint_measure(107, 1, qubits); } - - // Single qubit measurement -- differentiate between Mx and Mz - elif Length(paulis) == 1 { - if (paulis[0] == PauliX) { set res = Phys.Mx(qubits[0]); } - else { set res = Phys.Mz(qubits[0]); } - } - - // Specialize for two-qubit measurements: Mxx, Mxz, Mzx, Mzz - elif paulis[0] == PauliX and paulis[1] == PauliX { set res = Phys.Mxx(qubits); } - elif paulis[0] == PauliX and paulis[1] == PauliZ { set res = Phys.Mxz(qubits); } - elif paulis[0] == PauliZ and paulis[1] == PauliX { set res = Phys.Mzx(qubits); } - elif paulis[0] == PauliZ and paulis[1] == PauliZ { set res = Phys.Mzz(qubits); } - - //shouldn't get here - return res; - } - } - - operation ApplyConditionallyIntrinsic( - measurementResults : Result[], resultsValues : Result[], - onEqualOp : (Unit => Unit) , onNonEqualOp : (Unit => Unit)) : Unit { - body (...) { return Phys.apply_conditionally(measurementResults, resultsValues, onEqualOp, onNonEqualOp); } - } - - operation ApplyIfElseIntrinsic( - measurementResult : Result, onResultZeroOp : (Unit => Unit) , onResultOneOp : (Unit => Unit)) : Unit { - body (...) { return Phys.apply_conditionally([measurementResult], [Zero], onResultZeroOp, onResultOneOp); } - } - - // operation SWAP(a : Qubit, b : Qubit) : Unit - // is Adj { - // body intrinsic; - // adjoint self; - // } - - -} diff --git a/src/Qir/Tests/QIR-tracer/tracer-config.cpp b/src/Qir/Tests/QIR-tracer/tracer-config.cpp deleted file mode 100644 index 41f6410feb8..00000000000 --- a/src/Qir/Tests/QIR-tracer/tracer-config.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// TODO: ideally, this file should be generated by the Q# compiler alongside the qir, using the mappings specified in -// target.qs. - -#include - -#include "QirRuntimeApi_I.hpp" -#include "tracer-config.hpp" - -namespace TracerUser -{ -const std::unordered_map g_operationNames = { - {0, "X"}, {1, "CX"}, {2, "MCX"}, {3, "Y"}, {4, "CY"}, {5, "MCY"}, {6, "Z"}, - {7, "CZ"}, {8, "MCZ"}, {19, "Rx"}, {20, "MCRx"}, {21, "Ry"}, {22, "MCRy"}, {23, "Rz"}, - {24, "MCRz"}, {9, "H"}, {10, "MCH"}, {11, "T"}, {12, "MCT"}, {15, "S"}, {16, "MCS"} /*etc.*/}; -} diff --git a/src/Qir/Tests/QIR-tracer/tracer-config.hpp b/src/Qir/Tests/QIR-tracer/tracer-config.hpp deleted file mode 100644 index 621bd269347..00000000000 --- a/src/Qir/Tests/QIR-tracer/tracer-config.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// TODO: ideally, this file should be generated by the Q# compiler alongside the qir - -#pragma once - -#include -#include - -#include "TracerTypes.hpp" - -namespace TracerUser -{ -extern const std::unordered_map g_operationNames; -} // namespace TracerUser - -// Available function in generated QIR -extern "C" void Microsoft__Quantum__Testing__Tracer__TestCoreIntrinsics__Interop(); // NOLINT -extern "C" void Microsoft__Quantum__Testing__Tracer__TestMeasurements__Interop(); // NOLINT diff --git a/src/Qir/Tests/TestUtils.cpp b/src/Qir/Tests/TestUtils.cpp deleted file mode 100644 index 045b3730160..00000000000 --- a/src/Qir/Tests/TestUtils.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include -#include - -// Can manually add calls to DebugLog in the ll files for debugging. -extern "C" void DebugLog(int64_t value) -{ - std::cout << value << std::endl; -} -extern "C" void DebugLogPtr(char* value) -{ - std::cout << (const void*)value << std::endl; -} diff --git a/src/Qir/Tests/TestUtils.hpp b/src/Qir/Tests/TestUtils.hpp deleted file mode 100644 index c4239f5ce27..00000000000 --- a/src/Qir/Tests/TestUtils.hpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#ifndef TESTUTILS_HPP -#define TESTUTILS_HPP - -#include - -extern "C" void DebugLog(int64_t value); -extern "C" void DebugLogPtr(char* value); - -#endif // #ifndef TESTUTILS_HPP diff --git a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs deleted file mode 100644 index ea65c7069e5..00000000000 --- a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Collections.Generic; -using System.IO; -using System.Text; - -using Xunit; - -using Microsoft.Quantum.Qir.Runtime.Tools.Driver; -using Microsoft.Quantum.Qir.Serialization; - -namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools -{ - public class QirDriverGeneratorTests - { - private static string TestArtifactsDirectory = Path.Combine("TestArtifacts", "FullStateDriverGenerator"); - private static string TestCasesDirectory = Path.Combine("TestCases", "FullStateDriverGenerator"); - private static IDictionary TestCases = - new Dictionary - { - { - "UseNoArgs", - new EntryPointOperation{Name = "UseNoArgs"} - }, - { - "UseBoolArg", - new EntryPointOperation - { - Name = "UseBoolArg", - Parameters = new List{new Parameter{ Name = "BoolArg", Type = DataType.BoolType}} - } - }, - { - "UseBoolArrayArg", - new EntryPointOperation - { - Name = "UseBoolArrayArg", - Parameters = new List{new Parameter{ Name = "BoolArrayArg", Type = DataType.ArrayType, ArrayType = DataType.BoolType}} - } - }, - { - "UseDoubleArg", - new EntryPointOperation - { - Name = "UseDoubleArg", - Parameters = new List{new Parameter{ Name = "DoubleArg", Type = DataType.DoubleType}} - } - }, - { - "UseDoubleArrayArg", - new EntryPointOperation - { - Name = "UseDoubleArrayArg", - Parameters = new List{new Parameter{ Name = "DoubleArrayArg", Type = DataType.ArrayType, ArrayType = DataType.DoubleType}} - } - }, - { - "UseIntegerArg", - new EntryPointOperation - { - Name = "UseIntegerArg", - Parameters = new List{new Parameter{ Name = "IntegerArg", Type = DataType.IntegerType}} - } - }, - { - "UseIntegerArrayArg", - new EntryPointOperation - { - Name = "UseIntegerArrayArg", - Parameters = new List{new Parameter{ Name = "IntegerArrayArg", Type = DataType.ArrayType, ArrayType = DataType.IntegerType}} - } - }, - { - "UsePauliArg", - new EntryPointOperation - { - Name = "UsePauliArg", - Parameters = new List{new Parameter{ Name = "PauliArg", Type = DataType.PauliType}} - } - }, - { - "UsePauliArrayArg", - new EntryPointOperation - { - Name = "UsePauliArrayArg", - Parameters = new List{new Parameter{ Name = "PauliArrayArg", Type = DataType.ArrayType, ArrayType = DataType.PauliType}} - } - }, - { - "UseRangeArg", - new EntryPointOperation - { - Name = "UseRangeArg", - Parameters = new List{new Parameter{ Name = "RangeArg", Type = DataType.RangeType}} - } - }, - { - "UseRangeArrayArg", - new EntryPointOperation - { - Name = "UseRangeArrayArg", - Parameters = new List{new Parameter{ Name = "RangeArrayArg", Type = DataType.ArrayType, ArrayType = DataType.RangeType}} - } - }, - { - "UseResultArg", - new EntryPointOperation - { - Name = "UseResultArg", - Parameters = new List{new Parameter{ Name = "ResultArg", Type = DataType.ResultType}} - } - }, - { - "UseResultArrayArg", - new EntryPointOperation - { - Name = "UseResultArrayArg", - Parameters = new List{new Parameter{ Name = "ResultArrayArg", Type = DataType.ArrayType, ArrayType = DataType.ResultType}} - } - }, - { - "UseStringArg", - new EntryPointOperation - { - Name = "UseStringArg", - Parameters = new List{new Parameter{ Name = "StringArg", Type = DataType.StringType}} - } - }, - { - "UseMiscArgs", - new EntryPointOperation - { - Name = "UseMiscArgs", - Parameters = new List{ - new Parameter{ Name = "BoolArg", Type = DataType.BoolType}, - new Parameter{ Name = "IntegerArrayArg", Position = 1, Type = DataType.ArrayType, ArrayType = DataType.IntegerType}, - new Parameter{ Name = "RangeArg", Position = 2, Type = DataType.RangeType}, - new Parameter{ Name = "StringArg", Position = 3, Type = DataType.StringType} - } - } - } - }; - - private static string RemoveLineEndings(string str) => - str.Replace("\n", string.Empty).Replace("\r", string.Empty); - - [Theory] - [InlineData("UseNoArgs")] - [InlineData("UseBoolArg")] - [InlineData("UseBoolArrayArg")] - [InlineData("UseDoubleArg")] - [InlineData("UseDoubleArrayArg")] - [InlineData("UseIntegerArg")] - [InlineData("UseIntegerArrayArg")] - [InlineData("UsePauliArg")] - [InlineData("UsePauliArrayArg")] - [InlineData("UseRangeArg")] - [InlineData("UseRangeArrayArg")] - [InlineData("UseResultArg")] - [InlineData("UseResultArrayArg")] - [InlineData("UseStringArg")] - [InlineData("UseMiscArgs")] - public void GenerateFullStateSimulatorDriver(string testCase) - { - var entryPointOperation = TestCases[testCase]; - var driverGenerator = new QirFullStateDriverGenerator(); - var driverFileName = $"{testCase}.cpp"; - var verificationCppSourceCode = RemoveLineEndings(File.ReadAllText(Path.Combine(TestCasesDirectory, driverFileName))); - Directory.CreateDirectory(TestArtifactsDirectory); - var generatedStream = File.Create(Path.Combine(TestArtifactsDirectory, driverFileName)); - driverGenerator.GenerateAsync(entryPointOperation, generatedStream).Wait(); - var generatedStreamReader = new StreamReader(generatedStream, Encoding.UTF8); - var generatedCppSourceCode = RemoveLineEndings(generatedStreamReader.ReadToEnd()); - Assert.Equal(verificationCppSourceCode, generatedCppSourceCode); - generatedStream.Close(); - } - - [Theory] - [MemberData(nameof(CommandLineArgumentsData))] - public void GenerateCommandLineArguments(string expected, ExecutionInformation info) - { - var generator = new QirFullStateDriverGenerator(); - Assert.Equal(expected, generator.GetCommandLineArguments(info)); - } - - public static IEnumerable CommandLineArgumentsData - { - get - { - static object[] TestCase( - string expected, List parameters, Dictionary arguments) => - new object[] - { - expected, - new ExecutionInformation - { - EntryPoint = new EntryPointOperation { Parameters = parameters }, - ArgumentValues = arguments - } - }; - - yield return TestCase( - "--foo 5", - new List { new Parameter { Name = "foo", Position = 0, Type = DataType.IntegerType } }, - new Dictionary - { ["foo"] = new ArgumentValue { Type = DataType.IntegerType, Integer = 5 } }); - - yield return TestCase( - "-n 5", - new List { new Parameter { Name = "n", Position = 0, Type = DataType.IntegerType } }, - new Dictionary - { ["n"] = new ArgumentValue { Type = DataType.IntegerType, Integer = 5 } }); - - yield return TestCase( - "--foo 5 --bar \"abc\"", - new List - { - new Parameter { Name = "bar", Position = 1, Type = DataType.StringType }, - new Parameter { Name = "foo", Position = 0, Type = DataType.IntegerType } - }, - new Dictionary - { - ["bar"] = new ArgumentValue { Type = DataType.StringType, String = "abc" }, - ["foo"] = new ArgumentValue { Type = DataType.IntegerType, Integer = 5 } - }); - } - } - } -} diff --git a/src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs b/src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs deleted file mode 100644 index 3b916ffdb25..00000000000 --- a/src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Runtime.Tools.Executable; -using Moq; -using Xunit; - -namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools -{ - public class QirExecutableGeneratorTests : IDisposable - { - private readonly Mock clangClientMock; - private readonly QirExecutableGenerator executableGenerator; - private readonly DirectoryInfo sourceDirectory; - private readonly DirectoryInfo includeDirectory; - private readonly DirectoryInfo libraryDirectory; - private readonly DirectoryInfo binDirectory; - private readonly IList libraryFiles; - private readonly IList sourceFiles; - private IList linkLibraries; - - public QirExecutableGeneratorTests() - { - clangClientMock = new Mock(); - executableGenerator = new QirExecutableGenerator(clangClientMock.Object, null); - - // Set up files. - var prefix = Guid.NewGuid().ToString(); - binDirectory = new DirectoryInfo($"{prefix}-bin"); - binDirectory.Create(); - libraryDirectory = new DirectoryInfo($"{prefix}-library"); - libraryDirectory.Create(); - libraryFiles = new List() - { - Util.CreateBinaryFile(libraryDirectory, "lib1", new byte[]{0x01, 0x23, 0x45, 0x67}), - Util.CreateBinaryFile(libraryDirectory, "lib2", new byte[]{0x89, 0xAB, 0xCD, 0xEF}), - }; - includeDirectory = new DirectoryInfo($"{prefix}-include"); - includeDirectory.Create(); - sourceDirectory = new DirectoryInfo($"{prefix}-source"); - sourceDirectory.Create(); - sourceFiles = new List() - { - Util.CreateTextFile(sourceDirectory, "src1.cpp", "src1 contents"), - Util.CreateBinaryFile(sourceDirectory, "src2.bc", new byte[]{0xFE, 0xDC, 0xBA, 0x98}), - }; - linkLibraries = new List { "lib1", "lib2" }; - } - - public void Dispose() - { - sourceDirectory.Delete(true); - includeDirectory.Delete(true); - libraryDirectory.Delete(true); - binDirectory.Delete(true); - } - - [Fact] - public async Task TestGenerateExecutable() - { - var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); - await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries); - - // Verify invocation of clang. - clangClientMock.Verify(obj => obj.CreateExecutableAsync( - It.Is(s => s.OrderBy(val => val).SequenceEqual(sourceFiles.OrderBy(val => val.FullName).Select(fileInfo => fileInfo.FullName))), - It.Is(s => s.OrderBy(val => val).SequenceEqual(linkLibraries.OrderBy(val => val))), - new[] { libraryDirectory.FullName }, - new[] { includeDirectory.FullName }, - executableFile.FullName)); - - // Verify files were copied. - Assert.True(FilesWereCopied(libraryFiles.ToArray(), binDirectory)); - } - - private static bool FilesWereCopied(FileInfo[] files, DirectoryInfo destinationDirectory) - { - var destinationDirectoryFiles = destinationDirectory.GetFiles(); - - // N.B. Runtime is (number of FileInfo objects passed in) * (number of files in destination directory). - foreach (var file in files) - { - // Make sure that the file was copied and that the contents is the same. - var copiedFile = destinationDirectoryFiles.FirstOrDefault(fileInfo => fileInfo.Name == file.Name); - if (copiedFile == null) - { - return false; - } - - if (!Util.CompareFiles(file, copiedFile)) - { - return false; - } - - } - - return true; - } - } -} diff --git a/src/Qir/Tests/Tools/QirExecutableTests.cs b/src/Qir/Tests/Tools/QirExecutableTests.cs deleted file mode 100644 index 23881978475..00000000000 --- a/src/Qir/Tests/Tools/QirExecutableTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Quantum.Qir.Serialization; -using Microsoft.Quantum.Qir.Runtime.Tools.Driver; -using Microsoft.Quantum.Qir.Runtime.Tools.Executable; -using Moq; -using Xunit; - -namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools -{ - public class QirExecutableTests : IDisposable - { - private readonly DirectoryInfo sourceDirectory; - private readonly DirectoryInfo includeDirectory; - private readonly DirectoryInfo libraryDirectory; - private readonly DirectoryInfo binDirectory; - private readonly Mock driverGeneratorMock; - private readonly Mock executableGeneratorMock; - private readonly Mock runnerMock; - private readonly Mock qirExecutable; - private readonly FileInfo executableFile; - private readonly byte[] qirBytecode = { 1, 2, 3, 4, 5 }; - private readonly IList linkLibraries; - private readonly IList headerDirectories; - private readonly IList libraryDirectories; - - public QirExecutableTests() - { - // Set up files. - var prefix = Guid.NewGuid().ToString(); - binDirectory = new DirectoryInfo($"{prefix}-bin"); - binDirectory.Create(); - libraryDirectory = new DirectoryInfo($"{prefix}-library"); - libraryDirectory.Create(); - includeDirectory = new DirectoryInfo($"{prefix}-include"); - includeDirectory.Create(); - sourceDirectory = new DirectoryInfo($"{prefix}-source"); - sourceDirectory.Create(); - executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executable")); - driverGeneratorMock = new Mock(); - executableGeneratorMock = new Mock(); - runnerMock = new Mock(); - qirExecutable = new Mock(executableFile, qirBytecode, driverGeneratorMock.Object, executableGeneratorMock.Object, runnerMock.Object, null) { CallBase = true }; - linkLibraries = new List { "lib1", "lib2" }; - headerDirectories = new List(); - libraryDirectories = new List(); - qirExecutable.SetupGet(obj => obj.LinkLibraries).Returns(linkLibraries); - qirExecutable.SetupGet(obj => obj.SourceDirectoryPath).Returns(sourceDirectory.FullName); - qirExecutable.SetupGet(obj => obj.DriverFileExtension).Returns(".cpp"); - qirExecutable.SetupGet(obj => obj.HeaderDirectories).Returns(headerDirectories); - qirExecutable.SetupGet(obj => obj.LibraryDirectories).Returns(libraryDirectories); - } - - public void Dispose() - { - sourceDirectory.Delete(true); - includeDirectory.Delete(true); - libraryDirectory.Delete(true); - binDirectory.Delete(true); - } - - [Fact] - public async Task TestBuild() - { - // Set up. - var entryPoint = new EntryPointOperation(); - var driverFileContents = "driver file contents"; - driverGeneratorMock.Setup(obj => obj.GenerateAsync(entryPoint, It.IsAny())).Callback((entryPoint, stream) => - { - using var streamWriter = new StreamWriter(stream); - streamWriter.Write(driverFileContents); - }); - - // Build the executable. - await qirExecutable.Object.BuildAsync(entryPoint, new[] { libraryDirectory }, new[] { includeDirectory }); - - // Verify that the "bytecode" file was created correctly. - var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); - using var bytecodeFileStream = bytecodeFilePath.OpenRead(); - Assert.True(Util.CompareStreams(new MemoryStream(qirBytecode), bytecodeFileStream)); - - // Verify that the driver was written to the correct file. - var driver = new FileInfo(Path.Combine(sourceDirectory.FullName, "driver.cpp")); - using var driverStreamReader = driver.OpenText(); - var actualDriverContents = driverStreamReader.ReadToEnd(); - Assert.Equal(driverFileContents, actualDriverContents); - - // Verify that the executable was generated. - executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync(executableFile, It.Is(arg => arg.FullName == sourceDirectory.FullName), new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries)); - } - - [Fact] - public async Task TestRun() - { - // Set up. - using var outputStream = new MemoryStream(); - var execInfo = new ExecutionInformation(); - var arguments = "arguments"; - driverGeneratorMock.Setup(obj => obj.GetCommandLineArguments(execInfo)).Returns(arguments); - - // Run executable. - await qirExecutable.Object.RunAsync(execInfo, outputStream); - - // Verify runner was invoked properly. - runnerMock.Verify(obj => obj.RunExecutableAsync(executableFile, outputStream, arguments)); - } - } -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp deleted file mode 100644 index 1855912aa69..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Bool type. -const char InteropFalseAsChar = 0x0; -const char InteropTrueAsChar = 0x1; -map BoolAsCharMap{ - {"0", InteropFalseAsChar}, - {"false", InteropFalseAsChar}, - {"1", InteropTrueAsChar}, - {"true", InteropTrueAsChar} -}; - -extern "C" void UseBoolArg( - char BoolArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - char BoolArgCli; - BoolArgCli = InteropFalseAsChar; - app.add_option("--BoolArg", BoolArgCli, "Option to provide a value for the BoolArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - char BoolArgInterop = BoolArgCli; - - // Execute the entry point operation. - UseBoolArg( - BoolArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp deleted file mode 100644 index 5bfbc30196b..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp +++ /dev/null @@ -1,84 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -// Auxiliary functions for interop with Q# Bool type. -const char InteropFalseAsChar = 0x0; -const char InteropTrueAsChar = 0x1; -map BoolAsCharMap{ - {"0", InteropFalseAsChar}, - {"false", InteropFalseAsChar}, - {"1", InteropTrueAsChar}, - {"true", InteropTrueAsChar} -}; - -extern "C" void UseBoolArrayArg( - InteropArray* BoolArrayArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - vector BoolArrayArgCli; - app.add_option("--BoolArrayArg", BoolArrayArgCli, "Option to provide a value for the BoolArrayArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - unique_ptr BoolArrayArgUniquePtr = CreateInteropArray(BoolArrayArgCli); - InteropArray* BoolArrayArgInterop = BoolArrayArgUniquePtr.get(); - - // Execute the entry point operation. - UseBoolArrayArg( - BoolArrayArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp deleted file mode 100644 index 6eefd32abbe..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -extern "C" void UseDoubleArg( - double_t DoubleArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - double_t DoubleArgCli; - DoubleArgCli = 0.0; - app.add_option("--DoubleArg", DoubleArgCli, "Option to provide a value for the DoubleArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - double_t DoubleArgInterop = DoubleArgCli; - - // Execute the entry point operation. - UseDoubleArg( - DoubleArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp deleted file mode 100644 index d6593923e71..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp +++ /dev/null @@ -1,73 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -extern "C" void UseDoubleArrayArg( - InteropArray* DoubleArrayArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - vector DoubleArrayArgCli; - app.add_option("--DoubleArrayArg", DoubleArrayArgCli, "Option to provide a value for the DoubleArrayArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - unique_ptr DoubleArrayArgUniquePtr = CreateInteropArray(DoubleArrayArgCli); - InteropArray* DoubleArrayArgInterop = DoubleArrayArgUniquePtr.get(); - - // Execute the entry point operation. - UseDoubleArrayArg( - DoubleArrayArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp deleted file mode 100644 index d6e6ebfd3f1..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -extern "C" void UseIntegerArg( - int64_t IntegerArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - int64_t IntegerArgCli; - IntegerArgCli = 0; - app.add_option("--IntegerArg", IntegerArgCli, "Option to provide a value for the IntegerArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - int64_t IntegerArgInterop = IntegerArgCli; - - // Execute the entry point operation. - UseIntegerArg( - IntegerArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp deleted file mode 100644 index 73ef2abbbe6..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp +++ /dev/null @@ -1,73 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -extern "C" void UseIntegerArrayArg( - InteropArray* IntegerArrayArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - vector IntegerArrayArgCli; - app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); - InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); - - // Execute the entry point operation. - UseIntegerArrayArg( - IntegerArrayArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp deleted file mode 100644 index c43b4506176..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp +++ /dev/null @@ -1,146 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -// Auxiliary functions for interop with Q# Range type. -using RangeTuple = tuple; -struct InteropRange -{ - int64_t Start; - int64_t Step; - int64_t End; - - InteropRange() : - Start(0), - Step(0), - End(0){} - - InteropRange(RangeTuple rangeTuple) : - Start(get<0>(rangeTuple)), - Step(get<1>(rangeTuple)), - End(get<2>(rangeTuple)){} -}; - -unique_ptr CreateInteropRange(RangeTuple rangeTuple) -{ - unique_ptr range(new InteropRange(rangeTuple)); - return range; -} - -InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) -{ - InteropRange* range = new InteropRange(rangeTuple); - return range; -} - -// Auxiliary functions for interop with Q# Bool type. -const char InteropFalseAsChar = 0x0; -const char InteropTrueAsChar = 0x1; -map BoolAsCharMap{ - {"0", InteropFalseAsChar}, - {"false", InteropFalseAsChar}, - {"1", InteropTrueAsChar}, - {"true", InteropTrueAsChar} -}; - -// Auxiliary functions for interop with Q# String type. -const char* TranslateStringToCharBuffer(string& s) -{ - return s.c_str(); -} - -extern "C" void UseMiscArgs( - char BoolArg, - InteropArray* IntegerArrayArg, - InteropRange* RangeArg, - const char* StringArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - char BoolArgCli; - BoolArgCli = InteropFalseAsChar; - app.add_option("--BoolArg", BoolArgCli, "Option to provide a value for the BoolArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); - - vector IntegerArrayArgCli; - app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") - ->required(); - - RangeTuple RangeArgCli; - app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") - ->required(); - - string StringArgCli; - app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - char BoolArgInterop = BoolArgCli; - - unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); - InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); - - InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); - - const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); - - // Execute the entry point operation. - UseMiscArgs( - BoolArgInterop, - IntegerArrayArgInterop, - RangeArgInterop, - StringArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp deleted file mode 100644 index 56061656927..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp +++ /dev/null @@ -1,37 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -extern "C" void UseNoArgs( -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Execute the entry point operation. - UseNoArgs( - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp deleted file mode 100644 index cb923423b76..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Pauli type. -map PauliMap{ - {"PauliI", 0}, - {"PauliX", 1}, - {"PauliY", 3}, - {"PauliZ", 2} -}; - -extern "C" void UsePauliArg( - char PauliArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - char PauliArgCli; - PauliArgCli = 0; - app.add_option("--PauliArg", PauliArgCli, "Option to provide a value for the PauliArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - char PauliArgInterop = PauliArgCli; - - // Execute the entry point operation. - UsePauliArg( - PauliArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp deleted file mode 100644 index c754efbb865..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp +++ /dev/null @@ -1,82 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -// Auxiliary functions for interop with Q# Pauli type. -map PauliMap{ - {"PauliI", 0}, - {"PauliX", 1}, - {"PauliY", 3}, - {"PauliZ", 2} -}; - -extern "C" void UsePauliArrayArg( - InteropArray* PauliArrayArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - vector PauliArrayArgCli; - app.add_option("--PauliArrayArg", PauliArrayArgCli, "Option to provide a value for the PauliArrayArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgCli); - InteropArray* PauliArrayArgInterop = PauliArrayArgUniquePtr.get(); - - // Execute the entry point operation. - UsePauliArrayArg( - PauliArrayArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp deleted file mode 100644 index e05012edb4a..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp +++ /dev/null @@ -1,78 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Range type. -using RangeTuple = tuple; -struct InteropRange -{ - int64_t Start; - int64_t Step; - int64_t End; - - InteropRange() : - Start(0), - Step(0), - End(0){} - - InteropRange(RangeTuple rangeTuple) : - Start(get<0>(rangeTuple)), - Step(get<1>(rangeTuple)), - End(get<2>(rangeTuple)){} -}; - -unique_ptr CreateInteropRange(RangeTuple rangeTuple) -{ - unique_ptr range(new InteropRange(rangeTuple)); - return range; -} - -InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) -{ - InteropRange* range = new InteropRange(rangeTuple); - return range; -} - -extern "C" void UseRangeArg( - InteropRange* RangeArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - RangeTuple RangeArgCli; - app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); - - // Execute the entry point operation. - UseRangeArg( - RangeArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp deleted file mode 100644 index 2ea36be72c2..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp +++ /dev/null @@ -1,116 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -// Auxiliary functions for interop with Q# Range type. -using RangeTuple = tuple; -struct InteropRange -{ - int64_t Start; - int64_t Step; - int64_t End; - - InteropRange() : - Start(0), - Step(0), - End(0){} - - InteropRange(RangeTuple rangeTuple) : - Start(get<0>(rangeTuple)), - Step(get<1>(rangeTuple)), - End(get<2>(rangeTuple)){} -}; - -unique_ptr CreateInteropRange(RangeTuple rangeTuple) -{ - unique_ptr range(new InteropRange(rangeTuple)); - return range; -} - -InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) -{ - InteropRange* range = new InteropRange(rangeTuple); - return range; -} - -// Auxiliary functions for interop with Q# Range[] type -template -void FreePointerVector(vector& v) -{ - for (auto p : v) - { - delete p; - } -} - -extern "C" void UseRangeArrayArg( - InteropArray* RangeArrayArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - vector RangeArrayArgCli; - app.add_option("--RangeArrayArg", RangeArrayArgCli, "Option to provide a value for the RangeArrayArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - vector RangeArrayArgIntermediate; - TranslateVector(RangeArrayArgCli, RangeArrayArgIntermediate, TranslateRangeTupleToInteropRangePointer); - unique_ptr RangeArrayArgUniquePtr = CreateInteropArray(RangeArrayArgIntermediate); - InteropArray* RangeArrayArgInterop = RangeArrayArgUniquePtr.get(); - - // Execute the entry point operation. - UseRangeArrayArg( - RangeArrayArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp deleted file mode 100644 index c3757bff52f..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Result type. -const char InteropResultZeroAsChar = 0x0; -const char InteropResultOneAsChar = 0x1; -map ResultAsCharMap{ - {"0", InteropResultZeroAsChar}, - {"Zero", InteropResultZeroAsChar}, - {"1", InteropResultOneAsChar}, - {"One", InteropResultOneAsChar} -}; - -extern "C" void UseResultArg( - char ResultArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - char ResultArgCli; - ResultArgCli = InteropResultZeroAsChar; - app.add_option("--ResultArg", ResultArgCli, "Option to provide a value for the ResultArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case)); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - char ResultArgInterop = ResultArgCli; - - // Execute the entry point operation. - UseResultArg( - ResultArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp deleted file mode 100644 index 1bc6992c838..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp +++ /dev/null @@ -1,84 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# Array type. -struct InteropArray -{ - int64_t Size; - void* Data; - - InteropArray(int64_t size, void* data) : - Size(size), - Data(data){} -}; - -template -unique_ptr CreateInteropArray(vector& v) -{ - unique_ptr array(new InteropArray(v.size(), v.data())); - return array; -} - -template -void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) -{ - destinationVector.resize(sourceVector.size()); - transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); -} - -// Auxiliary functions for interop with Q# Result type. -const char InteropResultZeroAsChar = 0x0; -const char InteropResultOneAsChar = 0x1; -map ResultAsCharMap{ - {"0", InteropResultZeroAsChar}, - {"Zero", InteropResultZeroAsChar}, - {"1", InteropResultOneAsChar}, - {"One", InteropResultOneAsChar} -}; - -extern "C" void UseResultArrayArg( - InteropArray* ResultArrayArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - vector ResultArrayArgCli; - app.add_option("--ResultArrayArg", ResultArrayArgCli, "Option to provide a value for the ResultArrayArg parameter") - ->required() - ->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case)); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - unique_ptr ResultArrayArgUniquePtr = CreateInteropArray(ResultArrayArgCli); - InteropArray* ResultArrayArgInterop = ResultArrayArgUniquePtr.get(); - - // Execute the entry point operation. - UseResultArrayArg( - ResultArrayArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp deleted file mode 100644 index a1908a9c045..00000000000 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp +++ /dev/null @@ -1,53 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. -// The purpose of this source code file is to provide an entry-point for executing a QIR program. -// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. -//---------------------------------------------------------------------------------------------------------------------- - -#include -#include -#include -#include -#include - -#include "CLI11.hpp" - -using namespace std; - -// Auxiliary functions for interop with Q# String type. -const char* TranslateStringToCharBuffer(string& s) -{ - return s.c_str(); -} - -extern "C" void UseStringArg( - const char* StringArg -); // QIR interop function. - -int main(int argc, char* argv[]) -{ - CLI::App app("QIR Standalone Entry Point"); - - // Initialize runtime. - // Add a command line option for each entry-point parameter. - string StringArgCli; - app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") - ->required(); - - // After all the options have been added, parse arguments from the command line. - CLI11_PARSE(app, argc, argv); - - // Cast parsed arguments to its interop types. - const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); - - // Execute the entry point operation. - UseStringArg( - StringArgInterop - ); - - // Flush the output of the simulation. - cout.flush(); - - return 0; -} diff --git a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj deleted file mode 100644 index f74161bb917..00000000000 --- a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - x64 - net6.0 - false - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - - diff --git a/src/Qir/Tests/Tools/Util.cs b/src/Qir/Tests/Tools/Util.cs deleted file mode 100644 index 5783df11d3a..00000000000 --- a/src/Qir/Tests/Tools/Util.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.IO; - -namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools -{ - internal static class Util - { - public static bool CompareFiles(FileInfo fileA, FileInfo fileB) - { - if (fileA.FullName == fileB.FullName) - { - return true; - } - - using var fileStreamA = fileA.OpenRead(); - using var fileStreamB = fileB.OpenRead(); - return CompareStreams(fileStreamA, fileStreamB); - } - - public static bool CompareStreams(Stream streamA, Stream streamB) - { - if (streamA.Length != streamB.Length) - { - return false; - } - - (streamA.Position, streamB.Position) = (0, 0); - (int byteA, int byteB) = (0, 0); - while ((byteA == byteB) && (byteA != -1)) - { - (byteA, byteB) = (streamA.ReadByte(), streamB.ReadByte()); - } - - return byteA == byteB; - } - - public static FileInfo CreateBinaryFile(DirectoryInfo directory, string fileName, byte[] contents) - { - var fileInfo = new FileInfo(Path.Combine(directory.FullName, fileName)); - File.WriteAllBytes(fileInfo.FullName, contents); - return fileInfo; - } - - public static FileInfo CreateTextFile(DirectoryInfo directory, string fileName, string contents) - { - var fileInfo = new FileInfo(Path.Combine(directory.FullName, fileName)); - File.WriteAllText(fileInfo.FullName, contents); - return fileInfo; - } - } -} diff --git a/src/Qir/Tests/build-qir-tests.ps1 b/src/Qir/Tests/build-qir-tests.ps1 index 51673d69b53..a6e66e8f365 100644 --- a/src/Qir/Tests/build-qir-tests.ps1 +++ b/src/Qir/Tests/build-qir-tests.ps1 @@ -16,7 +16,6 @@ Write-Host "##[info]Compile Q# Test Projects into QIR" Build-QirProject (Join-Path $PSScriptRoot QIR-static qsharp) -SkipQSharpBuild:$SkipQSharpBuild Build-QirProject (Join-Path $PSScriptRoot QIR-dynamic qsharp) -SkipQSharpBuild:$SkipQSharpBuild -Build-QirProject (Join-Path $PSScriptRoot QIR-tracer qsharp) -SkipQSharpBuild:$SkipQSharpBuild Build-QirProject (Join-Path $PSScriptRoot FullstateSimulator qsharp) -SkipQSharpBuild:$SkipQSharpBuild if (-not (Build-CMakeProject $PSScriptRoot "QIR Tests")) { diff --git a/src/Qir/build_all.ps1 b/src/Qir/build_all.ps1 deleted file mode 100644 index 9901b96cfef..00000000000 --- a/src/Qir/build_all.ps1 +++ /dev/null @@ -1 +0,0 @@ -& Runtime/build-qir-runtime.ps1 && & Tests/build-qir-tests.ps1 && & Samples/build-qir-samples.ps1 diff --git a/src/Qir/riqir-test/CMakeLists.txt b/src/Qir/riqir-test/CMakeLists.txt deleted file mode 100644 index 35a282bf9d2..00000000000 --- a/src/Qir/riqir-test/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -cmake_minimum_required(VERSION 3.20 FATAL_ERROR) - -message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") - -# set the project name and version -project(riqir-tests) - -# specify the C++ standard, compiler and other tools -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") - -# feel free to customize these flags for your local builds (don't check in) -set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-inline") - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../Common/cmake") - -set(public_includes "${PROJECT_SOURCE_DIR}/../Runtime/public") -set(common_includes "${PROJECT_SOURCE_DIR}/../Common/Include") -set(test_includes "${PROJECT_SOURCE_DIR}/../Common/Externals/catch2") - -# set the environment path for loading shared libs the tests are using -if(DEFINED ENV{NATIVE_SIMULATOR}) - set(simulator_lib_path $ENV{NATIVE_SIMULATOR}) -else() - set(simulator_lib_path "${PROJECT_SOURCE_DIR}/../../Simulation/Native/build/drop") -endif() - -include(qir_cmake_include) -include(unit_test_include) - -add_subdirectory(FullstateSimulator) -add_subdirectory(QIR-dynamic) -add_subdirectory(QIR-static) diff --git a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt b/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt deleted file mode 100644 index c3fc6685cfd..00000000000 --- a/src/Qir/riqir-test/FullstateSimulator/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -add_executable(fullstate-simulator-tests -../../Tests/FullstateSimulator/FullstateSimulatorTests.cpp) - -target_source_from_qir(fullstate-simulator-tests ../../Tests/FullstateSimulator/qsharp/obj/qsharp/qir-test-simulator.bc) - -target_link_libraries(fullstate-simulator-tests PUBLIC - "-L${simulator_lib_path}" - -lMicrosoft.Quantum.Simulator.Runtime -) - -target_include_directories(fullstate-simulator-tests PUBLIC - ${test_includes} - ${public_includes} -) -target_compile_definitions(fullstate-simulator-tests PRIVATE RIQIR_TESTING) - -install(TARGETS fullstate-simulator-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(fullstate-simulator-tests) - diff --git a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt b/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt deleted file mode 100644 index 0a47549335e..00000000000 --- a/src/Qir/riqir-test/QIR-dynamic/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -set(TEST_FILES -../../Tests/QIR-dynamic/qsharp/obj/qsharp/qir-test-random.bc -) - -#============================================================================== -# This executable target links test code against the dynamic libraries rather than the explicit -# static QIR/RT libs (qir will statically link in the bridge via transitivity of target_link_libraries). -# -add_executable(qir-dynamic-tests -../../Tests/QIR-dynamic/qir-driver.cpp -) - -foreach(file ${TEST_FILES}) - target_source_from_qir(qir-dynamic-tests ${file}) -endforeach() - -target_link_libraries(qir-dynamic-tests PUBLIC - "-L${simulator_lib_path}" - -lMicrosoft.Quantum.Simulator.Runtime -) - -target_include_directories(qir-dynamic-tests PUBLIC - ${test_includes} - ${public_includes} -) -target_compile_definitions(qir-dynamic-tests PRIVATE RIQIR_TESTING) - -install(TARGETS qir-dynamic-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-dynamic-tests) diff --git a/src/Qir/riqir-test/QIR-static/CMakeLists.txt b/src/Qir/riqir-test/QIR-static/CMakeLists.txt deleted file mode 100644 index e3da5d882f0..00000000000 --- a/src/Qir/riqir-test/QIR-static/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -set(TEST_FILES - ../../Tests/QIR-static/qsharp/qir/qir-gen.ll -) - -#============================================================================== -# The executable target for QIR tests triggers the custom actions to compile ll files -# -add_executable(qir-static-tests - ../../Tests/QIR-static/qir-driver.cpp - ../../Tests/QIR-static/qir-test-math.cpp - ../../Tests/QIR-static/qir-test-strings.cpp - ../../Tests/QIR-static/qir-test-other.cpp -) - -foreach(file ${TEST_FILES}) - target_source_from_qir(qir-static-tests ${file}) -endforeach() - -target_link_libraries(qir-static-tests PUBLIC - "-L${simulator_lib_path}" - -lMicrosoft.Quantum.Simulator.Runtime -) - -target_include_directories(qir-static-tests PUBLIC - ${test_includes} - ${common_includes} - ${public_includes} -) -target_compile_definitions(qir-static-tests PRIVATE RIQIR_TESTING) - -install(TARGETS qir-static-tests RUNTIME DESTINATION "${CMAKE_BINARY_DIR}/bin") -add_unit_test(qir-static-tests) - diff --git a/src/Qir/riqir-test/build-qir-tests.ps1 b/src/Qir/riqir-test/build-qir-tests.ps1 deleted file mode 100644 index ad3beddc51d..00000000000 --- a/src/Qir/riqir-test/build-qir-tests.ps1 +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -[CmdletBinding()] -param ( - [Parameter()] - [Switch] - $SkipQSharpBuild -) - -Write-Host "##[info]Compile Q# Test Projects into QIR" - -. (Join-Path $PSScriptRoot .. qir-utils.ps1) - -& (Join-Path $PSScriptRoot ".." check-sources-formatted.ps1) -Path $PSScriptRoot - -Build-QirProject (Join-Path $PSScriptRoot .. Tests QIR-static qsharp) -SkipQSharpBuild:$SkipQSharpBuild -Build-QirProject (Join-Path $PSScriptRoot .. Tests QIR-dynamic qsharp) -SkipQSharpBuild:$SkipQSharpBuild -Build-QirProject (Join-Path $PSScriptRoot .. Tests QIR-tracer qsharp) -SkipQSharpBuild:$SkipQSharpBuild -Build-QirProject (Join-Path $PSScriptRoot .. Tests FullstateSimulator qsharp) -SkipQSharpBuild:$SkipQSharpBuild - -if (-not (Build-CMakeProject $PSScriptRoot "RIQIR Tests")) { - throw "At least one project failed to compile. Check the logs." -} \ No newline at end of file diff --git a/src/Qir/riqir-test/test-qir-tests.ps1 b/src/Qir/riqir-test/test-qir-tests.ps1 deleted file mode 100644 index 2b7abdf3c19..00000000000 --- a/src/Qir/riqir-test/test-qir-tests.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -. (Join-Path $PSScriptRoot .. qir-utils.ps1) - -$oldLSAN_OPTIONS = $env:LSAN_OPTIONS -$oldASAN_OPTIONS = $env:ASAN_OPTIONS # Colon-separated list of options (https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags). - -$env:LSAN_OPTIONS = "" -$env:ASAN_OPTIONS = "" - -if ($Env:BUILD_CONFIGURATION -eq "Debug") -{ - # Sanitizers (https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation): - - if (-not ($IsWindows)) - { - $env:LSAN_OPTIONS += "suppressions=../../../../LSan.ignore" # https://clang.llvm.org/docs/AddressSanitizer.html#suppressing-memory-leaks - # TODO: macOS: `ASAN_OPTIONS=detect_leaks=1` (https://clang.llvm.org/docs/AddressSanitizer.html#memory-leak-detection). - $env:ASAN_OPTIONS = "check_initialization_order=true:detect_stack_use_after_return=true:" ` - + "alloc_dealloc_mismatch=true:new_delete_type_mismatch=true:strict_init_order=true:strict_string_checks=true" - # + ":detect_invalid_pointer_pairs=2" TODO(rokuzmin, #883): ==8218==ERROR: AddressSanitizer: invalid-pointer-pair: 0x602000000af4 0x602000000af0 - } -} - -$all_ok = $true - -if (-not (Test-CTest (Join-Path $PSScriptRoot bin $Env:BUILD_CONFIGURATION) "RIQIR Tests")) { - $all_ok = $false -} - -$env:ASAN_OPTIONS = $oldASAN_OPTIONS -$env:LSAN_OPTIONS = $oldLSAN_OPTIONS - -if (-not $all_ok) -{ - throw "At least one project failed testing. Check the logs." -} diff --git a/src/Qir/test_all.ps1 b/src/Qir/test_all.ps1 deleted file mode 100644 index fe2cb3d6658..00000000000 --- a/src/Qir/test_all.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -& Runtime/test-qir-runtime.ps1 -& Tests/test-qir-tests.ps1 -& Samples/test-qir-samples.ps1 From 7aaa60f09f327d3c889245ed330227c90bc94cca Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 7 Sep 2022 01:22:44 -0700 Subject: [PATCH 35/55] Use environment variables in native sim build --- src/Simulation/Native/build-native-simulator.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Simulation/Native/build-native-simulator.ps1 b/src/Simulation/Native/build-native-simulator.ps1 index 4ff668f9e71..d26528b8de3 100644 --- a/src/Simulation/Native/build-native-simulator.ps1 +++ b/src/Simulation/Native/build-native-simulator.ps1 @@ -3,6 +3,8 @@ Write-Host "##[info]Build Native simulator for $Env:BUILD_CONFIGURATION" +& (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) + if ($IsMacOS) { # To ensure loading succeeds on Mac the install id of the library needs to be updated to use # paths relative to target dynamic load path. Otherwise it will keep the full path encoding in the From 8021315d89656c967d440018591b2aa5217c5647 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 7 Sep 2022 11:22:28 -0700 Subject: [PATCH 36/55] Put back Tools tests --- .../Tests/Tools/QirDriverGeneratorTests.cs | 230 ++++++++++++++++++ .../Tools/QirExecutableGeneratorTests.cs | 105 ++++++++ src/Qir/Tests/Tools/QirExecutableTests.cs | 114 +++++++++ .../FullStateDriverGenerator/UseBoolArg.cpp | 59 +++++ .../UseBoolArrayArg.cpp | 84 +++++++ .../FullStateDriverGenerator/UseDoubleArg.cpp | 48 ++++ .../UseDoubleArrayArg.cpp | 73 ++++++ .../UseIntegerArg.cpp | 48 ++++ .../UseIntegerArrayArg.cpp | 73 ++++++ .../FullStateDriverGenerator/UseMiscArgs.cpp | 146 +++++++++++ .../FullStateDriverGenerator/UseNoArgs.cpp | 37 +++ .../FullStateDriverGenerator/UsePauliArg.cpp | 57 +++++ .../UsePauliArrayArg.cpp | 82 +++++++ .../FullStateDriverGenerator/UseRangeArg.cpp | 78 ++++++ .../UseRangeArrayArg.cpp | 116 +++++++++ .../FullStateDriverGenerator/UseResultArg.cpp | 59 +++++ .../UseResultArrayArg.cpp | 84 +++++++ .../FullStateDriverGenerator/UseStringArg.cpp | 53 ++++ ...Microsoft.Quantum.Qir.Runtime.Tools.csproj | 69 ++++++ src/Qir/Tests/Tools/Util.cs | 53 ++++ 20 files changed, 1668 insertions(+) create mode 100644 src/Qir/Tests/Tools/QirDriverGeneratorTests.cs create mode 100644 src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs create mode 100644 src/Qir/Tests/Tools/QirExecutableTests.cs create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp create mode 100644 src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp create mode 100644 src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj create mode 100644 src/Qir/Tests/Tools/Util.cs diff --git a/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs new file mode 100644 index 00000000000..ea65c7069e5 --- /dev/null +++ b/src/Qir/Tests/Tools/QirDriverGeneratorTests.cs @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.IO; +using System.Text; + +using Xunit; + +using Microsoft.Quantum.Qir.Runtime.Tools.Driver; +using Microsoft.Quantum.Qir.Serialization; + +namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools +{ + public class QirDriverGeneratorTests + { + private static string TestArtifactsDirectory = Path.Combine("TestArtifacts", "FullStateDriverGenerator"); + private static string TestCasesDirectory = Path.Combine("TestCases", "FullStateDriverGenerator"); + private static IDictionary TestCases = + new Dictionary + { + { + "UseNoArgs", + new EntryPointOperation{Name = "UseNoArgs"} + }, + { + "UseBoolArg", + new EntryPointOperation + { + Name = "UseBoolArg", + Parameters = new List{new Parameter{ Name = "BoolArg", Type = DataType.BoolType}} + } + }, + { + "UseBoolArrayArg", + new EntryPointOperation + { + Name = "UseBoolArrayArg", + Parameters = new List{new Parameter{ Name = "BoolArrayArg", Type = DataType.ArrayType, ArrayType = DataType.BoolType}} + } + }, + { + "UseDoubleArg", + new EntryPointOperation + { + Name = "UseDoubleArg", + Parameters = new List{new Parameter{ Name = "DoubleArg", Type = DataType.DoubleType}} + } + }, + { + "UseDoubleArrayArg", + new EntryPointOperation + { + Name = "UseDoubleArrayArg", + Parameters = new List{new Parameter{ Name = "DoubleArrayArg", Type = DataType.ArrayType, ArrayType = DataType.DoubleType}} + } + }, + { + "UseIntegerArg", + new EntryPointOperation + { + Name = "UseIntegerArg", + Parameters = new List{new Parameter{ Name = "IntegerArg", Type = DataType.IntegerType}} + } + }, + { + "UseIntegerArrayArg", + new EntryPointOperation + { + Name = "UseIntegerArrayArg", + Parameters = new List{new Parameter{ Name = "IntegerArrayArg", Type = DataType.ArrayType, ArrayType = DataType.IntegerType}} + } + }, + { + "UsePauliArg", + new EntryPointOperation + { + Name = "UsePauliArg", + Parameters = new List{new Parameter{ Name = "PauliArg", Type = DataType.PauliType}} + } + }, + { + "UsePauliArrayArg", + new EntryPointOperation + { + Name = "UsePauliArrayArg", + Parameters = new List{new Parameter{ Name = "PauliArrayArg", Type = DataType.ArrayType, ArrayType = DataType.PauliType}} + } + }, + { + "UseRangeArg", + new EntryPointOperation + { + Name = "UseRangeArg", + Parameters = new List{new Parameter{ Name = "RangeArg", Type = DataType.RangeType}} + } + }, + { + "UseRangeArrayArg", + new EntryPointOperation + { + Name = "UseRangeArrayArg", + Parameters = new List{new Parameter{ Name = "RangeArrayArg", Type = DataType.ArrayType, ArrayType = DataType.RangeType}} + } + }, + { + "UseResultArg", + new EntryPointOperation + { + Name = "UseResultArg", + Parameters = new List{new Parameter{ Name = "ResultArg", Type = DataType.ResultType}} + } + }, + { + "UseResultArrayArg", + new EntryPointOperation + { + Name = "UseResultArrayArg", + Parameters = new List{new Parameter{ Name = "ResultArrayArg", Type = DataType.ArrayType, ArrayType = DataType.ResultType}} + } + }, + { + "UseStringArg", + new EntryPointOperation + { + Name = "UseStringArg", + Parameters = new List{new Parameter{ Name = "StringArg", Type = DataType.StringType}} + } + }, + { + "UseMiscArgs", + new EntryPointOperation + { + Name = "UseMiscArgs", + Parameters = new List{ + new Parameter{ Name = "BoolArg", Type = DataType.BoolType}, + new Parameter{ Name = "IntegerArrayArg", Position = 1, Type = DataType.ArrayType, ArrayType = DataType.IntegerType}, + new Parameter{ Name = "RangeArg", Position = 2, Type = DataType.RangeType}, + new Parameter{ Name = "StringArg", Position = 3, Type = DataType.StringType} + } + } + } + }; + + private static string RemoveLineEndings(string str) => + str.Replace("\n", string.Empty).Replace("\r", string.Empty); + + [Theory] + [InlineData("UseNoArgs")] + [InlineData("UseBoolArg")] + [InlineData("UseBoolArrayArg")] + [InlineData("UseDoubleArg")] + [InlineData("UseDoubleArrayArg")] + [InlineData("UseIntegerArg")] + [InlineData("UseIntegerArrayArg")] + [InlineData("UsePauliArg")] + [InlineData("UsePauliArrayArg")] + [InlineData("UseRangeArg")] + [InlineData("UseRangeArrayArg")] + [InlineData("UseResultArg")] + [InlineData("UseResultArrayArg")] + [InlineData("UseStringArg")] + [InlineData("UseMiscArgs")] + public void GenerateFullStateSimulatorDriver(string testCase) + { + var entryPointOperation = TestCases[testCase]; + var driverGenerator = new QirFullStateDriverGenerator(); + var driverFileName = $"{testCase}.cpp"; + var verificationCppSourceCode = RemoveLineEndings(File.ReadAllText(Path.Combine(TestCasesDirectory, driverFileName))); + Directory.CreateDirectory(TestArtifactsDirectory); + var generatedStream = File.Create(Path.Combine(TestArtifactsDirectory, driverFileName)); + driverGenerator.GenerateAsync(entryPointOperation, generatedStream).Wait(); + var generatedStreamReader = new StreamReader(generatedStream, Encoding.UTF8); + var generatedCppSourceCode = RemoveLineEndings(generatedStreamReader.ReadToEnd()); + Assert.Equal(verificationCppSourceCode, generatedCppSourceCode); + generatedStream.Close(); + } + + [Theory] + [MemberData(nameof(CommandLineArgumentsData))] + public void GenerateCommandLineArguments(string expected, ExecutionInformation info) + { + var generator = new QirFullStateDriverGenerator(); + Assert.Equal(expected, generator.GetCommandLineArguments(info)); + } + + public static IEnumerable CommandLineArgumentsData + { + get + { + static object[] TestCase( + string expected, List parameters, Dictionary arguments) => + new object[] + { + expected, + new ExecutionInformation + { + EntryPoint = new EntryPointOperation { Parameters = parameters }, + ArgumentValues = arguments + } + }; + + yield return TestCase( + "--foo 5", + new List { new Parameter { Name = "foo", Position = 0, Type = DataType.IntegerType } }, + new Dictionary + { ["foo"] = new ArgumentValue { Type = DataType.IntegerType, Integer = 5 } }); + + yield return TestCase( + "-n 5", + new List { new Parameter { Name = "n", Position = 0, Type = DataType.IntegerType } }, + new Dictionary + { ["n"] = new ArgumentValue { Type = DataType.IntegerType, Integer = 5 } }); + + yield return TestCase( + "--foo 5 --bar \"abc\"", + new List + { + new Parameter { Name = "bar", Position = 1, Type = DataType.StringType }, + new Parameter { Name = "foo", Position = 0, Type = DataType.IntegerType } + }, + new Dictionary + { + ["bar"] = new ArgumentValue { Type = DataType.StringType, String = "abc" }, + ["foo"] = new ArgumentValue { Type = DataType.IntegerType, Integer = 5 } + }); + } + } + } +} diff --git a/src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs b/src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs new file mode 100644 index 00000000000..3b916ffdb25 --- /dev/null +++ b/src/Qir/Tests/Tools/QirExecutableGeneratorTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Runtime.Tools.Executable; +using Moq; +using Xunit; + +namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools +{ + public class QirExecutableGeneratorTests : IDisposable + { + private readonly Mock clangClientMock; + private readonly QirExecutableGenerator executableGenerator; + private readonly DirectoryInfo sourceDirectory; + private readonly DirectoryInfo includeDirectory; + private readonly DirectoryInfo libraryDirectory; + private readonly DirectoryInfo binDirectory; + private readonly IList libraryFiles; + private readonly IList sourceFiles; + private IList linkLibraries; + + public QirExecutableGeneratorTests() + { + clangClientMock = new Mock(); + executableGenerator = new QirExecutableGenerator(clangClientMock.Object, null); + + // Set up files. + var prefix = Guid.NewGuid().ToString(); + binDirectory = new DirectoryInfo($"{prefix}-bin"); + binDirectory.Create(); + libraryDirectory = new DirectoryInfo($"{prefix}-library"); + libraryDirectory.Create(); + libraryFiles = new List() + { + Util.CreateBinaryFile(libraryDirectory, "lib1", new byte[]{0x01, 0x23, 0x45, 0x67}), + Util.CreateBinaryFile(libraryDirectory, "lib2", new byte[]{0x89, 0xAB, 0xCD, 0xEF}), + }; + includeDirectory = new DirectoryInfo($"{prefix}-include"); + includeDirectory.Create(); + sourceDirectory = new DirectoryInfo($"{prefix}-source"); + sourceDirectory.Create(); + sourceFiles = new List() + { + Util.CreateTextFile(sourceDirectory, "src1.cpp", "src1 contents"), + Util.CreateBinaryFile(sourceDirectory, "src2.bc", new byte[]{0xFE, 0xDC, 0xBA, 0x98}), + }; + linkLibraries = new List { "lib1", "lib2" }; + } + + public void Dispose() + { + sourceDirectory.Delete(true); + includeDirectory.Delete(true); + libraryDirectory.Delete(true); + binDirectory.Delete(true); + } + + [Fact] + public async Task TestGenerateExecutable() + { + var executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executableFile")); + await executableGenerator.GenerateExecutableAsync(executableFile, sourceDirectory, new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries); + + // Verify invocation of clang. + clangClientMock.Verify(obj => obj.CreateExecutableAsync( + It.Is(s => s.OrderBy(val => val).SequenceEqual(sourceFiles.OrderBy(val => val.FullName).Select(fileInfo => fileInfo.FullName))), + It.Is(s => s.OrderBy(val => val).SequenceEqual(linkLibraries.OrderBy(val => val))), + new[] { libraryDirectory.FullName }, + new[] { includeDirectory.FullName }, + executableFile.FullName)); + + // Verify files were copied. + Assert.True(FilesWereCopied(libraryFiles.ToArray(), binDirectory)); + } + + private static bool FilesWereCopied(FileInfo[] files, DirectoryInfo destinationDirectory) + { + var destinationDirectoryFiles = destinationDirectory.GetFiles(); + + // N.B. Runtime is (number of FileInfo objects passed in) * (number of files in destination directory). + foreach (var file in files) + { + // Make sure that the file was copied and that the contents is the same. + var copiedFile = destinationDirectoryFiles.FirstOrDefault(fileInfo => fileInfo.Name == file.Name); + if (copiedFile == null) + { + return false; + } + + if (!Util.CompareFiles(file, copiedFile)) + { + return false; + } + + } + + return true; + } + } +} diff --git a/src/Qir/Tests/Tools/QirExecutableTests.cs b/src/Qir/Tests/Tools/QirExecutableTests.cs new file mode 100644 index 00000000000..23881978475 --- /dev/null +++ b/src/Qir/Tests/Tools/QirExecutableTests.cs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Quantum.Qir.Serialization; +using Microsoft.Quantum.Qir.Runtime.Tools.Driver; +using Microsoft.Quantum.Qir.Runtime.Tools.Executable; +using Moq; +using Xunit; + +namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools +{ + public class QirExecutableTests : IDisposable + { + private readonly DirectoryInfo sourceDirectory; + private readonly DirectoryInfo includeDirectory; + private readonly DirectoryInfo libraryDirectory; + private readonly DirectoryInfo binDirectory; + private readonly Mock driverGeneratorMock; + private readonly Mock executableGeneratorMock; + private readonly Mock runnerMock; + private readonly Mock qirExecutable; + private readonly FileInfo executableFile; + private readonly byte[] qirBytecode = { 1, 2, 3, 4, 5 }; + private readonly IList linkLibraries; + private readonly IList headerDirectories; + private readonly IList libraryDirectories; + + public QirExecutableTests() + { + // Set up files. + var prefix = Guid.NewGuid().ToString(); + binDirectory = new DirectoryInfo($"{prefix}-bin"); + binDirectory.Create(); + libraryDirectory = new DirectoryInfo($"{prefix}-library"); + libraryDirectory.Create(); + includeDirectory = new DirectoryInfo($"{prefix}-include"); + includeDirectory.Create(); + sourceDirectory = new DirectoryInfo($"{prefix}-source"); + sourceDirectory.Create(); + executableFile = new FileInfo(Path.Combine(binDirectory.FullName, "executable")); + driverGeneratorMock = new Mock(); + executableGeneratorMock = new Mock(); + runnerMock = new Mock(); + qirExecutable = new Mock(executableFile, qirBytecode, driverGeneratorMock.Object, executableGeneratorMock.Object, runnerMock.Object, null) { CallBase = true }; + linkLibraries = new List { "lib1", "lib2" }; + headerDirectories = new List(); + libraryDirectories = new List(); + qirExecutable.SetupGet(obj => obj.LinkLibraries).Returns(linkLibraries); + qirExecutable.SetupGet(obj => obj.SourceDirectoryPath).Returns(sourceDirectory.FullName); + qirExecutable.SetupGet(obj => obj.DriverFileExtension).Returns(".cpp"); + qirExecutable.SetupGet(obj => obj.HeaderDirectories).Returns(headerDirectories); + qirExecutable.SetupGet(obj => obj.LibraryDirectories).Returns(libraryDirectories); + } + + public void Dispose() + { + sourceDirectory.Delete(true); + includeDirectory.Delete(true); + libraryDirectory.Delete(true); + binDirectory.Delete(true); + } + + [Fact] + public async Task TestBuild() + { + // Set up. + var entryPoint = new EntryPointOperation(); + var driverFileContents = "driver file contents"; + driverGeneratorMock.Setup(obj => obj.GenerateAsync(entryPoint, It.IsAny())).Callback((entryPoint, stream) => + { + using var streamWriter = new StreamWriter(stream); + streamWriter.Write(driverFileContents); + }); + + // Build the executable. + await qirExecutable.Object.BuildAsync(entryPoint, new[] { libraryDirectory }, new[] { includeDirectory }); + + // Verify that the "bytecode" file was created correctly. + var bytecodeFilePath = new FileInfo(Path.Combine(sourceDirectory.FullName, "qir.bc")); + using var bytecodeFileStream = bytecodeFilePath.OpenRead(); + Assert.True(Util.CompareStreams(new MemoryStream(qirBytecode), bytecodeFileStream)); + + // Verify that the driver was written to the correct file. + var driver = new FileInfo(Path.Combine(sourceDirectory.FullName, "driver.cpp")); + using var driverStreamReader = driver.OpenText(); + var actualDriverContents = driverStreamReader.ReadToEnd(); + Assert.Equal(driverFileContents, actualDriverContents); + + // Verify that the executable was generated. + executableGeneratorMock.Verify(obj => obj.GenerateExecutableAsync(executableFile, It.Is(arg => arg.FullName == sourceDirectory.FullName), new[] { libraryDirectory }, new[] { includeDirectory }, linkLibraries)); + } + + [Fact] + public async Task TestRun() + { + // Set up. + using var outputStream = new MemoryStream(); + var execInfo = new ExecutionInformation(); + var arguments = "arguments"; + driverGeneratorMock.Setup(obj => obj.GetCommandLineArguments(execInfo)).Returns(arguments); + + // Run executable. + await qirExecutable.Object.RunAsync(execInfo, outputStream); + + // Verify runner was invoked properly. + runnerMock.Verify(obj => obj.RunExecutableAsync(executableFile, outputStream, arguments)); + } + } +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp new file mode 100644 index 00000000000..1855912aa69 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp @@ -0,0 +1,59 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map BoolAsCharMap{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; + +extern "C" void UseBoolArg( + char BoolArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + char BoolArgCli; + BoolArgCli = InteropFalseAsChar; + app.add_option("--BoolArg", BoolArgCli, "Option to provide a value for the BoolArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char BoolArgInterop = BoolArgCli; + + // Execute the entry point operation. + UseBoolArg( + BoolArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp new file mode 100644 index 00000000000..5bfbc30196b --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp @@ -0,0 +1,84 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map BoolAsCharMap{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; + +extern "C" void UseBoolArrayArg( + InteropArray* BoolArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + vector BoolArrayArgCli; + app.add_option("--BoolArrayArg", BoolArrayArgCli, "Option to provide a value for the BoolArrayArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr BoolArrayArgUniquePtr = CreateInteropArray(BoolArrayArgCli); + InteropArray* BoolArrayArgInterop = BoolArrayArgUniquePtr.get(); + + // Execute the entry point operation. + UseBoolArrayArg( + BoolArrayArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp new file mode 100644 index 00000000000..6eefd32abbe --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp @@ -0,0 +1,48 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +extern "C" void UseDoubleArg( + double_t DoubleArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + double_t DoubleArgCli; + DoubleArgCli = 0.0; + app.add_option("--DoubleArg", DoubleArgCli, "Option to provide a value for the DoubleArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + double_t DoubleArgInterop = DoubleArgCli; + + // Execute the entry point operation. + UseDoubleArg( + DoubleArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp new file mode 100644 index 00000000000..d6593923e71 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp @@ -0,0 +1,73 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +extern "C" void UseDoubleArrayArg( + InteropArray* DoubleArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + vector DoubleArrayArgCli; + app.add_option("--DoubleArrayArg", DoubleArrayArgCli, "Option to provide a value for the DoubleArrayArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr DoubleArrayArgUniquePtr = CreateInteropArray(DoubleArrayArgCli); + InteropArray* DoubleArrayArgInterop = DoubleArrayArgUniquePtr.get(); + + // Execute the entry point operation. + UseDoubleArrayArg( + DoubleArrayArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp new file mode 100644 index 00000000000..d6e6ebfd3f1 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp @@ -0,0 +1,48 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +extern "C" void UseIntegerArg( + int64_t IntegerArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + int64_t IntegerArgCli; + IntegerArgCli = 0; + app.add_option("--IntegerArg", IntegerArgCli, "Option to provide a value for the IntegerArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + int64_t IntegerArgInterop = IntegerArgCli; + + // Execute the entry point operation. + UseIntegerArg( + IntegerArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp new file mode 100644 index 00000000000..73ef2abbbe6 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp @@ -0,0 +1,73 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +extern "C" void UseIntegerArrayArg( + InteropArray* IntegerArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + vector IntegerArrayArgCli; + app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); + InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); + + // Execute the entry point operation. + UseIntegerArrayArg( + IntegerArrayArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp new file mode 100644 index 00000000000..c43b4506176 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp @@ -0,0 +1,146 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +struct InteropRange +{ + int64_t Start; + int64_t Step; + int64_t End; + + InteropRange() : + Start(0), + Step(0), + End(0){} + + InteropRange(RangeTuple rangeTuple) : + Start(get<0>(rangeTuple)), + Step(get<1>(rangeTuple)), + End(get<2>(rangeTuple)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} + +// Auxiliary functions for interop with Q# Bool type. +const char InteropFalseAsChar = 0x0; +const char InteropTrueAsChar = 0x1; +map BoolAsCharMap{ + {"0", InteropFalseAsChar}, + {"false", InteropFalseAsChar}, + {"1", InteropTrueAsChar}, + {"true", InteropTrueAsChar} +}; + +// Auxiliary functions for interop with Q# String type. +const char* TranslateStringToCharBuffer(string& s) +{ + return s.c_str(); +} + +extern "C" void UseMiscArgs( + char BoolArg, + InteropArray* IntegerArrayArg, + InteropRange* RangeArg, + const char* StringArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + char BoolArgCli; + BoolArgCli = InteropFalseAsChar; + app.add_option("--BoolArg", BoolArgCli, "Option to provide a value for the BoolArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(BoolAsCharMap, CLI::ignore_case)); + + vector IntegerArrayArgCli; + app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") + ->required(); + + RangeTuple RangeArgCli; + app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") + ->required(); + + string StringArgCli; + app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char BoolArgInterop = BoolArgCli; + + unique_ptr IntegerArrayArgUniquePtr = CreateInteropArray(IntegerArrayArgCli); + InteropArray* IntegerArrayArgInterop = IntegerArrayArgUniquePtr.get(); + + InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); + + const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); + + // Execute the entry point operation. + UseMiscArgs( + BoolArgInterop, + IntegerArrayArgInterop, + RangeArgInterop, + StringArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp new file mode 100644 index 00000000000..56061656927 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp @@ -0,0 +1,37 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +extern "C" void UseNoArgs( +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Execute the entry point operation. + UseNoArgs( + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp new file mode 100644 index 00000000000..cb923423b76 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp @@ -0,0 +1,57 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Pauli type. +map PauliMap{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} +}; + +extern "C" void UsePauliArg( + char PauliArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + char PauliArgCli; + PauliArgCli = 0; + app.add_option("--PauliArg", PauliArgCli, "Option to provide a value for the PauliArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char PauliArgInterop = PauliArgCli; + + // Execute the entry point operation. + UsePauliArg( + PauliArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp new file mode 100644 index 00000000000..c754efbb865 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp @@ -0,0 +1,82 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Pauli type. +map PauliMap{ + {"PauliI", 0}, + {"PauliX", 1}, + {"PauliY", 3}, + {"PauliZ", 2} +}; + +extern "C" void UsePauliArrayArg( + InteropArray* PauliArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + vector PauliArrayArgCli; + app.add_option("--PauliArrayArg", PauliArrayArgCli, "Option to provide a value for the PauliArrayArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(PauliMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr PauliArrayArgUniquePtr = CreateInteropArray(PauliArrayArgCli); + InteropArray* PauliArrayArgInterop = PauliArrayArgUniquePtr.get(); + + // Execute the entry point operation. + UsePauliArrayArg( + PauliArrayArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp new file mode 100644 index 00000000000..e05012edb4a --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp @@ -0,0 +1,78 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +struct InteropRange +{ + int64_t Start; + int64_t Step; + int64_t End; + + InteropRange() : + Start(0), + Step(0), + End(0){} + + InteropRange(RangeTuple rangeTuple) : + Start(get<0>(rangeTuple)), + Step(get<1>(rangeTuple)), + End(get<2>(rangeTuple)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} + +extern "C" void UseRangeArg( + InteropRange* RangeArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + RangeTuple RangeArgCli; + app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + InteropRange* RangeArgInterop = TranslateRangeTupleToInteropRangePointer(RangeArgCli); + + // Execute the entry point operation. + UseRangeArg( + RangeArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp new file mode 100644 index 00000000000..2ea36be72c2 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp @@ -0,0 +1,116 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Range type. +using RangeTuple = tuple; +struct InteropRange +{ + int64_t Start; + int64_t Step; + int64_t End; + + InteropRange() : + Start(0), + Step(0), + End(0){} + + InteropRange(RangeTuple rangeTuple) : + Start(get<0>(rangeTuple)), + Step(get<1>(rangeTuple)), + End(get<2>(rangeTuple)){} +}; + +unique_ptr CreateInteropRange(RangeTuple rangeTuple) +{ + unique_ptr range(new InteropRange(rangeTuple)); + return range; +} + +InteropRange* TranslateRangeTupleToInteropRangePointer(RangeTuple& rangeTuple) +{ + InteropRange* range = new InteropRange(rangeTuple); + return range; +} + +// Auxiliary functions for interop with Q# Range[] type +template +void FreePointerVector(vector& v) +{ + for (auto p : v) + { + delete p; + } +} + +extern "C" void UseRangeArrayArg( + InteropArray* RangeArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + vector RangeArrayArgCli; + app.add_option("--RangeArrayArg", RangeArrayArgCli, "Option to provide a value for the RangeArrayArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + vector RangeArrayArgIntermediate; + TranslateVector(RangeArrayArgCli, RangeArrayArgIntermediate, TranslateRangeTupleToInteropRangePointer); + unique_ptr RangeArrayArgUniquePtr = CreateInteropArray(RangeArrayArgIntermediate); + InteropArray* RangeArrayArgInterop = RangeArrayArgUniquePtr.get(); + + // Execute the entry point operation. + UseRangeArrayArg( + RangeArrayArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp new file mode 100644 index 00000000000..c3757bff52f --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp @@ -0,0 +1,59 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Result type. +const char InteropResultZeroAsChar = 0x0; +const char InteropResultOneAsChar = 0x1; +map ResultAsCharMap{ + {"0", InteropResultZeroAsChar}, + {"Zero", InteropResultZeroAsChar}, + {"1", InteropResultOneAsChar}, + {"One", InteropResultOneAsChar} +}; + +extern "C" void UseResultArg( + char ResultArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + char ResultArgCli; + ResultArgCli = InteropResultZeroAsChar; + app.add_option("--ResultArg", ResultArgCli, "Option to provide a value for the ResultArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + char ResultArgInterop = ResultArgCli; + + // Execute the entry point operation. + UseResultArg( + ResultArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp new file mode 100644 index 00000000000..1bc6992c838 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp @@ -0,0 +1,84 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# Array type. +struct InteropArray +{ + int64_t Size; + void* Data; + + InteropArray(int64_t size, void* data) : + Size(size), + Data(data){} +}; + +template +unique_ptr CreateInteropArray(vector& v) +{ + unique_ptr array(new InteropArray(v.size(), v.data())); + return array; +} + +template +void TranslateVector(vector& sourceVector, vector& destinationVector, function translationFunction) +{ + destinationVector.resize(sourceVector.size()); + transform(sourceVector.begin(), sourceVector.end(), destinationVector.begin(), translationFunction); +} + +// Auxiliary functions for interop with Q# Result type. +const char InteropResultZeroAsChar = 0x0; +const char InteropResultOneAsChar = 0x1; +map ResultAsCharMap{ + {"0", InteropResultZeroAsChar}, + {"Zero", InteropResultZeroAsChar}, + {"1", InteropResultOneAsChar}, + {"One", InteropResultOneAsChar} +}; + +extern "C" void UseResultArrayArg( + InteropArray* ResultArrayArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + vector ResultArrayArgCli; + app.add_option("--ResultArrayArg", ResultArrayArgCli, "Option to provide a value for the ResultArrayArg parameter") + ->required() + ->transform(CLI::CheckedTransformer(ResultAsCharMap, CLI::ignore_case)); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + unique_ptr ResultArrayArgUniquePtr = CreateInteropArray(ResultArrayArgCli); + InteropArray* ResultArrayArgInterop = ResultArrayArgUniquePtr.get(); + + // Execute the entry point operation. + UseResultArrayArg( + ResultArrayArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp new file mode 100644 index 00000000000..a1908a9c045 --- /dev/null +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp @@ -0,0 +1,53 @@ +//---------------------------------------------------------------------------------------------------------------------- +// +// This code was generated by the Microsoft.Quantum.Qir.Runtime.Tools package. +// The purpose of this source code file is to provide an entry-point for executing a QIR program. +// It handles parsing of command line arguments, and it invokes an entry-point function exposed by the QIR program. +//---------------------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "CLI11.hpp" + +using namespace std; + +// Auxiliary functions for interop with Q# String type. +const char* TranslateStringToCharBuffer(string& s) +{ + return s.c_str(); +} + +extern "C" void UseStringArg( + const char* StringArg +); // QIR interop function. + +int main(int argc, char* argv[]) +{ + CLI::App app("QIR Standalone Entry Point"); + + // Initialize runtime. + // Add a command line option for each entry-point parameter. + string StringArgCli; + app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") + ->required(); + + // After all the options have been added, parse arguments from the command line. + CLI11_PARSE(app, argc, argv); + + // Cast parsed arguments to its interop types. + const char* StringArgInterop = TranslateStringToCharBuffer(StringArgCli); + + // Execute the entry point operation. + UseStringArg( + StringArgInterop + ); + + // Flush the output of the simulation. + cout.flush(); + + return 0; +} diff --git a/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj new file mode 100644 index 00000000000..f74161bb917 --- /dev/null +++ b/src/Qir/Tests/Tools/Tests.Microsoft.Quantum.Qir.Runtime.Tools.csproj @@ -0,0 +1,69 @@ + + + + x64 + net6.0 + false + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + + diff --git a/src/Qir/Tests/Tools/Util.cs b/src/Qir/Tests/Tools/Util.cs new file mode 100644 index 00000000000..5783df11d3a --- /dev/null +++ b/src/Qir/Tests/Tools/Util.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.IO; + +namespace Tests.Microsoft.Quantum.Qir.Runtime.Tools +{ + internal static class Util + { + public static bool CompareFiles(FileInfo fileA, FileInfo fileB) + { + if (fileA.FullName == fileB.FullName) + { + return true; + } + + using var fileStreamA = fileA.OpenRead(); + using var fileStreamB = fileB.OpenRead(); + return CompareStreams(fileStreamA, fileStreamB); + } + + public static bool CompareStreams(Stream streamA, Stream streamB) + { + if (streamA.Length != streamB.Length) + { + return false; + } + + (streamA.Position, streamB.Position) = (0, 0); + (int byteA, int byteB) = (0, 0); + while ((byteA == byteB) && (byteA != -1)) + { + (byteA, byteB) = (streamA.ReadByte(), streamB.ReadByte()); + } + + return byteA == byteB; + } + + public static FileInfo CreateBinaryFile(DirectoryInfo directory, string fileName, byte[] contents) + { + var fileInfo = new FileInfo(Path.Combine(directory.FullName, fileName)); + File.WriteAllBytes(fileInfo.FullName, contents); + return fileInfo; + } + + public static FileInfo CreateTextFile(DirectoryInfo directory, string fileName, string contents) + { + var fileInfo = new FileInfo(Path.Combine(directory.FullName, fileName)); + File.WriteAllText(fileInfo.FullName, contents); + return fileInfo; + } + } +} From 11f0670e15b721d8c86ce051d35aa20e921295b0 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 7 Sep 2022 11:28:01 -0700 Subject: [PATCH 37/55] Remove tracer test from solution --- Simulation.sln | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Simulation.sln b/Simulation.sln index 0259b6f8eff..eac73692ab4 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -93,10 +93,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-static", "QIR-static", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "qir-gen", "src\Qir\Tests\QIR-static\qsharp\qir-gen.csproj", "{33ED37AB-61B1-4A49-9952-58D19AA8EF30}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QIR-tracer", "QIR-tracer", "{522EAA31-6317-42D5-831F-C39313DF03A0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tracer-qir", "src\Qir\Tests\QIR-tracer\qsharp\tracer-qir.csproj", "{002174F4-BFA8-4675-908D-0E9C32ED951A}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{AAFB81D3-BC87-404D-BA64-AF40B2D2E45A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandaloneInputReference", "StandaloneInputReference", "{A7DB7367-9FD6-4164-8263-A05077BE54AB}" @@ -772,22 +768,6 @@ Global {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU {33ED37AB-61B1-4A49-9952-58D19AA8EF30}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|x64.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Debug|x64.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.MinSizeRel|x64.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|Any CPU.Build.0 = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|x64.ActiveCfg = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.Release|x64.Build.0 = Release|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU - {002174F4-BFA8-4675-908D-0E9C32ED951A}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1039,8 +1019,6 @@ Global {5DBF6402-D9CD-4470-A309-3755CFDA42CF} = {3DFACF7F-D5C2-455B-AB8A-26908DEF7E2D} {54000816-122C-4AA0-9FE9-B0ABB9547232} = {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} {33ED37AB-61B1-4A49-9952-58D19AA8EF30} = {54000816-122C-4AA0-9FE9-B0ABB9547232} - {522EAA31-6317-42D5-831F-C39313DF03A0} = {7F7BB60A-5DCB-469E-8546-1BE9E3CAC833} - {002174F4-BFA8-4675-908D-0E9C32ED951A} = {522EAA31-6317-42D5-831F-C39313DF03A0} {AAFB81D3-BC87-404D-BA64-AF40B2D2E45A} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {A7DB7367-9FD6-4164-8263-A05077BE54AB} = {AAFB81D3-BC87-404D-BA64-AF40B2D2E45A} {D7D34736-A719-4B45-A33F-2723F59EC29D} = {A7DB7367-9FD6-4164-8263-A05077BE54AB} From 3f38b9e118df2fa9c4c4bd22963ae029be448d9f Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 7 Sep 2022 16:19:29 -0700 Subject: [PATCH 38/55] Remove unused qir_backend --- Cargo.toml | 1 - src/Qir/Runtime/backend/Cargo.toml | 20 - .../Runtime/backend/src/common_matrices.rs | 283 ---- src/Qir/Runtime/backend/src/lib.rs | 969 -------------- src/Qir/Runtime/backend/src/nearly_zero.rs | 24 - src/Qir/Runtime/backend/src/result_bool.rs | 44 - src/Qir/Runtime/backend/src/simulator.rs | 1165 ----------------- src/Qir/Runtime/build-qir-stdlib.ps1 | 102 +- src/Qir/Runtime/test-qir-stdlib.ps1 | 40 +- 9 files changed, 69 insertions(+), 2579 deletions(-) delete mode 100644 src/Qir/Runtime/backend/Cargo.toml delete mode 100644 src/Qir/Runtime/backend/src/common_matrices.rs delete mode 100644 src/Qir/Runtime/backend/src/lib.rs delete mode 100644 src/Qir/Runtime/backend/src/nearly_zero.rs delete mode 100644 src/Qir/Runtime/backend/src/result_bool.rs delete mode 100644 src/Qir/Runtime/backend/src/simulator.rs diff --git a/Cargo.toml b/Cargo.toml index 697c8c46c0f..fccdcff7ac1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "src/Simulation/qdk_sim_rs", "src/Qir/Runtime/stdlib", - "src/Qir/Runtime/backend", ] [profile.release] diff --git a/src/Qir/Runtime/backend/Cargo.toml b/src/Qir/Runtime/backend/Cargo.toml deleted file mode 100644 index 9a887cbb501..00000000000 --- a/src/Qir/Runtime/backend/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "qir-backend" -version = "0.1.0" -authors = ["Microsoft"] -edition = "2021" - -[dependencies] -qir-stdlib = { path = "../stdlib" } -rand = "0.8.5" -rustc-hash = "1.1.0" -num-complex = "0.4" -num-traits = "0.2.14" -num-bigint = { version = "0.4.3", default-features = false } -rayon = "1.5.1" -ndarray = { version = "0.15.4", features = [ "rayon", "matrixmultiply-threading" ] } -lazy_static = "1.4.0" -bitvec = "1.0.0" - -[lib] -crate-type = ["cdylib","rlib"] diff --git a/src/Qir/Runtime/backend/src/common_matrices.rs b/src/Qir/Runtime/backend/src/common_matrices.rs deleted file mode 100644 index cfdf8ee826a..00000000000 --- a/src/Qir/Runtime/backend/src/common_matrices.rs +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use core::f64::consts::FRAC_1_SQRT_2; -use ndarray::{array, s, Array2}; -use num_complex::Complex64; -use num_traits::{One, Zero}; - -/// Returns a unitary matrix representing the `X` operation. -#[cfg(test)] -#[must_use] -pub fn x() -> Array2 { - array![ - [Complex64::zero(), Complex64::one()], - [Complex64::one(), Complex64::zero()] - ] -} - -/// Returns a unitary matrix representing the `Y` operation. -#[cfg(test)] -#[must_use] -pub fn y() -> Array2 { - array![ - [Complex64::zero(), -Complex64::i()], - [Complex64::i(), Complex64::zero()] - ] -} - -/// Returns a unitary matrix representing the `Z` operation. -#[cfg(test)] -#[must_use] -pub fn z() -> Array2 { - array![ - [Complex64::one(), Complex64::zero()], - [Complex64::zero(), -Complex64::one()] - ] -} - -/// Returns a unitary matrix representing the single-qubit Hadamard transformation. -#[must_use] -pub fn h() -> Array2 { - array![ - [Complex64::one(), Complex64::one()], - [Complex64::one(), -Complex64::one()] - ] * FRAC_1_SQRT_2 -} - -/// Returns a unitary matrix representing the `T` operation. -#[must_use] -pub fn t() -> Array2 { - array![ - [Complex64::one(), Complex64::zero()], - [ - Complex64::zero(), - Complex64::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2) - ] - ] -} - -/// Returns a unitary matrix representing the `S` operation. -#[must_use] -pub fn s() -> Array2 { - array![ - [Complex64::one(), Complex64::zero()], - [Complex64::zero(), Complex64::new(0.0_f64, 1.0_f64)] - ] -} - -/// Returns a unitary matrix representing the `Rx` operation with the given angle. -#[must_use] -pub fn rx(theta: f64) -> Array2 { - let cos_theta = f64::cos(theta / 2.0); - let sin_theta = f64::sin(theta / 2.0); - array![ - [ - Complex64::new(cos_theta, 0.0), - Complex64::new(0.0, -sin_theta) - ], - [ - Complex64::new(0.0, -sin_theta), - Complex64::new(cos_theta, 0.0) - ] - ] -} - -/// Returns a unitary matrix representing the `Ry` operation with the given angle. -#[must_use] -pub fn ry(theta: f64) -> Array2 { - let cos_theta = f64::cos(theta / 2.0); - let sin_theta = f64::sin(theta / 2.0); - array![ - [ - Complex64::new(cos_theta, 0.0), - Complex64::new(-sin_theta, 0.0) - ], - [ - Complex64::new(sin_theta, 0.0), - Complex64::new(cos_theta, 0.0) - ] - ] -} - -/// Returns a unitary matrix representing the `Rz` operation with the given angle. -#[must_use] -pub fn rz(theta: f64) -> Array2 { - let exp_theta = Complex64::exp(Complex64::new(0.0, theta / 2.0)); - let neg_exp_theta = Complex64::exp(Complex64::new(0.0, -theta / 2.0)); - array![ - [neg_exp_theta, Complex64::zero()], - [Complex64::zero(), exp_theta] - ] -} - -/// Returns a unitary matrix representing the `G` or `GlobalPhase` operation with the given angle. -#[must_use] -pub fn g(theta: f64) -> Array2 { - let neg_exp_theta = Complex64::exp(Complex64::new(0.0, -theta / 2.0)); - array![ - [neg_exp_theta, Complex64::zero()], - [Complex64::zero(), neg_exp_theta] - ] -} - -/// Returns a unitary matrix representing the `SWAP` operation. -#[must_use] -pub fn swap() -> Array2 { - array![ - [ - Complex64::one(), - Complex64::zero(), - Complex64::zero(), - Complex64::zero() - ], - [ - Complex64::zero(), - Complex64::zero(), - Complex64::one(), - Complex64::zero() - ], - [ - Complex64::zero(), - Complex64::one(), - Complex64::zero(), - Complex64::zero() - ], - [ - Complex64::zero(), - Complex64::zero(), - Complex64::zero(), - Complex64::one() - ] - ] -} - -/// Transforms the given matrix into it's adjoint using the transpose of the complex conjugate. -#[must_use] -pub fn adjoint(u: &Array2) -> Array2 { - u.t().map(Complex64::conj) -} - -/// Extends the given unitary matrix into a matrix corresponding to the same unitary with a given number of controls -/// by inserting it into an identity matrix. -#[must_use] -pub fn controlled(u: &Array2, num_ctrls: u32) -> Array2 { - let mut controlled_u = Array2::eye(u.nrows() * 2_usize.pow(num_ctrls)); - controlled_u - .slice_mut(s![ - (controlled_u.nrows() - u.nrows()).., - (controlled_u.ncols() - u.ncols()).. - ]) - .assign(u); - controlled_u -} - -#[cfg(test)] -mod tests { - use super::*; - use core::f64::consts::PI; - - fn is_self_adjoint(arr: &Array2) -> bool { - arr == adjoint(arr) - } - - fn are_equal_to_precision(actual: Array2, expected: Array2) -> bool { - // If we use assert_eq here, we'll get bitten by finite precision. - // We also can't use LAPACK, since that greatly complicates bindings, - // so we do an ad hoc implementation here. - (actual - expected).map(|x| x.norm()).sum() <= 1e-10 - } - - #[test] - fn h_is_self_adjoint() { - assert!(is_self_adjoint(&h())); - } - - #[test] - fn x_is_self_adjoint() { - assert!(is_self_adjoint(&x())); - } - - #[test] - fn y_is_self_adjoint() { - assert!(is_self_adjoint(&y())); - } - - #[test] - fn z_is_self_adjoint() { - assert!(is_self_adjoint(&z())); - } - - #[test] - fn swap_is_self_adjoint() { - assert!(is_self_adjoint(&swap())); - } - - #[test] - fn s_squares_to_z() { - assert_eq!(s().dot(&s()), z()); - } - - #[test] - fn t_squares_to_s() { - assert!(are_equal_to_precision(t().dot(&t()), s())); - } - - #[test] - fn rx_pi_is_x() { - assert!(are_equal_to_precision(Complex64::i() * rx(PI), x())); - } - - #[test] - fn ry_pi_is_y() { - assert!(are_equal_to_precision(Complex64::i() * ry(PI), y())); - } - - #[test] - fn rz_pi_is_z() { - assert!(are_equal_to_precision(Complex64::i() * rz(PI), z())); - } - - #[test] - fn gate_multiplication() { - assert!(are_equal_to_precision(x().dot(&y()), Complex64::i() * z())); - } - - #[test] - fn controlled_extension() { - fn cnot() -> Array2 { - array![ - [ - Complex64::one(), - Complex64::zero(), - Complex64::zero(), - Complex64::zero() - ], - [ - Complex64::zero(), - Complex64::one(), - Complex64::zero(), - Complex64::zero() - ], - [ - Complex64::zero(), - Complex64::zero(), - Complex64::zero(), - Complex64::one() - ], - [ - Complex64::zero(), - Complex64::zero(), - Complex64::one(), - Complex64::zero() - ] - ] - } - assert!(are_equal_to_precision(controlled(&x(), 1), cnot())); - assert!(are_equal_to_precision( - controlled(&x(), 2), - controlled(&cnot(), 1) - )); - assert_eq!(controlled(&x(), 3).nrows(), 2_usize.pow(4)); - } -} diff --git a/src/Qir/Runtime/backend/src/lib.rs b/src/Qir/Runtime/backend/src/lib.rs deleted file mode 100644 index 29c3bb42827..00000000000 --- a/src/Qir/Runtime/backend/src/lib.rs +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] - -//! Module defining QIR compliant APIs for quantum simulation. - -pub mod result_bool; - -mod common_matrices; -mod nearly_zero; -mod simulator; - -use bitvec::prelude::*; -use lazy_static::lazy_static; -use simulator::QuantumSim; -use std::convert::TryInto; -use std::ffi::{c_void, CString}; -use std::mem::size_of; -use std::os::raw::c_double; -use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; -use std::sync::Mutex; - -use result_bool::{ - __quantum__rt__result_equal, __quantum__rt__result_get_one, __quantum__rt__result_get_zero, -}; - -pub use qir_stdlib::{ - arrays::*, bigints::*, callables::*, math::*, output_recording::*, range_support::*, - strings::*, tuples::*, *, -}; - -lazy_static! { - static ref SIM: Mutex> = Mutex::new(None); - static ref RESULTS: Mutex = Mutex::new(bitvec![]); - static ref MAX_QUBIT_ID: AtomicUsize = AtomicUsize::new(0); -} - -fn ensure_sufficient_qubits(sim: &mut QuantumSim, qubit_id: usize) { - while qubit_id + 1 > (*MAX_QUBIT_ID).load(Relaxed) { - let _ = sim.allocate(); - (*MAX_QUBIT_ID).fetch_add(1, Relaxed); - } -} - -#[allow(clippy::cast_ptr_alignment)] -unsafe fn map_paulis( - sim: &mut QuantumSim, - paulis: *const QirArray, - qubits: *const QirArray, -) -> Vec<(Pauli, usize)> { - let paulis_size = __quantum__rt__array_get_size_1d(paulis); - let qubits_size = __quantum__rt__array_get_size_1d(qubits); - if paulis_size != qubits_size { - __quantum__rt__fail(__quantum__rt__string_create( - CString::new("Pauli array and Qubit array must be the same size.") - .unwrap() - .as_bytes_with_nul() - .as_ptr() as *mut i8, - )); - } - - let combined_list: Vec<(Pauli, usize)> = (0..paulis_size) - .filter_map(|index| { - let p = - *__quantum__rt__array_get_element_ptr_1d(paulis, index).cast::() as Pauli; - let q = *__quantum__rt__array_get_element_ptr_1d(qubits, index as u64) - .cast::<*mut c_void>() as usize; - if let Pauli::I = p { - None - } else { - ensure_sufficient_qubits(sim, q); - Some((p, q)) - } - }) - .collect(); - - for (pauli, qubit) in &combined_list { - match pauli { - Pauli::X => sim.apply(&common_matrices::h(), &[*qubit], None), - Pauli::Y => { - sim.apply(&common_matrices::h(), &[*qubit], None); - sim.apply(&common_matrices::s(), &[*qubit], None); - sim.apply(&common_matrices::h(), &[*qubit], None); - } - _ => (), - } - } - - combined_list -} - -fn unmap_paulis(sim: &mut QuantumSim, combined_list: Vec<(Pauli, usize)>) { - for (pauli, qubit) in combined_list { - match pauli { - Pauli::X => sim.apply(&common_matrices::h(), &[qubit], None), - Pauli::Y => { - sim.apply(&common_matrices::h(), &[qubit], None); - sim.apply( - &common_matrices::adjoint(&common_matrices::s()), - &[qubit], - None, - ); - sim.apply(&common_matrices::h(), &[qubit], None); - } - _ => (), - } - } -} - -macro_rules! single_qubit_gate { - ($(#[$meta:meta])* - $qir_name:ident, $gate_matrix:expr) => { - $(#[$meta])* - #[no_mangle] - pub extern "C" fn $qir_name(qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - - sim.apply(&$gate_matrix, &[qubit as usize], None); - - *sim_lock = Some(sim); - } - }; -} - -macro_rules! fast_single_qubit_gate { - ($(#[$meta:meta])* - $qir_name:ident, $fast_gate:expr) => { - $(#[$meta])* - #[no_mangle] - pub extern "C" fn $qir_name(qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - - sim.flush(); - $fast_gate(&mut sim, qubit as usize); - - *sim_lock = Some(sim); - } - }; -} - -single_qubit_gate!( - /// QIR API for performing the H gate on the given qubit. - __quantum__qis__h__body, - common_matrices::h() -); -single_qubit_gate!( - /// QIR API for performing the S gate on the given qubit. - __quantum__qis__s__body, - common_matrices::s() -); -single_qubit_gate!( - /// QIR API for performing the Adjoint S gate on the given qubit. - __quantum__qis__s__adj, - common_matrices::adjoint(&common_matrices::s()) -); -single_qubit_gate!( - /// QIR API for performing the T gate on the given qubit. - __quantum__qis__t__body, - common_matrices::t() -); -single_qubit_gate!( - /// QIR API for performing the Adjoint T gate on the given qubit. - __quantum__qis__t__adj, - common_matrices::adjoint(&common_matrices::t()) -); -fast_single_qubit_gate!( - /// QIR API for performing the X gate on the given qubit. - __quantum__qis__x__body, - QuantumSim::fast_x -); -fast_single_qubit_gate!( - /// QIR API for performing the Y gate on the given qubit. - __quantum__qis__y__body, - QuantumSim::fast_y -); -fast_single_qubit_gate!( - /// QIR API for performing the Z gate on the given qubit. - __quantum__qis__z__body, - QuantumSim::fast_z -); - -macro_rules! fast_controlled_qubit_gate { - ($(#[$meta:meta])* - $qir_name:ident, $fast_gate:expr, 1) => { - $(#[$meta])* - #[no_mangle] - pub extern "C" fn $qir_name(control: *mut c_void, target: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, target as usize); - ensure_sufficient_qubits(&mut sim, control as usize); - - sim.flush(); - $fast_gate(&mut sim, &[control as usize], target as usize); - - *sim_lock = Some(sim); - } - }; - ($(#[$meta:meta])* - $qir_name:ident, $fast_gate:expr, 2) => { - $(#[$meta])* - #[no_mangle] - pub extern "C" fn $qir_name( - control_1: *mut c_void, - control_2: *mut c_void, - target: *mut c_void, - ) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, target as usize); - ensure_sufficient_qubits(&mut sim, control_1 as usize); - ensure_sufficient_qubits(&mut sim, control_2 as usize); - - sim.flush(); - $fast_gate(&mut sim, &[control_1 as usize, control_2 as usize], target as usize); - - *sim_lock = Some(sim); - } - }; -} - -fast_controlled_qubit_gate!( - /// QIR API for performing the CNOT gate with the given qubits. - __quantum__qis__cnot__body, - QuantumSim::fast_mcx, - 1 -); -fast_controlled_qubit_gate!( - /// QIR API for performing the CNOT gate with the given qubits. - __quantum__qis__cx__body, - QuantumSim::fast_mcx, - 1 -); -fast_controlled_qubit_gate!( - /// QIR API for performing the CNOT gate with the given qubits. - __quantum__qis__ccx__body, - QuantumSim::fast_mcx, - 2 -); -fast_controlled_qubit_gate!( - /// QIR API for performing the CZ gate with the given qubits. - __quantum__qis__cz__body, - QuantumSim::fast_mcz, - 1 -); - -macro_rules! single_qubit_rotation { - ($(#[$meta:meta])* - $qir_name:ident, $gate_matrix:expr) => { - $(#[$meta])* - #[no_mangle] - pub extern "C" fn $qir_name(theta: c_double, qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - - sim.apply(&$gate_matrix(theta), &[qubit as usize], None); - - *sim_lock = Some(sim); - } - }; -} - -single_qubit_rotation!( - /// QIR API for applying a global phase with the given angle and qubit. - __quantum__qis__ri__body, - common_matrices::g -); -single_qubit_rotation!( - /// QIR API for applying a Pauli-X rotation with the given angle and qubit. - __quantum__qis__rx__body, - common_matrices::rx -); -single_qubit_rotation!( - /// QIR API for applying a Pauli-Y rotation with the given angle and qubit. - __quantum__qis__ry__body, - common_matrices::ry -); -single_qubit_rotation!( - /// QIR API for applying a Pauli-Z rotation with the given angle and qubit. - __quantum__qis__rz__body, - common_matrices::rz -); - -macro_rules! multicontrolled_qubit_gate { - ($(#[$meta:meta])* - $qir_name:ident, $gate_matrix:expr) => { - $(#[$meta])* - /// # Safety - /// - /// This function should only be called with arrays and tuples created by the QIR runtime library. - #[no_mangle] - pub unsafe extern "C" fn $qir_name(ctls: *const QirArray, qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - let ctls_size = __quantum__rt__array_get_size_1d(ctls); - let ctls_list: Vec = (0..ctls_size) - .map(|index| { - let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index) - .cast::<*mut c_void>() as usize; - ensure_sufficient_qubits(&mut sim, q); - q - }) - .collect(); - - sim.apply(&$gate_matrix, &[qubit as usize], Some(&ctls_list)); - - *sim_lock = Some(sim); - } - }; -} - -macro_rules! fast_multicontrolled_qubit_gate { - ($(#[$meta:meta])* - $qir_name:ident, $fast_gate:expr) => { - $(#[$meta])* - /// # Safety - /// - /// This function should only be called with arrays and tuples created by the QIR runtime library. - #[no_mangle] - pub unsafe extern "C" fn $qir_name(ctls: *const QirArray, qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - let ctls_size = __quantum__rt__array_get_size_1d(ctls); - let ctls_list: Vec = (0..ctls_size) - .map(|index| { - let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index) - .cast::<*mut c_void>() as usize; - ensure_sufficient_qubits(&mut sim, q); - q - }) - .collect(); - - sim.flush(); - $fast_gate(&mut sim, &ctls_list, qubit as usize); - - *sim_lock = Some(sim); - } - }; -} - -multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled H gate with the given qubits. - __quantum__qis__h__ctl, - common_matrices::h() -); -multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled S gate with the given qubits. - __quantum__qis__s__ctl, - common_matrices::s() -); -multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled Adjoint S gate with the given qubits. - __quantum__qis__s__ctladj, - common_matrices::adjoint(&common_matrices::s()) -); -multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled T gate with the given qubits. - __quantum__qis__t__ctl, - common_matrices::t() -); -multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled Adjoint T gate with the given qubits. - __quantum__qis__t__ctladj, - common_matrices::adjoint(&common_matrices::t()) -); -fast_multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled X gate with the given qubits. - __quantum__qis__x__ctl, - QuantumSim::fast_mcx -); -fast_multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled Y gate with the given qubits. - __quantum__qis__y__ctl, - QuantumSim::fast_mcy -); -fast_multicontrolled_qubit_gate!( - /// QIR API for performing the multicontrolled Z gate with the given qubits. - __quantum__qis__z__ctl, - QuantumSim::fast_mcz -); - -#[derive(Copy, Clone)] -#[repr(C)] -struct RotationArgs { - theta: c_double, - qubit: *mut c_void, -} - -macro_rules! multicontrolled_qubit_rotation { - ($(#[$meta:meta])* - $qir_name:ident, $gate_matrix:expr) => { - $(#[$meta])* - /// # Safety - /// - /// This function should only be called with arrays and tuples created by the QIR runtime library. - #[no_mangle] - pub unsafe extern "C" fn $qir_name( - ctls: *const QirArray, - arg_tuple: *mut *const Vec, - ) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock - .take() - .map_or_else(QuantumSim::default, |s| s); - - let args = *arg_tuple.cast::(); - - ensure_sufficient_qubits(&mut sim, args.qubit as usize); - let ctls_size = __quantum__rt__array_get_size_1d(ctls); - let ctls_list: Vec = (0..ctls_size) - .map(|index| { - let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index) - .cast::<*mut c_void>() as usize; - ensure_sufficient_qubits(&mut sim, q); - q - }) - .collect(); - - sim.apply( - &$gate_matrix(args.theta), - &[args.qubit as usize], - Some(&ctls_list), - ); - - *sim_lock = Some(sim); - } - }; -} - -multicontrolled_qubit_rotation!( - /// QIR API for applying a multicontrolled global phase rotation with the given angle and qubit. - __quantum__qis__ri__ctl, - common_matrices::g -); -multicontrolled_qubit_rotation!( - /// QIR API for applying a multicontrolled Pauli-X rotation with the given angle and qubit. - __quantum__qis__rx__ctl, - common_matrices::rx -); -multicontrolled_qubit_rotation!( - /// QIR API for applying a multicontrolled Pauli-Y rotation with the given angle and qubit. - __quantum__qis__ry__ctl, - common_matrices::ry -); -multicontrolled_qubit_rotation!( - /// QIR API for applying a multicontrolled Pauli-Z rotation with the given angle and qubit. - __quantum__qis__rz__ctl, - common_matrices::rz -); - -/// QIR API for applying a rotation about the given Pauli axis with the given angle and qubit. -#[no_mangle] -pub extern "C" fn __quantum__qis__r__body(pauli: Pauli, theta: c_double, qubit: *mut c_void) { - match pauli { - Pauli::I => __quantum__qis__ri__body(theta, qubit), - Pauli::X => __quantum__qis__rx__body(theta, qubit), - Pauli::Y => __quantum__qis__ry__body(theta, qubit), - Pauli::Z => __quantum__qis__rz__body(theta, qubit), - } -} - -/// QIR API for applying an adjoint rotation about the given Pauli axis with the given angle and qubit. -#[no_mangle] -pub extern "C" fn __quantum__qis__r__adj(pauli: Pauli, theta: c_double, qubit: *mut c_void) { - __quantum__qis__r__body(pauli, -theta, qubit); -} - -#[derive(Copy, Clone)] -#[repr(C)] -struct PauliRotationArgs { - pauli: Pauli, - theta: c_double, - qubit: *mut c_void, -} - -/// QIR API for applying a controlled rotation about the given Pauli axis with the given angle and qubit. -/// # Safety -/// -/// This function should only be called with arrays and tuples created by the QIR runtime library. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn __quantum__qis__r__ctl( - ctls: *const QirArray, - arg_tuple: *mut *const Vec, -) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - - let args = *arg_tuple.cast::(); - - ensure_sufficient_qubits(&mut sim, args.qubit as usize); - let ctls_size = __quantum__rt__array_get_size_1d(ctls); - let ctls_list: Vec = (0..ctls_size) - .map(|index| { - let q = *__quantum__rt__array_get_element_ptr_1d(ctls, index).cast::<*mut c_void>() - as usize; - ensure_sufficient_qubits(&mut sim, q); - q - }) - .collect(); - - sim.apply( - &(match args.pauli { - Pauli::I => common_matrices::g, - Pauli::X => common_matrices::rx, - Pauli::Y => common_matrices::ry, - Pauli::Z => common_matrices::rz, - })(args.theta), - &[args.qubit as usize], - Some(&ctls_list), - ); - - *sim_lock = Some(sim); -} - -/// QIR API for applying an adjoint controlled rotation about the given Pauli axis with the given angle and qubit. -/// # Safety -/// -/// This function should only be called with arrays and tuples created by the QIR runtime library. -#[no_mangle] -pub unsafe extern "C" fn __quantum__qis__r__ctladj( - ctls: *const QirArray, - arg_tuple: *mut *const Vec, -) { - let args = *arg_tuple.cast::(); - let new_args = PauliRotationArgs { - pauli: args.pauli, - theta: -args.theta, - qubit: args.qubit, - }; - let new_arg_tuple = __quantum__rt__tuple_create(size_of::() as u64); - *new_arg_tuple.cast::() = new_args; - __quantum__qis__r__ctl(ctls, new_arg_tuple); - __quantum__rt__tuple_update_reference_count(new_arg_tuple, -1); -} - -/// QIR API for applying a SWAP gate to the given qubits. -#[no_mangle] -pub extern "C" fn __quantum__qis__swap__body(qubit0: *mut c_void, qubit1: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit0 as usize); - ensure_sufficient_qubits(&mut sim, qubit1 as usize); - - sim.apply( - &common_matrices::swap(), - &[qubit0 as usize, qubit1 as usize], - None, - ); - - *sim_lock = Some(sim); -} - -/// QIR API for performing the exponential of a multi-qubit Pauli operator with the given angle. -#[no_mangle] -pub extern "C" fn __quantum__qis__exp__body( - _paulis: *const QirArray, - _theta: c_double, - _qubits: *const QirArray, -) { - unimplemented!("Exp is not implemented.") -} - -/// QIR API for performing the adjoint exponential of a multi-qubit Pauli operator with the given angle. -#[no_mangle] -pub extern "C" fn __quantum__qis__exp__adj( - paulis: *const QirArray, - theta: c_double, - qubits: *const QirArray, -) { - __quantum__qis__exp__body(paulis, -theta, qubits); -} - -/// QIR API for performing the multicontrolled exponential of a multi-qubit Pauli operator with the given angle. -#[no_mangle] -pub extern "C" fn __quantum__qis__exp__ctl( - _ctls: *const QirArray, - _arg_tuple: *mut *const Vec, -) { - unimplemented!("Controlled Exp is not implemented.") -} - -/// QIR API for performing the adjoint multicontrolled exponential of a multi-qubit Pauli operator with the given angle. -#[no_mangle] -pub extern "C" fn __quantum__qis__exp__ctladj( - _ctls: *const QirArray, - _arg_tuple: *mut *const Vec, -) { - unimplemented!("Controlled Exp is not implemented.") -} - -/// QIR API for resetting the given qubit in the computational basis. -#[no_mangle] -pub extern "C" fn __quantum__qis__reset__body(qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - - if sim.measure(qubit as usize) { - sim.flush(); - sim.fast_x(qubit as usize); - } - - *sim_lock = Some(sim); -} - -/// QIR API for measuring the given qubit in the computation basis and storing the measured value with the given result identifier. -#[no_mangle] -pub extern "C" fn __quantum__qis__mz__body(qubit: *mut c_void, result: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - let mut res = RESULTS.lock().expect("Unable to lock global result state."); - let res_id = result as usize; - ensure_sufficient_qubits(&mut sim, qubit as usize); - - if res.len() < res_id + 1 { - res.resize(res_id + 1, false); - } - - *res.get_mut(res_id) - .expect("Result with given id missing after expansion.") = sim.measure(qubit as usize); - - *sim_lock = Some(sim); -} - -/// QIR API that reads the Boolean value corresponding to the given result identifier, where true -/// indicates a |1⟩ state and false indicates a |0⟩ state. -#[no_mangle] -pub extern "C" fn __quantum__qis__read_result__body(result: *mut c_void) -> bool { - let mut res = RESULTS.lock().expect("Unable to lock global result state."); - let res_id = result as usize; - if res.len() < res_id + 1 { - res.resize(res_id + 1, false); - } - - let b = *res - .get(res_id) - .expect("Result with given id missing after expansion."); - b -} - -/// QIR API that measures a given qubit in the computational basis, returning a runtime managed result value. -/// # Panics -/// -#[no_mangle] -pub extern "C" fn __quantum__qis__m__body(qubit: *mut c_void) -> *mut c_void { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - ensure_sufficient_qubits(&mut sim, qubit as usize); - - let res = sim.measure(qubit as usize); - - *sim_lock = Some(sim); - if res { - __quantum__rt__result_get_one() - } else { - __quantum__rt__result_get_zero() - } -} - -/// QIR API that performs joint measurement of the given qubits in the corresponding Pauli bases, returning the parity as a runtime managed result value. -/// # Safety -/// -/// This function should only be called with arrays created by the QIR runtime library. -/// # Panics -/// -/// This function will panic if the provided paulis and qubits arrays are not of the same size. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn __quantum__qis__measure__body( - paulis: *const QirArray, - qubits: *const QirArray, -) -> *mut c_void { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - - let combined_list = map_paulis(&mut sim, paulis, qubits); - - let res = sim.joint_measure( - &combined_list - .iter() - .map(|(_, q)| *q) - .collect::>(), - ); - - unmap_paulis(&mut sim, combined_list); - - *sim_lock = Some(sim); - if res { - __quantum__rt__result_get_one() - } else { - __quantum__rt__result_get_zero() - } -} - -/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result -/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance. -/// # Safety -/// -/// This function should only be called with arrays created by the QIR runtime library. -#[no_mangle] -pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__body( - paulis: *const QirArray, - qubits: *const QirArray, - result: *mut c_void, - prob: c_double, - msg: *const CString, - tol: c_double, -) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - - let combined_list = map_paulis(&mut sim, paulis, qubits); - - let mut actual_prob = sim.joint_probability( - &combined_list - .iter() - .map(|(_, q)| *q) - .collect::>(), - ); - - if __quantum__rt__result_equal(result, __quantum__rt__result_get_zero()) { - actual_prob = 1.0 - actual_prob; - } - - if (actual_prob - (prob as f64)).abs() > tol as f64 { - __quantum__rt__fail(msg); - } - - unmap_paulis(&mut sim, combined_list); - - *sim_lock = Some(sim); -} - -#[derive(Copy, Clone)] -#[repr(C)] -struct AssertMeasurementProbabilityArgs { - paulis: *const QirArray, - qubits: *const QirArray, - result: *mut c_void, - prob: c_double, - msg: *const CString, - tol: c_double, -} - -/// QIR API for checking internal simulator state and verifying the probability of the given parity measurement result -/// for the given qubits in the given Pauli bases is equal to the expected probability, within the given tolerance. -/// Note that control qubits are ignored. -/// # Safety -/// -/// This function should only be called with arrays created by the QIR runtime library. -#[no_mangle] -pub unsafe extern "C" fn __quantum__qis__assertmeasurementprobability__ctl( - _ctls: *const QirArray, - arg_tuple: *mut *const Vec, -) { - let args = *arg_tuple.cast::(); - __quantum__qis__assertmeasurementprobability__body( - args.paulis, - args.qubits, - args.result, - args.prob, - args.msg, - args.tol, - ); -} - -/// QIR API for recording the given result into the program output. -#[no_mangle] -pub extern "C" fn __quantum__rt__result_record_output(result: *mut c_void) { - let mut res = RESULTS.lock().expect("Unable to lock global result state."); - let res_id = result as usize; - let b = if res.len() == 0 { - // No static measurements have been used, so default to dynamic handling. - __quantum__rt__result_equal(result, __quantum__rt__result_get_one()) - } else { - if res.len() < res_id + 1 { - res.resize(res_id + 1, false); - } - *res.get(res_id) - .expect("Result with given id missing after expansion.") - }; - - println!("RESULT\t{}", if b { "1" } else { "0" }); -} - -/// QIR API that allocates the next available qubit in the simulation. -#[no_mangle] -pub extern "C" fn __quantum__rt__qubit_allocate() -> *mut c_void { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - let qubit_id = sim.allocate(); - - // Increase the max qubit id global so that `ensure_sufficient_qubits` wont trigger more allocations. - // NOTE: static allocation and dynamic allocation shouldn't be used together, so this is safe to do. - (*MAX_QUBIT_ID).fetch_max(qubit_id + 1, Relaxed); - - *sim_lock = Some(sim); - qubit_id as *mut c_void -} - -/// QIR API for allocating the given number of qubits in the simulation, returning them as a runtime managed array. -/// # Panics -/// -/// This function will panic if the underlying platform has a pointer size that cannot be described in -/// a `u32`. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub extern "C" fn __quantum__rt__qubit_allocate_array(size: u64) -> *const QirArray { - let arr = __quantum__rt__array_create_1d(size_of::().try_into().unwrap(), size); - for index in 0..size { - unsafe { - let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>(); - *elem = __quantum__rt__qubit_allocate(); - } - } - arr -} - -/// QIR API for releasing the given runtime managed qubit array. -/// # Safety -/// -/// This function should only be called with arrays created by `__quantum__rt__qubit_allocate_array`. -#[allow(clippy::cast_ptr_alignment)] -#[no_mangle] -pub unsafe extern "C" fn __quantum__rt__qubit_release_array(arr: *const QirArray) { - for index in 0..__quantum__rt__array_get_size_1d(arr) { - let elem = __quantum__rt__array_get_element_ptr_1d(arr, index).cast::<*mut c_void>(); - __quantum__rt__qubit_release(*elem); - } - __quantum__rt__array_update_alias_count(arr, -1); -} - -/// QIR API for releasing the given qubit from the simulation. -#[no_mangle] -pub extern "C" fn __quantum__rt__qubit_release(qubit: *mut c_void) { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - sim.release(qubit as usize); - *sim_lock = Some(sim); -} - -/// QIR API for getting the string interpretation of a qubit identifier. -/// # Panics -/// -/// This function will panic if it is unable to allocate the memory for the string. -#[no_mangle] -pub extern "C" fn __quantum__rt__qubit_to_string(qubit: *mut c_void) -> *const CString { - unsafe { - __quantum__rt__string_create( - CString::new(format!("{}", qubit as usize)) - .unwrap() - .as_bytes_with_nul() - .as_ptr() as *mut i8, - ) - } -} - -/// API for viewing the current global result and quantum state for the simulator. -#[no_mangle] -pub extern "C" fn dump_state() { - let mut sim_lock = SIM.lock().expect("Unable to lock global simulator state."); - let mut sim = sim_lock.take().map_or_else(QuantumSim::default, |s| s); - let res = RESULTS.lock().expect("Unable to lock global result state."); - - if !(*res).is_empty() { - println!("Global Results: {}", *res); - } - sim.dump(); - - *sim_lock = Some(sim); -} - -/// QIR API for dumping full internal simulator state. -#[no_mangle] -pub extern "C" fn __quantum__qis__dumpmachine__body(location: *mut c_void) { - if !location.is_null() { - unimplemented!("Dump to location is not implemented.") - } - dump_state(); -} - -/// QIR API for dumping the internal simulator state for the given qubits. -#[no_mangle] -pub extern "C" fn __quantum__qis__dumpregister__body( - _location: *mut c_void, - _qubits: *const QirArray, -) { - unimplemented!("Dump of qubit register is not implemented.") -} - -#[cfg(test)] -mod tests { - use std::ffi::c_void; - - use super::{ - __quantum__qis__cnot__body, __quantum__qis__h__body, __quantum__qis__m__body, - __quantum__qis__mz__body, __quantum__qis__read_result__body, __quantum__qis__x__body, - __quantum__rt__qubit_allocate, __quantum__rt__qubit_allocate_array, - __quantum__rt__qubit_release, __quantum__rt__qubit_release_array, - __quantum__rt__result_equal, __quantum__rt__result_get_one, dump_state, - }; - use qir_stdlib::arrays::__quantum__rt__array_get_element_ptr_1d; - - // TODO(swernli): Split and expand simulator unit tests. - #[allow(clippy::cast_ptr_alignment)] - #[test] - fn basic_test() { - let q0 = 5 as *mut c_void; - let r0 = std::ptr::null_mut(); - let r1 = 1 as *mut c_void; - __quantum__qis__mz__body(q0, r0); - assert!(!__quantum__qis__read_result__body(r0)); - __quantum__qis__x__body(q0); - __quantum__qis__mz__body(q0, r1); - assert!(__quantum__qis__read_result__body(r1)); - __quantum__qis__x__body(q0); - __quantum__qis__mz__body(q0, r0); - assert!(!__quantum__qis__read_result__body(r0)); - assert!(!__quantum__qis__read_result__body(3 as *mut c_void)); - dump_state(); - - // Dynamic qubits can be used after static, but not the other way around. Keep test cases - // together to avoid issues with global state. - let q1 = __quantum__rt__qubit_allocate(); - let q2 = __quantum__rt__qubit_allocate(); - __quantum__qis__h__body(q1); - __quantum__qis__cnot__body(q1, q2); - let r1 = __quantum__qis__m__body(q1); - let r2 = __quantum__qis__m__body(q2); - assert!(__quantum__rt__result_equal(r1, r2)); - dump_state(); - __quantum__rt__qubit_release(q2); - __quantum__rt__qubit_release(q1); - let qs = __quantum__rt__qubit_allocate_array(4); - unsafe { - let q_elem = __quantum__rt__array_get_element_ptr_1d(qs, 3).cast::<*mut c_void>(); - __quantum__qis__x__body(*q_elem); - dump_state(); - let r = __quantum__qis__m__body(*q_elem); - assert!(__quantum__rt__result_equal( - r, - __quantum__rt__result_get_one() - )); - __quantum__rt__qubit_release_array(qs); - } - } -} diff --git a/src/Qir/Runtime/backend/src/nearly_zero.rs b/src/Qir/Runtime/backend/src/nearly_zero.rs deleted file mode 100644 index ad7918c56e8..00000000000 --- a/src/Qir/Runtime/backend/src/nearly_zero.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use num_complex::Complex; - -/// `NearlyZero` trait allows for approximate evaluation of a value to the additive identity. -pub(crate) trait NearlyZero { - fn is_nearly_zero(&self) -> bool; -} - -impl NearlyZero for f64 { - fn is_nearly_zero(&self) -> bool { - self.max(0.0) - 0.0_f64.min(*self) <= 1e-10 - } -} - -impl NearlyZero for Complex -where - T: NearlyZero, -{ - fn is_nearly_zero(&self) -> bool { - self.re.is_nearly_zero() && self.im.is_nearly_zero() - } -} diff --git a/src/Qir/Runtime/backend/src/result_bool.rs b/src/Qir/Runtime/backend/src/result_bool.rs deleted file mode 100644 index 01a0cdc52eb..00000000000 --- a/src/Qir/Runtime/backend/src/result_bool.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] - -use qir_stdlib::strings::__quantum__rt__string_create; -use std::ffi::{c_void, CString}; - -#[no_mangle] -pub extern "C" fn __quantum__rt__result_get_zero() -> *mut c_void { - std::ptr::null_mut() -} - -#[no_mangle] -pub extern "C" fn __quantum__rt__result_get_one() -> *mut c_void { - 1 as *mut c_void -} - -#[no_mangle] -pub extern "C" fn __quantum__rt__result_equal(r1: *mut c_void, r2: *mut c_void) -> bool { - r1 == r2 -} - -#[no_mangle] -pub extern "C" fn __quantum__rt__result_update_reference_count(_res: *mut c_void, _update: i32) { - // no-op -} - -#[no_mangle] -pub extern "C" fn __quantum__rt__result_to_string(res: *mut c_void) -> *const CString { - unsafe { - __quantum__rt__string_create( - CString::new( - if __quantum__rt__result_equal(res, __quantum__rt__result_get_one()) { - "One" - } else { - "Zero" - }, - ) - .expect("Failed to allocate memory for result string.") - .as_bytes_with_nul() - .as_ptr() as *mut i8, - ) - } -} diff --git a/src/Qir/Runtime/backend/src/simulator.rs b/src/Qir/Runtime/backend/src/simulator.rs deleted file mode 100644 index da5d4329b4a..00000000000 --- a/src/Qir/Runtime/backend/src/simulator.rs +++ /dev/null @@ -1,1165 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -use super::common_matrices; - -use crate::nearly_zero::NearlyZero; -use ndarray::Array2; -use num_bigint::BigUint; -use num_complex::Complex64; -use num_traits::{One, ToPrimitive, Zero}; -use rand::Rng; -use rayon::current_num_threads; -use rayon::prelude::*; -use rustc_hash::FxHashMap; -use std::convert::TryInto; -use std::mem::MaybeUninit; -use std::ops::ControlFlow; - -/// Limit that is checked to determine whether the operations buffer should be flushed before adding -/// more operations. Note that this is not a hard limit such that the buffer will never exceed this value, -/// just the value used to determine whether the buffer should accept the current incoming operation. -const BUFFER_LIMIT: usize = 4; - -/// Scaling used to determine when an operation is big enough to warrant multithreading. If the size -/// is too small the overhead of threading leads to performance degredation compared to the single threaded -/// approach. The scaling must be a power of two. -const CHUNK_SCALING: usize = 512; - -/// Get the chunk size we want to use in parallel for the given number of items based on the threads available in the -/// thread pool. -fn parallel_chunk_size(total_size: usize) -> usize { - // Chunk count needs to be a power of two to work with the state vector math. - let mut chunk_count = 1_usize; - while chunk_count << 1 <= current_num_threads() { - chunk_count <<= 1; - } - if chunk_count > 1 && total_size / CHUNK_SCALING > chunk_count { - total_size / chunk_count - } else { - // Treat as a single chunk. - total_size - } -} - -struct OpBuffer { - pub targets: Vec, - pub ops: Array2, -} - -pub type SparseState = FxHashMap; - -/// The `QuantumSim` struct contains the necessary state for tracking the simulation. Each instance of a -/// `QuantumSim` represents an independant simulation. -pub(crate) struct QuantumSim { - /// The structure that describes the current quantum state. - state: FxHashMap, - - /// The mapping from qubit identifiers to internal state locations. - id_map: FxHashMap, - - /// The ordered buffer containing queued quantum operations that have not yet been applied to the - /// state. - op_buffer: OpBuffer, -} - -impl Default for QuantumSim { - fn default() -> Self { - Self::new() - } -} - -/// Provides the common set of functionality across all quantum simulation types. -impl QuantumSim { - /// Creates a new sparse state quantum simulator object with empty initial state (no qubits allocated, no operations buffered). - #[must_use] - fn new() -> Self { - QuantumSim { - state: FxHashMap::default(), - - id_map: FxHashMap::default(), - - op_buffer: OpBuffer { - targets: vec![], - ops: Array2::default((0, 0)), - }, - } - } - - /// Allocates a fresh qubit, returning its identifier. Note that this will use the lowest available - /// identifier, and may result in qubits being allocated "in the middle" of an existing register - /// if those identifiers are available. - #[must_use] - pub(crate) fn allocate(&mut self) -> usize { - if self.id_map.is_empty() { - // Add the intial value for the zero state. - self.state.insert(BigUint::zero(), Complex64::one()); - } - - // Add the new entry into the FxHashMap at the first available sequential ID. - let mut sorted_keys: Vec<&usize> = self.id_map.keys().collect(); - sorted_keys.sort(); - let n_qubits = sorted_keys.len(); - let new_key = sorted_keys - .iter() - .enumerate() - .take_while(|(index, key)| index == **key) - .last() - .map_or(0_usize, |(_, &&key)| key + 1); - self.id_map.insert(new_key, n_qubits); - - // Return the new ID that was used. - new_key - } - - /// Utility function that will swap states of two qubits throughout the sparse state map. - fn swap_qubits(&mut self, qubit1: usize, qubit2: usize) { - if qubit1 == qubit2 { - return; - } - - let (q1, q2) = (qubit1 as u64, qubit2 as u64); - - // In parallel, swap entries in the sparse state to correspond to swapping of two qubits' - // locations. - let chunk_size = parallel_chunk_size(self.state.len()); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (k, v) in chunk { - if k.bit(q1) == k.bit(q2) { - accum.insert(k.clone(), *v); - } else { - let mut new_k = k.clone(); - new_k.set_bit(q1, !k.bit(q1)); - new_k.set_bit(q2, !k.bit(q2)); - accum.insert(new_k, *v); - } - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut chunk| { - for (k, v) in chunk.drain() { - accum.insert(k, v); - } - accum - }); - } - - /// Releases the given qubit, collapsing its state in the process. After release that identifier is - /// no longer valid for use in other functions and will cause an error if used. - /// # Panics - /// - /// The function will panic if the given id does not correpsond to an allocated qubit. - pub(crate) fn release(&mut self, id: usize) { - self.flush(); - - // Since it is easier to release a contiguous half of the state, find the qubit - // with the last location and swap that with the qubit to be released. - let n_qubits = self.id_map.len(); - let loc = *self - .id_map - .get(&id) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}.", id)); - let last_loc = n_qubits - 1; - if last_loc != loc { - let last_id = *self - .id_map - .iter() - .find(|(_, &value)| value == last_loc) - .unwrap() - .0; - self.swap_qubits(loc, last_loc); - *(self.id_map.get_mut(&last_id).unwrap()) = loc; - *(self.id_map.get_mut(&id).unwrap()) = last_loc; - }; - - // Measure and collapse the state for this qubit. - let res = self.measure_impl(last_loc); - - // Remove the released ID from the mapping and cleanup the unused part of the state. - self.id_map.remove(&id); - self.cleanup_state(res); - } - - /// Releases the given qubit, collapsing its state in the process. After release that identifier is - /// no longer valid for use in other functions and will cause an error if used. - /// # Panics - /// - /// The function will panic if the given id does not correpsond to an allocated qubit. - fn cleanup_state(&mut self, res: bool) { - if res { - let qubit = self.id_map.len() as u64; - let chunk_size = parallel_chunk_size(self.state.len()); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (k, v) in chunk { - let mut new_k = k.clone(); - new_k.set_bit(qubit, !k.bit(qubit)); - accum.insert(new_k, *v); - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut chunk| { - for (k, v) in chunk.drain() { - accum.insert(k, v); - } - accum - }); - } - } - - /// Flushes the current operations buffer and updates the resulting state, clearing the buffer - /// in the process. - /// # Panics - /// - /// This function will panic if the given ids that do not correspond to allocated qubits. - pub(crate) fn flush(&mut self) { - if !self.op_buffer.targets.is_empty() { - // Swap each of the target qubits to the right-most entries in the state, in order. - self.op_buffer - .targets - .clone() - .iter() - .rev() - .enumerate() - .for_each(|(target_loc, target)| { - let loc = *self - .id_map - .get(target) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", target)); - let swap_id = *self - .id_map - .iter() - .find(|(_, &value)| value == target_loc) - .unwrap() - .0; - self.swap_qubits(loc, target_loc); - *(self.id_map.get_mut(&swap_id).unwrap()) = loc; - *(self.id_map.get_mut(target).unwrap()) = target_loc; - }); - - self.apply_impl(); - - self.op_buffer = OpBuffer { - targets: vec![], - ops: Array2::default((0, 0)), - }; - } - } - - /// Prints the current state vector to standard output with integer labels for the states, skipping any - /// states with zero amplitude. - /// # Panics - /// - /// This function panics if it is unable sort the state into qubit id order. - pub(crate) fn dump(&mut self) { - self.flush(); - - // Swap all the entries in the state to be ordered by qubit identifier. This makes - // interpreting the state easier for external consumers that don't have access to the id map. - let mut sorted_keys: Vec = self.id_map.keys().copied().collect(); - sorted_keys.sort_unstable(); - sorted_keys.iter().enumerate().for_each(|(index, &key)| { - if index != self.id_map[&key] { - self.swap_qubits(self.id_map[&key], index); - let swapped_key = *self - .id_map - .iter() - .find(|(_, &value)| value == index) - .unwrap() - .0; - *(self.id_map.get_mut(&swapped_key).unwrap()) = self.id_map[&key]; - *(self.id_map.get_mut(&key).unwrap()) = index; - } - }); - - self.dump_impl(false); - } - - /// Applies the given unitary to the given targets, extending the unitary to accomodate controls if any. - /// # Panics - /// - /// This function will panic if given ids in either targets or optional controls that do not correspond to allocated - /// qubits, or if there is a duplicate id in targets or controls. - /// This funciton will panic if the given unitary matrix does not match the number of targets provided. - /// This function will panic if the given unitary is not square. - /// This function will panic if the total number of targets and controls too large for a `u32`. - pub(crate) fn apply( - &mut self, - unitary: &Array2, - targets: &[usize], - controls: Option<&[usize]>, - ) { - let mut targets = targets.to_vec(); - let mut unitary = unitary.clone(); - - assert!( - unitary.ncols() == unitary.nrows(), - "Application given non-square matrix." - ); - - assert!( - targets.len() == unitary.ncols() / 2, - "Application given incorrect number of targets; expected {}, given {}.", - unitary.ncols() / 2, - targets.len() - ); - - if let Some(ctrls) = controls { - // Add controls in order as targets. - ctrls - .iter() - .enumerate() - .for_each(|(index, &element)| targets.insert(index, element)); - - // Extend the provided unitary by inserting it into an identity matrix. - unitary = common_matrices::controlled(&unitary, ctrls.len().try_into().unwrap()); - } - - let mut sorted_targets = targets.clone(); - sorted_targets.sort_unstable(); - if let ControlFlow::Break(Some(duplicate)) = - sorted_targets.iter().try_fold(None, |last, current| { - last.map_or_else( - || ControlFlow::Continue(Some(current)), - |last| { - if last == current { - ControlFlow::Break(Some(current)) - } else { - ControlFlow::Continue(Some(current)) - } - }, - ) - }) - { - panic!("Duplicate qubit id '{}' found in application.", duplicate); - } - - // If the new operation would be too big for the buffer limit or if any target qubits are already - // targets of the buffered operations, flush the buffer first before adding the currently - // requested operation. - if self.op_buffer.targets.len() + targets.len() > BUFFER_LIMIT - || self.op_buffer.targets.iter().any(|q| targets.contains(q)) - { - self.flush(); - } - self.op_buffer.targets.append(&mut targets.clone()); - if self.op_buffer.targets.len() > targets.len() { - // Add the new unitary to the buffered operation by performing the Kronecker product. Note - // this means tthe order of targets must be maintained to match the combined matrix. - self.op_buffer.ops = QuantumSim::kron(&self.op_buffer.ops, &unitary); - } else { - // This is the only operation in an empty buffer, so just copy it. - self.op_buffer.ops = unitary; - } - } - - /// Parallelized Kronecker product implementation, copied from `ndarray::linalg::kron`. - fn kron(alpha: &Array2, beta: &Array2) -> Array2 { - let dim_alpha_rows = alpha.shape()[0]; - let dim_alpha_cols = alpha.shape()[1]; - let dim_beta_rows = beta.shape()[0]; - let dim_beta_cols = beta.shape()[1]; - let mut out: Array2> = Array2::uninit(( - dim_alpha_rows - .checked_mul(dim_beta_rows) - .expect("Dimensions of kronecker product output array overflows usize."), - dim_alpha_cols - .checked_mul(dim_beta_cols) - .expect("Dimensions of kronecker product output array overflows usize."), - )); - ndarray::Zip::from(out.exact_chunks_mut((dim_beta_rows, dim_beta_cols))) - .and(alpha) - .for_each(|out, &a| { - ndarray::Zip::from(out).and(beta).for_each(|out, &b| { - *out = MaybeUninit::new(a * b); - }); - }); - unsafe { out.assume_init() } - } - - /// Checks the probability of parity measurement in the computational basis for the given set of - /// qubits. - /// # Panics - /// - /// This function will panic if the given ids do not all correspond to allocated qubits. - /// This function will panic if there are duplicate ids in the given list. - #[must_use] - pub(crate) fn joint_probability(&mut self, ids: &[usize]) -> f64 { - let mut sorted_targets = ids.to_vec(); - sorted_targets.sort_unstable(); - if let ControlFlow::Break(Some(duplicate)) = - sorted_targets.iter().try_fold(None, |last, current| { - last.map_or_else( - || ControlFlow::Continue(Some(current)), - |last| { - if last == current { - ControlFlow::Break(Some(current)) - } else { - ControlFlow::Continue(Some(current)) - } - }, - ) - }) - { - panic!("Duplicate qubit id '{}' found in application.", duplicate); - } - - // Flush the buffered operations to update the state. - self.flush(); - - let locs: Vec = ids - .iter() - .map(|id| { - *self - .id_map - .get(id) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", id)) - }) - .collect(); - - self.check_joint_probability(&locs) - } - - /// Utility that performs the actual measurement and collapse of the state for the given - /// location. - fn measure_impl(&mut self, loc: usize) -> bool { - let mut rng = rand::thread_rng(); - let random_sample: f64 = rng.gen(); - let res = random_sample < self.check_joint_probability(&[loc]); - self.collapse(loc, res); - res - } - - /// Measures the qubit with the given id, collapsing the state based on the measured result. - /// # Panics - /// - /// This funciton will panic if the given identifier does not correspond to an allocated qubit. - #[must_use] - pub(crate) fn measure(&mut self, id: usize) -> bool { - // Flush the buffered operations and update the state. - self.flush(); - - self.measure_impl( - *self - .id_map - .get(&id) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", id)), - ) - } - - /// Performs a joint measurement to get the parity of the given qubits, collapsing the state - /// based on the measured result. - /// # Panics - /// - /// This function will panic if any of the given identifiers do not correspond to an allocated qubit. - /// This function will panic if any of the given identifiers are duplicates. - #[must_use] - pub(crate) fn joint_measure(&mut self, ids: &[usize]) -> bool { - let mut sorted_targets = ids.to_vec(); - sorted_targets.sort_unstable(); - if let ControlFlow::Break(Some(duplicate)) = - sorted_targets.iter().try_fold(None, |last, current| { - last.map_or_else( - || ControlFlow::Continue(Some(current)), - |last| { - if last == current { - ControlFlow::Break(Some(current)) - } else { - ControlFlow::Continue(Some(current)) - } - }, - ) - }) - { - panic!("Duplicate qubit id '{}' found in application.", duplicate); - } - - // Flush the buffered operations and update the state. - self.flush(); - - let locs: Vec = ids - .iter() - .map(|id| { - *self - .id_map - .get(id) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", id)) - }) - .collect(); - - let mut rng = rand::thread_rng(); - let random_sample: f64 = rng.gen(); - let res = random_sample < self.check_joint_probability(&locs); - self.joint_collapse(&locs, res); - res - } - - /// Utility function that performs the actual output of state (and optionally map) to screen. Can - /// be called internally from other functions to aid in debugging and does not perform any modification - /// of the internal structures. - fn dump_impl(&self, print_id_map: bool) { - if print_id_map { - println!("MAP: {:?}", self.id_map); - }; - print!("STATE: [ "); - let mut sorted_keys = self.state.keys().collect::>(); - sorted_keys.sort_unstable(); - for key in sorted_keys { - print!( - "|{}\u{27e9}: {}, ", - key, - self.state.get(key).map_or_else(Complex64::zero, |v| *v) - ); - } - println!("]"); - } - - /// Utility that actually performs the application of the buffered unitary to the targets within the - /// sparse state. - fn apply_impl(&mut self) { - // let state_size = 1_usize << self.id_map.len(); - let chunk_size = parallel_chunk_size(self.state.len()); - let op_size = self.op_buffer.ops.nrows(); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (index, val) in chunk { - let i = index / op_size; - let l = (index % op_size) - .to_usize() - .expect("Cannot operate on more than 64 qubits at a time."); - for j in - (0..op_size).filter(|j| !self.op_buffer.ops.row(*j)[l].is_nearly_zero()) - { - let loc = (&i * op_size) + j; - if let Some(entry) = accum.get_mut(&loc) { - *entry += self.op_buffer.ops.row(j)[l] * val; - } else { - accum.insert((&i * op_size) + j, self.op_buffer.ops.row(j)[l] * val); - } - if accum - .get(&loc) - .map_or_else(|| false, |entry| (*entry).is_nearly_zero()) - { - accum.remove(&loc); - } - } - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut sparse_chunk| { - for (k, v) in sparse_chunk.drain() { - if let Some(entry) = accum.get_mut(&k) { - *entry += v; - } else if !v.is_nearly_zero() { - accum.insert(k.clone(), v); - } - if accum - .get(&k) - .map_or_else(Complex64::one, |entry| *entry) - .is_nearly_zero() - { - accum.remove(&k); - } - } - accum - }); - assert!( - !self.state.is_empty(), - "State vector should never be empty." - ); - } - - /// Utility to get the sum of all probabilies where an odd number of the bits at the given locations - /// are set. This corresponds to the probability of jointly measuring those qubits in the computational - /// basis. - fn check_joint_probability(&self, locs: &[usize]) -> f64 { - let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { - accum | (BigUint::one() << loc) - }); - (&self.state) - .into_par_iter() - .fold( - || 0.0_f64, - |accum, (index, val)| { - if (index & &mask).count_ones() & 1 > 0 { - accum + val.norm_sqr() - } else { - accum - } - }, - ) - .sum() - } - - /// Utility to perform the normalize of the state. - fn normalize(&mut self) { - let scale = 1.0 - / self - .state - .par_iter() - .fold(|| 0.0_f64, |sum, (_, val)| sum + val.norm_sqr()) - .sum::() - .sqrt(); - - self.state.par_iter_mut().for_each(|(_, val)| *val *= scale); - } - - /// Utility to collapse the probability at the given location based on the boolean value. This means - /// that if the given value is 'true' then all keys in the sparse state where the given location - /// has a zero bit will be reduced to zero and removed. Then the sparse state is normalized. - fn collapse(&mut self, loc: usize, val: bool) { - self.joint_collapse(&[loc], val); - } - - /// Utility to collapse the joint probability of a particular set of locations in the sparse state. - /// The entries that do not correspond to the given boolean value are removed, and then the whole - /// state is normalized. - fn joint_collapse(&mut self, locs: &[usize], val: bool) { - let mask = locs.iter().fold(BigUint::zero(), |accum, loc| { - accum | (BigUint::one() << loc) - }); - - let chunk_size = parallel_chunk_size(self.state.len()); - self.state = self - .state - .drain() - .collect::>() - .par_chunks(chunk_size) - .fold(FxHashMap::default, |mut accum, chunk| { - for (k, v) in chunk - .iter() - .filter(|(index, _)| ((index & &mask).count_ones() & 1 > 0) == val) - { - accum.insert(k.clone(), *v); - } - accum - }) - .reduce(FxHashMap::default, |mut accum, mut chunk| { - for (k, v) in chunk.drain() { - accum.insert(k, v); - } - accum - }); - - self.normalize(); - } - - fn fast_gate(&mut self, target: usize, mut op: F) - where - F: FnMut((BigUint, Complex64), u64) -> (BigUint, Complex64), - { - assert!(self.op_buffer.targets.is_empty()); - let target = *self - .id_map - .get(&target) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", target)); - self.state = self - .state - .drain() - .into_iter() - .map(|kvp| op(kvp, target as u64)) - .fold(SparseState::default(), |mut accum, (k, v)| { - accum.insert(k, v); - accum - }); - } - - fn fast_controlled_gate(&mut self, ctls: &[usize], target: usize, mut op: F) - where - F: FnMut((BigUint, Complex64), u64) -> (BigUint, Complex64), - { - assert!(self.op_buffer.targets.is_empty()); - - let target = *self - .id_map - .get(&target) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", target)); - - let ctls: Vec = ctls - .iter() - .map(|c| { - *self - .id_map - .get(c) - .unwrap_or_else(|| panic!("Unable to find qubit with id {}", c)) - }) - .collect(); - - let mut sorted_qubits = ctls.clone(); - sorted_qubits.push(target); - sorted_qubits.sort_unstable(); - if let ControlFlow::Break(Some(duplicate)) = - sorted_qubits.iter().try_fold(None, |last, current| { - last.map_or_else( - || ControlFlow::Continue(Some(current)), - |last| { - if last == current { - ControlFlow::Break(Some(current)) - } else { - ControlFlow::Continue(Some(current)) - } - }, - ) - }) - { - panic!("Duplicate qubit id '{}' found in application.", duplicate); - } - - self.state = self - .state - .drain() - .into_iter() - .map(|kvp| { - if ctls.iter().all(|c| kvp.0.bit(*c as u64)) { - op(kvp, target as u64) - } else { - kvp - } - }) - .fold(SparseState::default(), |mut accum, (k, v)| { - accum.insert(k, v); - accum - }); - } - - fn x_transform((mut index, val): (BigUint, Complex64), target: u64) -> (BigUint, Complex64) { - index.set_bit(target, !index.bit(target)); - (index, val) - } - - pub(crate) fn fast_x(&mut self, target: usize) { - self.fast_gate(target, Self::x_transform); - } - - pub(crate) fn fast_mcx(&mut self, ctls: &[usize], target: usize) { - self.fast_controlled_gate(ctls, target, Self::x_transform); - } - - fn y_transform( - (mut index, mut val): (BigUint, Complex64), - target: u64, - ) -> (BigUint, Complex64) { - index.set_bit(target, !index.bit(target)); - val *= if index.bit(target) { - Complex64::i() - } else { - -Complex64::i() - }; - (index, val) - } - - pub(crate) fn fast_y(&mut self, target: usize) { - self.fast_gate(target, Self::y_transform); - } - - pub(crate) fn fast_mcy(&mut self, ctls: &[usize], target: usize) { - self.fast_controlled_gate(ctls, target, Self::y_transform); - } - - fn z_transform((index, mut val): (BigUint, Complex64), target: u64) -> (BigUint, Complex64) { - val *= if index.bit(target) { - -Complex64::one() - } else { - Complex64::one() - }; - (index, val) - } - - pub(crate) fn fast_z(&mut self, target: usize) { - self.fast_gate(target, Self::z_transform); - } - - pub(crate) fn fast_mcz(&mut self, ctls: &[usize], target: usize) { - self.fast_controlled_gate(ctls, target, Self::z_transform); - } -} - -#[cfg(test)] -mod tests { - use super::{ - common_matrices::{adjoint, controlled, h, s, x}, - *, - }; - use ndarray::array; - use num_traits::{One, Zero}; - - fn almost_equal(a: f64, b: f64) -> bool { - a.max(b) - b.min(a) <= 1e-10 - } - - // Test that basic allocation and release of qubits doesn't fail. - #[test] - fn test_alloc_release() { - let sim = &mut QuantumSim::default(); - for i in 0..16 { - assert_eq!(sim.allocate(), i); - } - sim.release(4); - sim.release(7); - sim.release(12); - assert_eq!(sim.allocate(), 4); - for i in 0..7 { - sim.release(i); - } - for i in 8..12 { - sim.release(i); - } - for i in 13..16 { - sim.release(i); - } - } - - /// Test basic appliation of gate matrices doesn't fail. Note that this does not verify the contents - /// of the state. - #[test] - fn test_apply1() { - let mut sim = QuantumSim::default(); - for i in 0..6 { - assert_eq!(sim.allocate(), i); - } - sim.apply(&x(), &[2], None); - sim.apply(&x(), &[4], None); - sim.apply(&x(), &[5], None); - sim.release(5); - sim.apply(&ndarray::linalg::kron(&h(), &x()), &[1, 4], None); - sim.apply(&x(), &[2], None); - sim.apply(&h(), &[1], None); - for i in 0..5 { - sim.release(i); - } - } - - /// Test appliction of gates that result in entanglement. Note that this does not verify the contents - /// of the state. - #[test] - fn test_apply2() { - let mut sim = QuantumSim::default(); - let q0 = sim.allocate(); - let q1 = sim.allocate(); - let q2 = sim.allocate(); - sim.apply(&h(), &[q0], None); - sim.apply(&controlled(&x(), 1), &[q0, q1], None); - sim.apply(&controlled(&x(), 1), &[q0, q2], None); - sim.apply(&controlled(&x(), 1), &[q1, q0], None); - sim.apply(&controlled(&x(), 1), &[q1, q2], None); - sim.apply(&h(), &[q1], None); - sim.release(q0); - sim.release(q1); - sim.release(q2); - } - - /// Verifies that application of gates to a qubit results in the correct probabilities. - #[test] - fn test_probability() { - let mut sim = QuantumSim::default(); - let q = sim.allocate(); - let extra = sim.allocate(); - assert!(almost_equal(0.0, sim.joint_probability(&[q]))); - sim.apply(&x(), &[q], None); - assert!(almost_equal(1.0, sim.joint_probability(&[q]))); - sim.apply(&x(), &[q], None); - assert!(almost_equal(0.0, sim.joint_probability(&[q]))); - sim.apply(&h(), &[q], None); - assert!(almost_equal(0.5, sim.joint_probability(&[q]))); - sim.apply(&h(), &[q], None); - assert!(almost_equal(0.0, sim.joint_probability(&[q]))); - sim.apply(&x(), &[q], None); - sim.apply(&h(), &[q], None); - sim.apply(&s(), &[q], None); - assert!(almost_equal(0.5, sim.joint_probability(&[q]))); - sim.apply(&adjoint(&s()), &[q], None); - sim.apply(&h(), &[q], None); - sim.apply(&x(), &[q], None); - assert!(almost_equal(0.0, sim.joint_probability(&[q]))); - sim.release(extra); - sim.release(q); - } - - /// Verify that a qubit in superposition has probability corresponding the measured value and - /// can be operationally reset back into the ground state. - #[test] - fn test_measure() { - let mut sim = QuantumSim::default(); - let q = sim.allocate(); - let extra = sim.allocate(); - assert!(!sim.measure(q)); - sim.apply(&x(), &[q], None); - assert!(sim.measure(q)); - let mut res = false; - while !res { - sim.apply(&h(), &[q], None); - res = sim.measure(q); - assert!(almost_equal( - sim.joint_probability(&[q]), - if res { 1.0 } else { 0.0 } - )); - if res { - sim.apply(&x(), &[q], None); - } - } - assert!(almost_equal(sim.joint_probability(&[q]), 0.0)); - sim.release(extra); - sim.release(q); - } - - /// Verify joint probability works as expected, namely that it corresponds to the parity of the - /// qubits. - #[test] - fn test_joint_probability() { - let mut sim = QuantumSim::default(); - let q0 = sim.allocate(); - let q1 = sim.allocate(); - assert!(almost_equal(0.0, sim.joint_probability(&[q0, q1]))); - sim.apply(&x(), &[q0], None); - assert!(almost_equal(1.0, sim.joint_probability(&[q0, q1]))); - sim.apply(&x(), &[q1], None); - assert!(almost_equal(0.0, sim.joint_probability(&[q0, q1]))); - assert!(almost_equal(1.0, sim.joint_probability(&[q0]))); - assert!(almost_equal(1.0, sim.joint_probability(&[q1]))); - sim.apply(&h(), &[q0], None); - assert!(almost_equal(0.5, sim.joint_probability(&[q0, q1]))); - sim.release(q1); - sim.release(q0); - } - - /// Verify joint measurement works as expected, namely that it corresponds to the parity of the - /// qubits. - #[test] - fn test_joint_measurement() { - let mut sim = QuantumSim::default(); - let q0 = sim.allocate(); - let q1 = sim.allocate(); - assert!(!sim.joint_measure(&[q0, q1])); - sim.apply(&x(), &[q0], None); - assert!(sim.joint_measure(&[q0, q1])); - sim.apply(&x(), &[q1], None); - assert!(!sim.joint_measure(&[q0, q1])); - assert!(sim.joint_measure(&[q0])); - assert!(sim.joint_measure(&[q1])); - sim.apply(&h(), &[q0], None); - let res = sim.joint_measure(&[q0, q1]); - assert!(almost_equal( - if res { 1.0 } else { 0.0 }, - sim.joint_probability(&[q0, q1]) - )); - sim.release(q1); - sim.release(q0); - } - - /// Test arbitrary controls, which should extend the applied unitary matrix. - #[test] - fn test_arbitrary_controls() { - let mut sim = QuantumSim::default(); - let q0 = sim.allocate(); - let q1 = sim.allocate(); - let q2 = sim.allocate(); - assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); - sim.apply(&h(), &[q0], None); - assert!(almost_equal(0.5, sim.joint_probability(&[q0]))); - sim.apply(&h(), &[q0], None); - assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); - sim.apply(&h(), &[q0], Some(&[q1])); - assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); - sim.apply(&x(), &[q1], None); - sim.apply(&h(), &[q0], Some(&[q1])); - assert!(almost_equal(0.5, sim.joint_probability(&[q0]))); - sim.apply(&h(), &[q0], Some(&[q2, q1])); - assert!(almost_equal(0.5, sim.joint_probability(&[q0]))); - sim.apply(&x(), &[q2], None); - sim.apply(&h(), &[q0], Some(&[q2, q1])); - assert!(almost_equal(0.0, sim.joint_probability(&[q0]))); - sim.apply(&x(), &[q1], None); - sim.apply(&x(), &[q2], None); - sim.release(q2); - sim.release(q1); - sim.release(q0); - } - - /// Verify that targets cannot be duplicated. - #[test] - #[should_panic(expected = "Duplicate qubit id '0' found in application.")] - fn test_duplicate_target() { - let mut sim = QuantumSim::new(); - let q = sim.allocate(); - sim.apply(&controlled(&x(), 1), &[q, q], None); - } - - /// Verify that controls cannot be duplicated. - #[test] - #[should_panic(expected = "Duplicate qubit id '1' found in application.")] - fn test_duplicate_control() { - let mut sim = QuantumSim::new(); - let q = sim.allocate(); - let c = sim.allocate(); - sim.apply(&x(), &[q], Some(&[c, c])); - } - - /// Verify that targets aren't in controls. - #[test] - #[should_panic(expected = "Duplicate qubit id '0' found in application.")] - fn test_target_in_control() { - let mut sim = QuantumSim::new(); - let q = sim.allocate(); - let c = sim.allocate(); - sim.apply(&x(), &[q], Some(&[c, q])); - } - - /// Verify target count logic, which should reject any application where the given targets does - /// not match the size of the given unitary. - #[test] - #[should_panic( - expected = "Application given incorrect number of targets; expected 2, given 1." - )] - fn test_target_count() { - let mut sim = QuantumSim::new(); - let q = sim.allocate(); - sim.apply(&controlled(&x(), 1), &[q], None); - } - - /// Verify that non-square matrices are rejected. - #[test] - #[should_panic(expected = "Application given non-square matrix.")] - fn test_nonsquare() { - let mut sim = QuantumSim::new(); - let q = sim.allocate(); - sim.apply(&array![[Complex64::zero()], [Complex64::one()]], &[q], None); - } - - /// Large, entangled state handling. - #[test] - fn test_large_state() { - let mut sim = QuantumSim::new(); - let ctl = sim.allocate(); - sim.apply(&h(), &[ctl], None); - for _ in 0..4999 { - let q = sim.allocate(); - sim.apply(&x(), &[q], Some(&[ctl])); - } - let _ = sim.measure(ctl); - for i in 0..5000 { - sim.release(i); - } - } - - /// Utility for testing operation equivalence. - fn assert_operation_equal_referenced(mut op: F1, mut reference: F2, count: usize) - where - F1: FnMut(&mut QuantumSim, &[usize]), - F2: FnMut(&mut QuantumSim, &[usize]), - { - let mut sim = QuantumSim::new(); - - // Allocte the control we use to verify behavior. - let ctl = sim.allocate(); - sim.apply(&common_matrices::h(), &[ctl], None); - - // Allocate the requested number of targets, entangling the control with them. - let mut qs = vec![]; - for _ in 0..count { - let q = sim.allocate(); - sim.apply(&common_matrices::x(), &[q], Some(&[ctl])); - qs.push(q); - } - - op(&mut sim, &qs); - reference(&mut sim, &qs); - - // Undo the entanglement. - for q in qs { - sim.apply(&common_matrices::x(), &[q], Some(&[ctl])); - sim.release(q); - } - sim.apply(&common_matrices::h(), &[ctl], None); - - // We know the operations are equal if the control is left in the zero state. - assert!(sim.joint_probability(&[ctl]).is_nearly_zero()); - } - - /// Test fast X - #[test] - fn test_fast_x() { - assert_operation_equal_referenced( - |sim, qs| { - sim.flush(); - sim.fast_x(qs[0]); - }, - |sim, qs| { - sim.apply(&common_matrices::x(), &[qs[0]], None); - }, - 1, - ); - } - - /// Test fast Y - #[test] - fn test_fast_y() { - assert_operation_equal_referenced( - |sim, qs| { - sim.flush(); - sim.fast_y(qs[0]); - }, - |sim, qs| { - sim.apply(&common_matrices::y(), &[qs[0]], None); - }, - 1, - ); - } - - /// Test fast Z - #[test] - fn test_fast_z() { - assert_operation_equal_referenced( - |sim, qs| { - sim.flush(); - sim.fast_z(qs[0]); - }, - |sim, qs| { - sim.apply(&common_matrices::z(), &[qs[0]], None); - }, - 1, - ); - } - - /// Test fast CX - #[test] - fn test_fast_cx() { - assert_operation_equal_referenced( - |sim, qs| { - sim.flush(); - sim.fast_mcx(&[qs[0]], qs[1]); - }, - |sim, qs| { - sim.apply(&common_matrices::x(), &[qs[1]], Some(&[qs[0]])); - }, - 2, - ); - } - - /// Test fast CZ - #[test] - fn test_fast_cz() { - assert_operation_equal_referenced( - |sim, qs| { - sim.flush(); - sim.fast_mcz(&[qs[0]], qs[1]); - }, - |sim, qs| { - sim.apply(&common_matrices::z(), &[qs[1]], Some(&[qs[0]])); - }, - 2, - ); - } -} diff --git a/src/Qir/Runtime/build-qir-stdlib.ps1 b/src/Qir/Runtime/build-qir-stdlib.ps1 index f58a7572115..2e2c57431da 100644 --- a/src/Qir/Runtime/build-qir-stdlib.ps1 +++ b/src/Qir/Runtime/build-qir-stdlib.ps1 @@ -7,57 +7,55 @@ $IsCI = "$Env:TF_BUILD" -ne "" -or "$Env:CI" -eq "true"; -foreach ($folder in "stdlib","backend") { - Push-Location (Join-Path $PSScriptRoot $folder) - try { - # Start with the quick check first and make sure that Rust sources - # meet formatting and style guide rules. - cargo fmt -- --check - if ($LASTEXITCODE -ne 0) { throw "Failed cargo fmt check on QIR $folder." } - - # Check linting rules defined by clippy, a linting tool provided with the - # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy - # and https://rust-lang.github.io/rust-clippy/master/index.html - # for more information. - # If there's a false positive, that check should be explicitly disabled - # at the point where the false positive occurs with an explanation as to - # why it's OK. - cargo clippy --all-targets -- -D warnings - if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR $folder." } - - $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - - # Actually run the build. - cargo build @releaseFlag - if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR $folder." } - - # Copy the results of compilation and the corresponding headers to the QIR drops folder so - # they can be included in pipeline artifacts. - $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) - $qirDropsInclude = (Join-Path $Env:QIR_DROPS include) - if (-not (Test-Path $Env:QIR_DROPS)) { - New-Item -Path $Env:QIR_DROPS -ItemType "directory" - } - if (-not (Test-Path $qirDropsBin)) { - New-Item -Path $qirDropsBin -ItemType "directory" - } - if (-not (Test-Path $qirDropsInclude)) { - New-Item -Path $qirDropsInclude -ItemType "directory" - } - $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) - Copy-Item $qirBinaries $qirDropsBin -Include "*qir_$folder*" -Exclude "*.rlib","*.d","*.exp" - - # Copy the C API header and def file - Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.h) (Join-Path $Env:QIR_DROPS include) - Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.def) (Join-Path $Env:QIR_DROPS include) - - # When building in CI, free disk space by cleaning up. - # Note that this takes longer, but saves ~1 GB of space. - if ($IsCI) { - cargo clean; - } +Push-Location (Join-Path $PSScriptRoot stdlib) +try { + # Start with the quick check first and make sure that Rust sources + # meet formatting and style guide rules. + cargo fmt -- --check + if ($LASTEXITCODE -ne 0) { throw "Failed cargo fmt check on QIR stdlib." } + + # Check linting rules defined by clippy, a linting tool provided with the + # Rust toolchain. Please see https://github.com/rust-lang/rust-clippy + # and https://rust-lang.github.io/rust-clippy/master/index.html + # for more information. + # If there's a false positive, that check should be explicitly disabled + # at the point where the false positive occurs with an explanation as to + # why it's OK. + cargo clippy --all-targets -- -D warnings + if ($LASTEXITCODE -ne 0) { throw "Failed clippy linting check on QIR stdlib." } + + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); + + # Actually run the build. + cargo build @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo build on QIR stdlib." } + + # Copy the results of compilation and the corresponding headers to the QIR drops folder so + # they can be included in pipeline artifacts. + $qirDropsBin = (Join-Path $Env:QIR_DROPS bin $env:BUILD_PLATFORM native) + $qirDropsInclude = (Join-Path $Env:QIR_DROPS include) + if (-not (Test-Path $Env:QIR_DROPS)) { + New-Item -Path $Env:QIR_DROPS -ItemType "directory" } - finally { - Pop-Location + if (-not (Test-Path $qirDropsBin)) { + New-Item -Path $qirDropsBin -ItemType "directory" } -} \ No newline at end of file + if (-not (Test-Path $qirDropsInclude)) { + New-Item -Path $qirDropsInclude -ItemType "directory" + } + $qirBinaries = (Join-Path $PSScriptRoot .. .. .. target "$Env:BUILD_CONFIGURATION".ToLower() *) + Copy-Item $qirBinaries $qirDropsBin -Include "*qir_stdlib*" -Exclude "*.rlib","*.d","*.exp" + + # Copy the C API header and def file + Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.h) (Join-Path $Env:QIR_DROPS include) + Copy-Item (Join-Path $PSScriptRoot stdlib include qir_stdlib.def) (Join-Path $Env:QIR_DROPS include) + + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; + } +} +finally { + Pop-Location +} diff --git a/src/Qir/Runtime/test-qir-stdlib.ps1 b/src/Qir/Runtime/test-qir-stdlib.ps1 index c2238361489..fc22dc689ec 100644 --- a/src/Qir/Runtime/test-qir-stdlib.ps1 +++ b/src/Qir/Runtime/test-qir-stdlib.ps1 @@ -1,28 +1,26 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -foreach ($folder in "stdlib","backend") { - Push-Location (Join-Path $PSScriptRoot $folder) - try { - $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); +Push-Location (Join-Path $PSScriptRoot stdlib) +try { + $releaseFlag = "$Env:BUILD_CONFIGURATION" -eq "Release" ? @("--release") : @(); - # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) - # for interoperating Rust and C. - # NB: CFG is only supported on Windows, but the Rust flag is supported on - # all platforms; it's ignored on platforms without CFG functionality. - $Env:RUSTFLAGS = "-C control-flow-guard"; + # Enable control flow guard (see https://github.com/microsoft/qsharp-runtime/pull/647) + # for interoperating Rust and C. + # NB: CFG is only supported on Windows, but the Rust flag is supported on + # all platforms; it's ignored on platforms without CFG functionality. + $Env:RUSTFLAGS = "-C control-flow-guard"; - # Actually run the test. - cargo test @releaseFlag - if ($LASTEXITCODE -ne 0) { throw "Failed cargo test on QIR $folder." } + # Actually run the test. + cargo test @releaseFlag + if ($LASTEXITCODE -ne 0) { throw "Failed cargo test on QIR stdlib." } - # When building in CI, free disk space by cleaning up. - # Note that this takes longer, but saves ~1 GB of space. - if ($IsCI) { - cargo clean; - } + # When building in CI, free disk space by cleaning up. + # Note that this takes longer, but saves ~1 GB of space. + if ($IsCI) { + cargo clean; } - finally { - Pop-Location - } -} \ No newline at end of file +} +finally { + Pop-Location +} From 184fb7210e04fd830712de7e4723ecc6ad1e2dde Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 8 Sep 2022 12:47:59 -0700 Subject: [PATCH 39/55] Include full state sim lib in nuget package --- .gitignore | 1 + build/pack.ps1 | 3 +++ src/Simulation/Common/Simulators.Dev.props | 1 + 3 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index a012f955a9c..4d84dbe7f05 100644 --- a/.gitignore +++ b/.gitignore @@ -355,6 +355,7 @@ out/ # Ignore drops from building native simulators. xplat src/Simulation/Native/win10/Microsoft.Quantum.Simulator.Runtime.dll +src/Simulation/Native/win10/Microsoft.Quantum.Simulator.Runtime.lib src/Simulation/Native/linux/libMicrosoft.Quantum.Simulator.Runtime.so src/Simulation/Native/osx/libMicrosoft.Quantum.Simulator.Runtime.dylib src/Simulation/Native/win10/Microsoft.Quantum.SparseSimulator.Runtime.dll diff --git a/build/pack.ps1 b/build/pack.ps1 index cabc7be4dfc..61edd997681 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -25,6 +25,9 @@ Push-Location (Join-Path $PSScriptRoot ../src/Simulation/Native) If (Test-Path "$DROP/Microsoft.Quantum.Simulator.Runtime.dll") { Copy-Item -Verbose "$DROP/Microsoft.Quantum.Simulator.Runtime.dll" "win10/Microsoft.Quantum.Simulator.Runtime.dll" } + If (Test-Path "$DROP/Microsoft.Quantum.Simulator.Runtime.lib") { + Copy-Item -Verbose "$DROP/Microsoft.Quantum.Simulator.Runtime.lib" "win10/Microsoft.Quantum.Simulator.Runtime.lib" + } $DROP = "$Env:DROP_NATIVE/src/Simulation/NativeSparseSimulator/build" Write-Host "##[info]Copying NativeSparseSimulator files from $DROP..."; diff --git a/src/Simulation/Common/Simulators.Dev.props b/src/Simulation/Common/Simulators.Dev.props index 28eac72abc0..07253c84044 100644 --- a/src/Simulation/Common/Simulators.Dev.props +++ b/src/Simulation/Common/Simulators.Dev.props @@ -22,6 +22,7 @@ + From e59fe1cdc4b6e5c837b2534231351a8a0d69b29f Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 10:45:42 -0700 Subject: [PATCH 40/55] Update array unit tests --- src/Qir/Runtime/stdlib/src/arrays.rs | 165 ++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/arrays.rs b/src/Qir/Runtime/stdlib/src/arrays.rs index f6929eeec96..405f93b764c 100644 --- a/src/Qir/Runtime/stdlib/src/arrays.rs +++ b/src/Qir/Runtime/stdlib/src/arrays.rs @@ -107,7 +107,7 @@ pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( ) -> *mut i8 { let array = Rc::from_raw(arr); let i: usize = index.try_into().unwrap(); - let ptr = array.data.as_ptr().wrapping_add(array.elem_size * i) as *mut i8; + let ptr = array.data.as_ptr().add(array.elem_size * i) as *mut i8; let _ = Rc::into_raw(array); ptr } @@ -138,11 +138,93 @@ pub unsafe extern "C" fn __quantum__rt__array_update_alias_count( #[cfg(test)] mod tests { + use std::{mem::size_of, convert::TryInto}; + use super::*; use crate::range_support::{quantum__rt__array_slice_1d, Range}; #[test] - fn test_array_1d_basics() { + fn test_array_creation_simple() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + // Note: array reference counts must be decremented to prevent leaking array from test + // and failing address sanitized runs. + __quantum__rt__array_update_reference_count(arr, -1); + } + } + + #[test] + fn test_array_item_zero_init() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + // Array contents should always be zero initialized. + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0); + __quantum__rt__array_update_reference_count(arr, -1); + } + } + + #[test] + fn test_array_item_updates() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + // Confirm that array contents can be updated. + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + let third = __quantum__rt__array_get_element_ptr_1d(arr, 2); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0); + *third = 20; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20); + __quantum__rt__array_update_reference_count(arr, -1); + } + } + + #[test] + fn test_array_copy() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + // First array has contents [42, 31, 0], copy to second array and confirm contents. + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + // Update first array to [42, 31, 20], confirm second array is independent copy that still + // has original value. + let third = __quantum__rt__array_get_element_ptr_1d(arr, 2); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 0); + *third = 20; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 2), 20); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 2), 0); + // Decrement ref count on first array to trigger deletion, confirm the second array is + // independent copy with original values. + __quantum__rt__array_update_reference_count(arr, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + __quantum__rt__array_update_reference_count(arr2, -1); + } + } + + #[test] + fn test_array_concat() { let arr = __quantum__rt__array_create_1d(1, 3); unsafe { assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); @@ -154,10 +236,12 @@ mod tests { assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); *second = 31; assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + // First array has contents [42, 31, 0], copy to second array and confirm contents. let arr2 = __quantum__rt__array_copy(arr, true); assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + // Create third array with concatenated contents [42, 31, 0, 42, 31, 0]. let arr3 = __quantum__rt__array_concatenate(arr, arr2); assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); @@ -166,6 +250,47 @@ mod tests { assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + // Decrement counts on first and secon array to trigger deletion, confirm third array + // maintains contents. + __quantum__rt__array_update_reference_count(arr, -1); + __quantum__rt__array_update_reference_count(arr2, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + __quantum__rt__array_update_reference_count(arr3, -1); + } + } + + #[test] + fn test_array_slicing() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + let arr3 = __quantum__rt__array_concatenate(arr, arr2); + assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + // Third array crated via concatenation has contents [42, 31, 0, 42, 31, 0], create + // fourth array via slicing with step size 2, expected contents [42, 0, 31]. let arr4 = quantum__rt__array_slice_1d( arr3, Range { @@ -178,6 +303,8 @@ mod tests { assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + // Create fifth array via slicing with reverse iteration, expected contents + // [31, 0, 42]. let arr5 = quantum__rt__array_slice_1d( arr3, Range { @@ -190,6 +317,8 @@ mod tests { assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + // Create sixth array with range end less than range start, should succeed and create + // an empty array. let arr6 = quantum__rt__array_slice_1d( arr5, Range { @@ -198,6 +327,7 @@ mod tests { end: -1, }, ); + // Confirm each copy, concatenation, and slice is independent of others. assert_eq!(__quantum__rt__array_get_size_1d(arr6), 0); __quantum__rt__array_update_reference_count(arr, -1); assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); @@ -221,4 +351,35 @@ mod tests { __quantum__rt__array_update_reference_count(arr6, -1); } } + + #[allow(clippy::cast_ptr_alignment)] + #[test] + fn test_array_creation_large_size() { + // Use a struct that is known to have a size greater than 1. + struct Data { + first: i64, + second: i64, + third: i64, + } + let arr = __quantum__rt__array_create_1d(size_of::().try_into().unwrap(), 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0).cast::(); + *first = Data{first: 1, second: 2, third: 3}; + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1).cast::(); + *second = Data{first: 10, second: 20, third: 30}; + let third = __quantum__rt__array_get_element_ptr_1d(arr, 2).cast::(); + *third = Data{first: 100, second: 200, third: 300}; + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).first, 1); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).second, 2); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).third, 3); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).first, 10); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).second, 20); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).third, 30); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).first, 100); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).second, 200); + assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).third, 300); + __quantum__rt__array_update_reference_count(arr, -1); + } + } } From 0fcb85f5399c15cccc18c1211900dd2f78dd9e09 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 14:50:23 -0700 Subject: [PATCH 41/55] Update bigint unit tests --- src/Qir/Runtime/stdlib/src/bigints.rs | 101 +++++++++++++++++++++----- 1 file changed, 82 insertions(+), 19 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/bigints.rs b/src/Qir/Runtime/stdlib/src/bigints.rs index 1081a217567..71956a35d4b 100644 --- a/src/Qir/Runtime/stdlib/src/bigints.rs +++ b/src/Qir/Runtime/stdlib/src/bigints.rs @@ -230,33 +230,51 @@ pub unsafe extern "C" fn __quantum__rt__bigint_greater_eq( #[cfg(test)] mod tests { + use std::convert::TryInto; + use super::*; #[test] - fn test_bigint_basics() { + fn test_bigint_create_from_int() { let bigint_0 = __quantum__rt__bigint_create_i64(42); + unsafe { + assert_eq!(*bigint_0, (42).try_into().unwrap()); + // Note that the test must decrement the reference count on any created items to ensure + // they are cleaned up and tests can pass with address sanitizer. + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + } + } + + #[test] + fn test_bigint_create_from_array() { let bytes = 42_i64.to_be_bytes(); unsafe { let bigint_1 = __quantum__rt__bigint_create_array(bytes.len().try_into().unwrap(), bytes.as_ptr()); - assert!(__quantum__rt__bigint_equal(bigint_0, bigint_1)); + assert_eq!(*bigint_1, (42).try_into().unwrap()); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + } + } + + #[test] + fn test_bigint_arithmetic() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bigint_1 = __quantum__rt__bigint_create_i64(3); + unsafe { let bigint_2 = __quantum__rt__bigint_add(bigint_0, bigint_1); - let bigint_3 = __quantum__rt__bigint_create_i64(84); - assert!((*bigint_2) == 84.try_into().unwrap()); - assert!(__quantum__rt__bigint_equal(bigint_2, bigint_3)); - let bigint_4 = __quantum__rt__bigint_negate(bigint_0); - assert!((*bigint_4) == (-42).try_into().unwrap()); - let bigint_5 = __quantum__rt__bigint_add(bigint_2, bigint_4); - assert!((*bigint_5) == (42).try_into().unwrap()); - assert!(__quantum__rt__bigint_greater(bigint_2, bigint_5)); - assert!(__quantum__rt__bigint_greater(bigint_5, bigint_4)); - let bigint_6 = __quantum__rt__bigint_create_i64(2); - let bigint_7 = __quantum__rt__bigint_multiply(bigint_5, bigint_6); - assert!(__quantum__rt__bigint_equal(bigint_7, bigint_2)); - let bigint_8 = __quantum__rt__bigint_shiftleft(bigint_5, 1); - assert!(__quantum__rt__bigint_equal(bigint_7, bigint_8)); - let bigint_9 = __quantum__rt__bigint_shiftright(bigint_8, 1); - assert!(__quantum__rt__bigint_equal(bigint_9, bigint_0)); + assert_eq!(*bigint_2, (45).try_into().unwrap()); + let bigint_3 = __quantum__rt__bigint_subtract(bigint_2, bigint_1); + assert_eq!(*bigint_3, (42).try_into().unwrap()); + let bigint_4 = __quantum__rt__bigint_divide(bigint_3, bigint_1); + assert_eq!(*bigint_4, (14).try_into().unwrap()); + let bigint_5 = __quantum__rt__bigint_multiply(bigint_4, bigint_1); + assert_eq!(*bigint_5, (42).try_into().unwrap()); + let bigint_6 = __quantum__rt__bigint_modulus(bigint_5, bigint_1); + assert_eq!(*bigint_6, (0).try_into().unwrap()); + let bigint_7 = __quantum__rt__bigint_negate(bigint_5); + assert_eq!(*bigint_7, (-42).try_into().unwrap()); + let bigint_8 = __quantum__rt__bigint_power(bigint_7, 3); + assert_eq!(*bigint_8, (-74088).try_into().unwrap()); __quantum__rt__bigint_update_reference_count(bigint_0, -1); __quantum__rt__bigint_update_reference_count(bigint_1, -1); __quantum__rt__bigint_update_reference_count(bigint_2, -1); @@ -266,7 +284,52 @@ mod tests { __quantum__rt__bigint_update_reference_count(bigint_6, -1); __quantum__rt__bigint_update_reference_count(bigint_7, -1); __quantum__rt__bigint_update_reference_count(bigint_8, -1); - __quantum__rt__bigint_update_reference_count(bigint_9, -1); + } + } + + #[test] + fn test_bigint_bitops() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bigint_1 = __quantum__rt__bigint_create_i64(3); + unsafe { + let bigint_2 = __quantum__rt__bigint_bitand(bigint_0, bigint_1); + assert_eq!(*bigint_2, (2).try_into().unwrap()); + let bigint_3 = __quantum__rt__bigint_bitor(bigint_0, bigint_1); + assert_eq!(*bigint_3, (43).try_into().unwrap()); + let bigint_4 = __quantum__rt__bigint_bitxor(bigint_0, bigint_3); + assert_eq!(*bigint_4, (1).try_into().unwrap()); + let bigint_5 = __quantum__rt__bigint_bitnot(bigint_4); + assert_eq!(*bigint_5, (-2).try_into().unwrap()); + let bigint_6 = __quantum__rt__bigint_shiftleft(bigint_0, 2); + assert_eq!(*bigint_6, (168).try_into().unwrap()); + let bigint_7 = __quantum__rt__bigint_shiftright(bigint_6, 3); + assert_eq!(*bigint_7, (21).try_into().unwrap()); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_3, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_5, -1); + __quantum__rt__bigint_update_reference_count(bigint_6, -1); + __quantum__rt__bigint_update_reference_count(bigint_7, -1); + } + } + + #[test] + fn test_bigint_comparisons() { + let bigint_0 = __quantum__rt__bigint_create_i64(42); + let bigint_1 = __quantum__rt__bigint_create_i64(43); + let bigint_2 = __quantum__rt__bigint_create_i64(42); + unsafe { + assert!(__quantum__rt__bigint_greater(bigint_1, bigint_0)); + assert!(!__quantum__rt__bigint_greater(bigint_0, bigint_1)); + assert!(__quantum__rt__bigint_equal(bigint_0, bigint_2)); + assert!(__quantum__rt__bigint_greater_eq(bigint_0, bigint_2)); + assert!(__quantum__rt__bigint_greater_eq(bigint_1, bigint_2)); + assert!(!__quantum__rt__bigint_greater_eq(bigint_0, bigint_1)); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); } } } From 221956bed8b3d924c6c24788cb96e41918066dc9 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 14:54:24 -0700 Subject: [PATCH 42/55] Update range unit tests --- src/Qir/Runtime/stdlib/src/arrays.rs | 89 ------------------ src/Qir/Runtime/stdlib/src/range_support.rs | 99 ++++++++++++++++++++- 2 files changed, 96 insertions(+), 92 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/arrays.rs b/src/Qir/Runtime/stdlib/src/arrays.rs index 405f93b764c..5fe933d7cd6 100644 --- a/src/Qir/Runtime/stdlib/src/arrays.rs +++ b/src/Qir/Runtime/stdlib/src/arrays.rs @@ -141,7 +141,6 @@ mod tests { use std::{mem::size_of, convert::TryInto}; use super::*; - use crate::range_support::{quantum__rt__array_slice_1d, Range}; #[test] fn test_array_creation_simple() { @@ -264,94 +263,6 @@ mod tests { } } - #[test] - fn test_array_slicing() { - let arr = __quantum__rt__array_create_1d(1, 3); - unsafe { - assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); - let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); - *first = 42; - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); - let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); - *second = 31; - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); - let arr2 = __quantum__rt__array_copy(arr, true); - assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); - let arr3 = __quantum__rt__array_concatenate(arr, arr2); - assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); - // Third array crated via concatenation has contents [42, 31, 0, 42, 31, 0], create - // fourth array via slicing with step size 2, expected contents [42, 0, 31]. - let arr4 = quantum__rt__array_slice_1d( - arr3, - Range { - start: 0, - step: 2, - end: 5, - }, - ); - assert_eq!(__quantum__rt__array_get_size_1d(arr4), 3); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); - // Create fifth array via slicing with reverse iteration, expected contents - // [31, 0, 42]. - let arr5 = quantum__rt__array_slice_1d( - arr3, - Range { - start: 4, - step: -2, - end: 0, - }, - ); - assert_eq!(__quantum__rt__array_get_size_1d(arr5), 3); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); - // Create sixth array with range end less than range start, should succeed and create - // an empty array. - let arr6 = quantum__rt__array_slice_1d( - arr5, - Range { - start: 0, - step: 1, - end: -1, - }, - ); - // Confirm each copy, concatenation, and slice is independent of others. - assert_eq!(__quantum__rt__array_get_size_1d(arr6), 0); - __quantum__rt__array_update_reference_count(arr, -1); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); - __quantum__rt__array_update_reference_count(arr2, -1); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); - __quantum__rt__array_update_reference_count(arr3, -1); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); - __quantum__rt__array_update_reference_count(arr4, -1); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); - assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); - __quantum__rt__array_update_reference_count(arr5, -1); - __quantum__rt__array_update_reference_count(arr6, -1); - } - } - #[allow(clippy::cast_ptr_alignment)] #[test] fn test_array_creation_large_size() { diff --git a/src/Qir/Runtime/stdlib/src/range_support.rs b/src/Qir/Runtime/stdlib/src/range_support.rs index 872a88b12a3..2c9a46ea44a 100644 --- a/src/Qir/Runtime/stdlib/src/range_support.rs +++ b/src/Qir/Runtime/stdlib/src/range_support.rs @@ -65,13 +65,18 @@ pub unsafe extern "C" fn quantum__rt__array_slice_1d( #[cfg(test)] mod tests { use super::*; - use crate::strings::{ - __quantum__rt__string_get_data, __quantum__rt__string_update_reference_count, + use crate::{ + arrays::{ + __quantum__rt__array_concatenate, __quantum__rt__array_copy, + __quantum__rt__array_create_1d, __quantum__rt__array_get_element_ptr_1d, + __quantum__rt__array_get_size_1d, __quantum__rt__array_update_reference_count, + }, + strings::{__quantum__rt__string_get_data, __quantum__rt__string_update_reference_count}, }; use std::ffi::CStr; #[test] - fn test_to_string() { + fn test_range_to_string() { let input4 = Range { start: 0, step: 1, @@ -105,4 +110,92 @@ mod tests { __quantum__rt__string_update_reference_count(str5, -1); } } + + #[test] + fn test_array_slicing() { + let arr = __quantum__rt__array_create_1d(1, 3); + unsafe { + assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); + let first = __quantum__rt__array_get_element_ptr_1d(arr, 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 0); + *first = 42; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 0), 42); + let second = __quantum__rt__array_get_element_ptr_1d(arr, 1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 0); + *second = 31; + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr, 1), 31); + let arr2 = __quantum__rt__array_copy(arr, true); + assert_eq!(__quantum__rt__array_get_size_1d(arr2), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + let arr3 = __quantum__rt__array_concatenate(arr, arr2); + assert_eq!(__quantum__rt__array_get_size_1d(arr3), 6); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + // Third array crated via concatenation has contents [42, 31, 0, 42, 31, 0], create + // fourth array via slicing with step size 2, expected contents [42, 0, 31]. + let arr4 = quantum__rt__array_slice_1d( + arr3, + Range { + start: 0, + step: 2, + end: 5, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr4), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + // Create fifth array via slicing with reverse iteration, expected contents + // [31, 0, 42]. + let arr5 = quantum__rt__array_slice_1d( + arr3, + Range { + start: 4, + step: -2, + end: 0, + }, + ); + assert_eq!(__quantum__rt__array_get_size_1d(arr5), 3); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + // Create sixth array with range end less than range start, should succeed and create + // an empty array. + let arr6 = quantum__rt__array_slice_1d( + arr5, + Range { + start: 0, + step: 1, + end: -1, + }, + ); + // Confirm each copy, concatenation, and slice is independent of others. + assert_eq!(__quantum__rt__array_get_size_1d(arr6), 0); + __quantum__rt__array_update_reference_count(arr, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr2, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + __quantum__rt__array_update_reference_count(arr2, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 1), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 2), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 3), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 4), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr3, 5), 0); + __quantum__rt__array_update_reference_count(arr3, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 0), 42); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr4, 2), 31); + __quantum__rt__array_update_reference_count(arr4, -1); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 0), 31); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 1), 0); + assert_eq!(*__quantum__rt__array_get_element_ptr_1d(arr5, 2), 42); + __quantum__rt__array_update_reference_count(arr5, -1); + __quantum__rt__array_update_reference_count(arr6, -1); + } + } } From cb5b341a92cba54a7de8aa09ed05ea0747d43b77 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 21:54:35 -0700 Subject: [PATCH 43/55] Update strings unit tests --- src/Qir/Runtime/stdlib/src/strings.rs | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/Qir/Runtime/stdlib/src/strings.rs b/src/Qir/Runtime/stdlib/src/strings.rs index 13da297e375..f4209a1d149 100644 --- a/src/Qir/Runtime/stdlib/src/strings.rs +++ b/src/Qir/Runtime/stdlib/src/strings.rs @@ -331,6 +331,36 @@ mod tests { "4.2" ); } + let input1_1 = 4.0; + let str1_1 = __quantum__rt__double_to_string(input1_1); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1_1)) + .to_str() + .unwrap(), + "4.0" + ); + } + let input1_2 = 0.1; + let str1_2 = __quantum__rt__double_to_string(input1_2); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1_2)) + .to_str() + .unwrap(), + "0.1" + ); + } + let input1_3 = 0.100_000_000_01; + let str1_3 = __quantum__rt__double_to_string(input1_3); + unsafe { + assert_eq!( + CStr::from_ptr(__quantum__rt__string_get_data(str1_3)) + .to_str() + .unwrap(), + "0.10000000001" + ); + } let input2 = false; let str2 = __quantum__rt__bool_to_string(input2); unsafe { @@ -363,6 +393,9 @@ mod tests { __quantum__rt__string_update_reference_count(str0, -1); __quantum__rt__string_update_reference_count(str1, -1); + __quantum__rt__string_update_reference_count(str1_1, -1); + __quantum__rt__string_update_reference_count(str1_2, -1); + __quantum__rt__string_update_reference_count(str1_3, -1); __quantum__rt__string_update_reference_count(str2, -1); __quantum__rt__string_update_reference_count(str3, -1); __quantum__rt__string_update_reference_count(str4, -1); From 2ce6a2e8a31e08f7818eb50f07431054d02d6b2c Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 21:56:18 -0700 Subject: [PATCH 44/55] __quantum__rt__fail will print string before panic --- src/Qir/Runtime/stdlib/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Qir/Runtime/stdlib/src/lib.rs b/src/Qir/Runtime/stdlib/src/lib.rs index 8846cc532af..f88ef49b9aa 100644 --- a/src/Qir/Runtime/stdlib/src/lib.rs +++ b/src/Qir/Runtime/stdlib/src/lib.rs @@ -78,6 +78,7 @@ pub extern "C" fn __quantum__rt__memory_allocate(size: u64) -> *mut u8 { /// Panics unconditionally with the given message. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) { + __quantum__rt__message(str); panic!("{}", (*str).to_str().expect("Unable to convert string")); } From fd0226dc24a231bb207b43d0d468623574203b89 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 22:03:00 -0700 Subject: [PATCH 45/55] Fix broken readme links --- src/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/README.md b/src/README.md index fa705e51899..d1a14f50c74 100644 --- a/src/README.md +++ b/src/README.md @@ -22,7 +22,6 @@ aspects of the code style. * [Clang-Tidy Checks](https://clang.llvm.org/extra/clang-tidy/checks/list.html) * Example of the checks used * [Qir/.clang-tidy](Qir/.clang-tidy) - * [Qir/Tests/.clang-tidy](Qir/Tests/.clang-tidy) * [Qir/Samples/StandaloneInputReference/.clang-tidy](Qir/Samples/StandaloneInputReference/.clang-tidy) * Use * Install: On Win and Mac is installed together with LLVM or Clang, to install on Linux search for "tidy" in @@ -53,7 +52,3 @@ Clang-format is a tool that enforces the code style. * Links to the documentation, example of the options are in [Qir/.clang-format](Qir/.clang-format). * Installation: Search for "format" in [Qir/Runtime/prerequisites.ps1](Qir/Runtime/prerequisites.ps1). * Run: [Qir/check-sources-formatted.ps1](Qir/check-sources-formatted.ps1). - -## See also - -* [Coding style and conventions](Qir/Runtime/README.md#coding-style-and-conventions) \ No newline at end of file From 339570891362b4c7922de2867b38c1a809893165 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Tue, 13 Sep 2022 22:52:54 -0700 Subject: [PATCH 46/55] Fix formatting in arrays.rs --- src/Qir/Runtime/stdlib/src/arrays.rs | 65 ++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/arrays.rs b/src/Qir/Runtime/stdlib/src/arrays.rs index 5fe933d7cd6..ba771997dec 100644 --- a/src/Qir/Runtime/stdlib/src/arrays.rs +++ b/src/Qir/Runtime/stdlib/src/arrays.rs @@ -138,7 +138,7 @@ pub unsafe extern "C" fn __quantum__rt__array_update_alias_count( #[cfg(test)] mod tests { - use std::{mem::size_of, convert::TryInto}; + use std::{convert::TryInto, mem::size_of}; use super::*; @@ -276,20 +276,59 @@ mod tests { unsafe { assert_eq!(__quantum__rt__array_get_size_1d(arr), 3); let first = __quantum__rt__array_get_element_ptr_1d(arr, 0).cast::(); - *first = Data{first: 1, second: 2, third: 3}; + *first = Data { + first: 1, + second: 2, + third: 3, + }; let second = __quantum__rt__array_get_element_ptr_1d(arr, 1).cast::(); - *second = Data{first: 10, second: 20, third: 30}; + *second = Data { + first: 10, + second: 20, + third: 30, + }; let third = __quantum__rt__array_get_element_ptr_1d(arr, 2).cast::(); - *third = Data{first: 100, second: 200, third: 300}; - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).first, 1); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).second, 2); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).third, 3); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).first, 10); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).second, 20); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).third, 30); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).first, 100); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).second, 200); - assert_eq!((*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).third, 300); + *third = Data { + first: 100, + second: 200, + third: 300, + }; + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).first, + 1 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).second, + 2 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 0).cast::())).third, + 3 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).first, + 10 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).second, + 20 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 1).cast::())).third, + 30 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).first, + 100 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).second, + 200 + ); + assert_eq!( + (*(__quantum__rt__array_get_element_ptr_1d(arr, 2).cast::())).third, + 300 + ); __quantum__rt__array_update_reference_count(arr, -1); } } From 4ed00e39791a9cc4af156d94680a350df98eddcc Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 14 Sep 2022 13:36:24 -0700 Subject: [PATCH 47/55] Clarifying comments, minor fixes from initial review --- build/steps-codecheck.yml | 10 +++++----- build/steps-init.yml | 3 +++ src/Qir/Runtime/stdlib/src/output_recording.rs | 2 ++ src/Qir/Runtime/stdlib/src/strings.rs | 4 +++- src/Simulation/Native/build-native-simulator.ps1 | 4 ++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/build/steps-codecheck.yml b/build/steps-codecheck.yml index 65c02f6c9ab..f85d8dce325 100644 --- a/build/steps-codecheck.yml +++ b/build/steps-codecheck.yml @@ -14,13 +14,13 @@ steps: packageType: sdk version: '6.0.x' - # QIR Runtime: + # QIR stdlib: - pwsh: src/Qir/Runtime/prerequisites.ps1 - displayName: "Install QIR Runtime Prerequisites" + displayName: "Install QIR stdlib Prerequisites" workingDirectory: $(System.DefaultWorkingDirectory) - pwsh: src/Qir/Runtime/build-qir-stdlib.ps1 - displayName: "Build QIR Runtime" + displayName: "Build QIR stdlib" workingDirectory: $(System.DefaultWorkingDirectory) # Native Simulator (needed to build and run the QIR tests): @@ -33,9 +33,9 @@ steps: displayName: "Build Native Simulator" workingDirectory: $(System.DefaultWorkingDirectory)/src/Simulation/Native - # QIR Runtime Tests: + # QIR stdlib Tests: - pwsh: src/Qir/Runtime/test-qir-stdlib.ps1 - displayName: "Test QIR Runtime" + displayName: "Test QIR stdlib" workingDirectory: $(System.DefaultWorkingDirectory) # QIR Tests: diff --git a/build/steps-init.yml b/build/steps-init.yml index d0283749e8c..044e9170344 100644 --- a/build/steps-init.yml +++ b/build/steps-init.yml @@ -26,6 +26,9 @@ steps: displayName: Windows install rust condition: eq( variables['Agent.OS'], 'Windows_NT' ) +# Installs Rust nightly toolchain and components. +# Note: the llvm-tools-preview component can be removed once QIR range support is simplified. +# See https://github.com/microsoft/qsharp-language/issues/108 - script: | rustup install nightly rustup component add rustfmt clippy llvm-tools-preview diff --git a/src/Qir/Runtime/stdlib/src/output_recording.rs b/src/Qir/Runtime/stdlib/src/output_recording.rs index d9bdc070c00..a2acc271f92 100644 --- a/src/Qir/Runtime/stdlib/src/output_recording.rs +++ b/src/Qir/Runtime/stdlib/src/output_recording.rs @@ -34,6 +34,8 @@ pub extern "C" fn __quantum__rt__double_record_output(val: c_double) { println!( "RESULT\t{}", if (val.floor() - val.ceil()).abs() < c_double::EPSILON { + // The value is a whole number, which by convention is displayed with one decimal point + // to differentiate it from an integer value. format!("{:.1}", val) } else { format!("{}", val) diff --git a/src/Qir/Runtime/stdlib/src/strings.rs b/src/Qir/Runtime/stdlib/src/strings.rs index f4209a1d149..b13a0172ab8 100644 --- a/src/Qir/Runtime/stdlib/src/strings.rs +++ b/src/Qir/Runtime/stdlib/src/strings.rs @@ -101,7 +101,9 @@ pub extern "C" fn __quantum__rt__int_to_string(input: i64) -> *const CString { #[no_mangle] pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString { if (input.floor() - input.ceil()).abs() < c_double::EPSILON { - convert(&format!("{:.1}", input)) + // The value is a whole number, which by convention is displayed with one decimal point + // to differentiate it from an integer value. + convert(&format!("{:.1}", input)) } else { convert(&input) } diff --git a/src/Simulation/Native/build-native-simulator.ps1 b/src/Simulation/Native/build-native-simulator.ps1 index d26528b8de3..257140024e7 100644 --- a/src/Simulation/Native/build-native-simulator.ps1 +++ b/src/Simulation/Native/build-native-simulator.ps1 @@ -1,10 +1,10 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -Write-Host "##[info]Build Native simulator for $Env:BUILD_CONFIGURATION" - & (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) +Write-Host "##[info]Build Native simulator for $Env:BUILD_CONFIGURATION" + if ($IsMacOS) { # To ensure loading succeeds on Mac the install id of the library needs to be updated to use # paths relative to target dynamic load path. Otherwise it will keep the full path encoding in the From 80c0d81ca994c04dd5e8f7ccff9c7f0e8d20da16 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 14 Sep 2022 13:53:01 -0700 Subject: [PATCH 48/55] Fix formatting --- src/Qir/Runtime/stdlib/src/strings.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/strings.rs b/src/Qir/Runtime/stdlib/src/strings.rs index b13a0172ab8..2ea7dbd75dd 100644 --- a/src/Qir/Runtime/stdlib/src/strings.rs +++ b/src/Qir/Runtime/stdlib/src/strings.rs @@ -101,9 +101,9 @@ pub extern "C" fn __quantum__rt__int_to_string(input: i64) -> *const CString { #[no_mangle] pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString { if (input.floor() - input.ceil()).abs() < c_double::EPSILON { - // The value is a whole number, which by convention is displayed with one decimal point - // to differentiate it from an integer value. - convert(&format!("{:.1}", input)) + // The value is a whole number, which by convention is displayed with one decimal point + // to differentiate it from an integer value. + convert(&format!("{:.1}", input)) } else { convert(&input) } From a504342f5660adcbc0b10e05155fab20aa95eae2 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 14 Sep 2022 15:12:04 -0700 Subject: [PATCH 49/55] Add `__quantum__rt__message_record_output` --- src/Qir/Runtime/stdlib/src/output_recording.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Qir/Runtime/stdlib/src/output_recording.rs b/src/Qir/Runtime/stdlib/src/output_recording.rs index a2acc271f92..e7b8443b1e4 100644 --- a/src/Qir/Runtime/stdlib/src/output_recording.rs +++ b/src/Qir/Runtime/stdlib/src/output_recording.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. #![deny(clippy::all, clippy::pedantic)] -use std::os::raw::c_double; +use std::{ffi::CString, os::raw::c_double}; #[no_mangle] pub extern "C" fn __quantum__rt__array_start_record_output() { @@ -47,3 +47,17 @@ pub extern "C" fn __quantum__rt__double_record_output(val: c_double) { pub extern "C" fn __quantum__rt__bool_record_output(val: bool) { println!("RESULT\t{}", val); } + +/// # Safety +/// +/// This function should only be called with strings created by `__quantum__rt__string_*` functions. +#[no_mangle] +pub unsafe extern "C" fn __quantum__rt__message_record_output(str: *const CString) { + println!( + "INFO\t{}", + (*str) + .to_str() + .expect("Unable to convert input string") + .escape_default() + ); +} From a25e80e49dd66594357457e91610b23b338b8fde Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 14 Sep 2022 17:11:54 -0700 Subject: [PATCH 50/55] Clean up file encoding for .cargo/config.toml --- .cargo/config.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 0db2a34893d..ea5cc6b6ada 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,3 @@ -[target.'cfg(target_os = "windows")'] -rustflags = ["-C", "control-flow-guard"] - +[target.'cfg(target_os = "windows")'] +rustflags = ["-C", "control-flow-guard"] + From c7168d397e3f5c5322d6a9161ff204f218d4b850 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Wed, 14 Sep 2022 17:17:35 -0700 Subject: [PATCH 51/55] Updating script patterns --- build/build.ps1 | 4 ++-- build/test.ps1 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/build.ps1 b/build/build.ps1 index 41f19d8cdbf..cd3c2e471a9 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -7,8 +7,8 @@ $ErrorActionPreference = 'Stop' $all_ok = $True if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirstdlib = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirstdlib/build-qir-stdlib.ps1" + $qirStdLibPath = (Join-Path $PSScriptRoot .. src Qir Runtime build-qir-stdlib.ps1) + & $qirStdLibPath if ($LastExitCode -ne 0) { $script:all_ok = $False } diff --git a/build/test.ps1 b/build/test.ps1 index 80734c97d7d..c26d928fd0d 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -43,8 +43,8 @@ function Test-One { Test-One '../Simulation.sln' if ($Env:ENABLE_QIRRUNTIME -ne "false") { - $qirstdlib = (Join-Path $PSScriptRoot "../src/Qir/Runtime") - & "$qirstdlib/test-qir-stdlib.ps1" + $qirStdLibPath = (Join-Path $PSScriptRoot .. src Qir Runtime test-qir-stdlib.ps1) + & $qirStdLibPath if ($LastExitCode -ne 0) { $script:all_ok = $False } From c494888ec9e9ff53a106fe403a648baf923454e1 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 15 Sep 2022 10:37:22 -0700 Subject: [PATCH 52/55] Updates from PR feedback --- bootstrap.ps1 | 2 + build/build.ps1 | 2 + build/set-env.ps1 | 2 + build/test.ps1 | 2 + src/Qir/Runtime/.cargo/config.toml | 2 + src/Qir/Runtime/build-qir-stdlib.ps1 | 2 +- src/Qir/Runtime/stdlib/build.rs | 4 +- src/Qir/Runtime/stdlib/include/qir_stdlib.def | 6 + src/Qir/Runtime/stdlib/include/qir_stdlib.h | 23 +++- src/Qir/Runtime/stdlib/src/arrays.rs | 85 +++++--------- src/Qir/Runtime/stdlib/src/bigints.rs | 106 ++++-------------- src/Qir/Runtime/stdlib/src/callables.rs | 43 ++----- src/Qir/Runtime/stdlib/src/lib.rs | 28 ++--- src/Qir/Runtime/stdlib/src/math.rs | 3 +- .../Runtime/stdlib/src/output_recording.rs | 17 +-- src/Qir/Runtime/stdlib/src/range_support.rs | 21 ++-- src/Qir/Runtime/stdlib/src/strings.rs | 47 +++----- src/Qir/Runtime/stdlib/src/tuples.rs | 18 +-- src/Qir/Runtime/test-qir-stdlib.ps1 | 2 +- 19 files changed, 140 insertions(+), 275 deletions(-) create mode 100644 src/Qir/Runtime/.cargo/config.toml diff --git a/bootstrap.ps1 b/bootstrap.ps1 index ac8a020a427..472344c779f 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + $ErrorActionPreference = 'Stop' Push-Location (Join-Path $PSScriptRoot "build") diff --git a/build/build.ps1 b/build/build.ps1 index cd3c2e471a9..29df268aa83 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + $ErrorActionPreference = 'Stop' & "$PSScriptRoot/set-env.ps1" diff --git a/build/set-env.ps1 b/build/set-env.ps1 index 396b8cc2be7..fe92c6c23b8 100644 --- a/build/set-env.ps1 +++ b/build/set-env.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + $ErrorActionPreference = 'Stop' Write-Host "Setting up build environment variables" diff --git a/build/test.ps1 b/build/test.ps1 index c26d928fd0d..955ba9c23c5 100644 --- a/build/test.ps1 +++ b/build/test.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +#Requires -PSEdition Core + & "$PSScriptRoot/set-env.ps1" $all_ok = $True diff --git a/src/Qir/Runtime/.cargo/config.toml b/src/Qir/Runtime/.cargo/config.toml new file mode 100644 index 00000000000..cb0dc23f903 --- /dev/null +++ b/src/Qir/Runtime/.cargo/config.toml @@ -0,0 +1,2 @@ +[profile.release] +panic = 'abort' diff --git a/src/Qir/Runtime/build-qir-stdlib.ps1 b/src/Qir/Runtime/build-qir-stdlib.ps1 index 2e2c57431da..90bf98d2f34 100644 --- a/src/Qir/Runtime/build-qir-stdlib.ps1 +++ b/src/Qir/Runtime/build-qir-stdlib.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. #Requires -PSEdition Core diff --git a/src/Qir/Runtime/stdlib/build.rs b/src/Qir/Runtime/stdlib/build.rs index 3c4d47bfb32..6cd08229cfd 100644 --- a/src/Qir/Runtime/stdlib/build.rs +++ b/src/Qir/Runtime/stdlib/build.rs @@ -9,13 +9,15 @@ use llvm_tools::LlvmTools; fn main() -> Result<(), String> { // Compile the LLVM IR bridge file. Requires the llvm-tools-preview component. + // This is only needed for range support, and this entire build.rs can be dropped when that functionality is + // no longer needed. let out_dir = env::var_os("OUT_DIR") .map(PathBuf::from) .ok_or_else(|| "Environment variable OUT_DIR not defined.".to_string())?; let llvm_tools = LlvmTools::new().map_err(|err| { format!( - "Failed to locate llvm tools: {:?}. Is the llvm-tools-preview component installed?", + "Failed to locate llvm tools: {:?}. Is the llvm-tools-preview component installed? Try using `rustup component add llvm-tools-preview`.", err ) })?; diff --git a/src/Qir/Runtime/stdlib/include/qir_stdlib.def b/src/Qir/Runtime/stdlib/include/qir_stdlib.def index 940cf636dab..af0236512b5 100644 --- a/src/Qir/Runtime/stdlib/include/qir_stdlib.def +++ b/src/Qir/Runtime/stdlib/include/qir_stdlib.def @@ -1,3 +1,9 @@ +; Copyright (c) Microsoft Corporation. +; Licensed under the MIT License. + +; This file lists the APIs defined by the qir_stdlib so that they can be re-exported on Windows platforms. +; See https://docs.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-def-files + EXPORTS __quantum__rt__memory_allocate __quantum__rt__fail diff --git a/src/Qir/Runtime/stdlib/include/qir_stdlib.h b/src/Qir/Runtime/stdlib/include/qir_stdlib.h index 22737028ea0..213ffa90d5a 100644 --- a/src/Qir/Runtime/stdlib/include/qir_stdlib.h +++ b/src/Qir/Runtime/stdlib/include/qir_stdlib.h @@ -1,6 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#pragma once +#ifndef QIR_STDLIB_H +#define QIR_STDLIB_H + #include #ifdef _WIN32 @@ -20,6 +24,13 @@ extern "C" struct QirTuple; struct QirBigInt; + // Type aliases with the same name are added for ease of use in C code. + typedef struct QirString QirString; + typedef struct QirArray QirArray; + typedef struct QirCallable QirCallable; + typedef struct QirTuple QirTuple; + typedef struct QirBigInt QirBigInt; + enum PauliId : int8_t { PauliId_I = 0, @@ -32,7 +43,7 @@ extern "C" typedef void (*t_CaptureCallback)(QirTuple*, int32_t); // Returns a pointer to the malloc-allocated block. - QIR_SHARED_API char* __quantum__rt__memory_allocate(uint64_t size); // NOLINT + QIR_SHARED_API void* __quantum__rt__memory_allocate(uint64_t size); // NOLINT // Fail the computation with the given error message. [[noreturn]] QIR_SHARED_API void __quantum__rt__fail(QirString* msg); // NOLINT @@ -149,7 +160,7 @@ extern "C" // Creates a big integer with the initial value specified by the i8 array. The 0-th element of the array is the // highest-order byte, followed by the first element, etc. - QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_array(int, char*); // NOLINT + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_create_array(int32_t, char*); // NOLINT // Adds the given integer value to the reference count for the big integer. Deallocates the big integer if the // reference count becomes 0. The behavior is undefined if the reference count becomes negative. @@ -174,7 +185,7 @@ extern "C" QIR_SHARED_API QirBigInt* __quantum__rt__bigint_modulus(QirBigInt*, QirBigInt*); // NOLINT // Returns the big integer raised to the integer power. - QIR_SHARED_API QirBigInt* __quantum__rt__bigint_power(QirBigInt*, int); // NOLINT + QIR_SHARED_API QirBigInt* __quantum__rt__bigint_power(QirBigInt*, int32_t); // NOLINT // Returns the bitwise-AND of two big integers. QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitand(QirBigInt*, QirBigInt*); // NOLINT @@ -188,10 +199,10 @@ extern "C" // Returns the bitwise complement of the big integer. QIR_SHARED_API QirBigInt* __quantum__rt__bigint_bitnot(QirBigInt*); // NOLINT - // Returns the big integer arithmetically shifted left by the integer amount of bits. + // Returns the big integer arithmetically shifted left by the (positive) integer amount of bits. QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftleft(QirBigInt*, int64_t); // NOLINT - // Returns the big integer arithmetically shifted right by the integer amount of bits. + // Returns the big integer arithmetically shifted right by the (positive) integer amount of bits. QIR_SHARED_API QirBigInt* __quantum__rt__bigint_shiftright(QirBigInt*, int64_t); // NOLINT // Returns true if the two big integers are equal, false otherwise. @@ -228,3 +239,5 @@ extern "C" #ifdef __cplusplus } // extern "C" #endif + +#endif diff --git a/src/Qir/Runtime/stdlib/src/arrays.rs b/src/Qir/Runtime/stdlib/src/arrays.rs index ba771997dec..6ef3828c096 100644 --- a/src/Qir/Runtime/stdlib/src/arrays.rs +++ b/src/Qir/Runtime/stdlib/src/arrays.rs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] -use crate::update_counts; +use crate::{__quantum__rt__fail, strings::convert, update_counts}; use std::{rc::Rc, usize}; #[derive(Debug, Clone)] @@ -11,21 +10,18 @@ pub struct QirArray { pub(crate) data: Vec, } -/// # Panics -/// -/// This function panics if the passed in sizes do not fit into the usize type for the -/// current platform. #[no_mangle] -pub extern "C" fn __quantum__rt__array_create_1d(elem_size: u32, size: u64) -> *const QirArray { - let elem_size = elem_size.try_into().unwrap(); - let size: usize = size.try_into().unwrap(); - let data = vec![0_u8; elem_size * size]; +pub extern "C" fn __quantum__rt__array_create_1d(elem_size: u32, count: u64) -> *const QirArray { + let elem_size = elem_size + .try_into() + .expect("The `elem_size` argument should fit in the `usize` type for this platform."); + let count: usize = count + .try_into() + .expect("The `count` argument should fit in the `usize` type for this platform."); + let data = vec![0_u8; elem_size * count]; Rc::into_raw(Rc::new(QirArray { elem_size, data })) } -/// # Safety -/// -/// This function should only be called with an array created by `__quantum__rt__array_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_copy( arr: *const QirArray, @@ -34,21 +30,15 @@ pub unsafe extern "C" fn __quantum__rt__array_copy( let rc = Rc::from_raw(arr); if force || Rc::weak_count(&rc) > 0 { let copy = rc.as_ref().clone(); - let _ = Rc::into_raw(rc); + Rc::into_raw(rc); Rc::into_raw(Rc::new(copy)) } else { - let _ = Rc::into_raw(Rc::clone(&rc)); - let _ = Rc::into_raw(rc); + Rc::into_raw(Rc::clone(&rc)); + Rc::into_raw(rc); arr } } -/// # Safety -/// -/// This function should only be called with arrays created by `__quantum__rt__array_*` functions. -/// # Panics -/// -/// This function will panic if the given arrays use different element sizes. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_concatenate( arr1: *const QirArray, @@ -56,12 +46,12 @@ pub unsafe extern "C" fn __quantum__rt__array_concatenate( ) -> *const QirArray { let array1 = Rc::from_raw(arr1); let array2 = Rc::from_raw(arr2); - assert!( - array1.elem_size == array2.elem_size, - "Cannot concatenate arrays with differing element sizes: {} vs {}", - array1.elem_size, - array2.elem_size - ); + if array1.elem_size != array2.elem_size { + __quantum__rt__fail(convert(&format!( + "Cannot concatenate arrays with differing element sizes: {} vs {}", + array1.elem_size, array2.elem_size + ))); + } let mut new_array = QirArray { elem_size: array1.elem_size, @@ -75,48 +65,34 @@ pub unsafe extern "C" fn __quantum__rt__array_concatenate( copy.copy_from_slice(array2.data.as_slice()); new_array.data.append(&mut copy); - let _ = Rc::into_raw(array1); - let _ = Rc::into_raw(array2); + Rc::into_raw(array1); + Rc::into_raw(array2); Rc::into_raw(Rc::new(new_array)) } -/// # Safety -/// -/// This function should only be called with an array created by `__quantum__rt__array_*` functions. -/// # Panics -/// -/// This function panics if the array size is larger than u64. This shouldn't happen. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const QirArray) -> u64 { let array = Rc::from_raw(arr); - let size = array.data.len() / array.elem_size; - let _ = Rc::into_raw(array); - size.try_into().unwrap() + let len = array.data.len() / array.elem_size; + Rc::into_raw(array); + len.try_into() + .expect("Length of array should always fit in a 64-bit integer.") } -/// # Safety -/// -/// This function should only be called with an array created by `__quantum__rt__array_*` functions. -/// # Panics -/// -/// This function panics if the given index is larger than the usize type for the current platform. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( arr: *const QirArray, index: u64, ) -> *mut i8 { let array = Rc::from_raw(arr); - let i: usize = index.try_into().unwrap(); - let ptr = array.data.as_ptr().add(array.elem_size * i) as *mut i8; - let _ = Rc::into_raw(array); + let index: usize = index + .try_into() + .expect("Indices into an array should fit into the `usize` "); + let ptr = array.data.as_ptr().add(array.elem_size * index) as *mut i8; + Rc::into_raw(array); ptr } -/// # Safety -/// -/// This function should only be called with an array created by `__quantum__rt__array_*` functions. -/// If the reference count after update is less than or equal to zero, the array is cleaned up -/// and the pointer is no longer valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_update_reference_count( arr: *const QirArray, @@ -125,9 +101,6 @@ pub unsafe extern "C" fn __quantum__rt__array_update_reference_count( update_counts(arr, update, false); } -/// # Safety -/// -/// This function should only be called with an array created by `__quantum__rt__array_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_update_alias_count( arr: *const QirArray, diff --git a/src/Qir/Runtime/stdlib/src/bigints.rs b/src/Qir/Runtime/stdlib/src/bigints.rs index 71956a35d4b..987b37a5010 100644 --- a/src/Qir/Runtime/stdlib/src/bigints.rs +++ b/src/Qir/Runtime/stdlib/src/bigints.rs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] use crate::update_counts; use num_bigint::BigInt; @@ -11,10 +10,6 @@ pub extern "C" fn __quantum__rt__bigint_create_i64(input: i64) -> *const BigInt Rc::into_raw(Rc::new(input.into())) } -/// # Safety -/// -/// This function expects the second argument to be a well-formed C-style array of signed big-endian bytes -/// with the size of that array passed as the first argument. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_create_array( size: u32, @@ -25,31 +20,18 @@ pub unsafe extern "C" fn __quantum__rt__bigint_create_array( ))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_get_data(input: *const BigInt) -> *const u8 { ManuallyDrop::new((*input).to_signed_bytes_be()).as_ptr() } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. -/// # Panics -/// -/// This function will panic if the length of the QIR bigint as an array is larger than can fit in a u32. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_get_length(input: *const BigInt) -> u32 { let size = (*input).to_signed_bytes_be().len(); - size.try_into().unwrap() + size.try_into() + .expect("Length of bigint representation too large for 32-bit integer.") } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. -/// If the reference count after update is less than or equal to zero, the QIR bigint is cleaned up -/// and the pointer is no longer valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_update_reference_count( input: *const BigInt, @@ -58,17 +40,11 @@ pub unsafe extern "C" fn __quantum__rt__bigint_update_reference_count( update_counts(input, update, false); } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_negate(input: *const BigInt) -> *const BigInt { Rc::into_raw(Rc::new(&(*input) * -1)) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_add( lhs: *const BigInt, @@ -77,9 +53,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_add( Rc::into_raw(Rc::new(&(*lhs) + &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_subtract( lhs: *const BigInt, @@ -88,9 +61,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_subtract( Rc::into_raw(Rc::new(&(*lhs) - &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_multiply( lhs: *const BigInt, @@ -99,9 +69,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_multiply( Rc::into_raw(Rc::new(&(*lhs) * &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_divide( lhs: *const BigInt, @@ -110,9 +77,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_divide( Rc::into_raw(Rc::new(&(*lhs) / &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_modulus( lhs: *const BigInt, @@ -121,9 +85,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_modulus( Rc::into_raw(Rc::new(&(*lhs) % &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_power( base: *const BigInt, @@ -132,9 +93,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_power( Rc::into_raw(Rc::new((*base).pow(exponent))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_bitand( lhs: *const BigInt, @@ -143,9 +101,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_bitand( Rc::into_raw(Rc::new(&(*lhs) & &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_bitor( lhs: *const BigInt, @@ -154,9 +109,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_bitor( Rc::into_raw(Rc::new(&(*lhs) | &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_bitxor( lhs: *const BigInt, @@ -165,17 +117,11 @@ pub unsafe extern "C" fn __quantum__rt__bigint_bitxor( Rc::into_raw(Rc::new(&(*lhs) ^ &(*rhs))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_bitnot(input: *const BigInt) -> *const BigInt { Rc::into_raw(Rc::new(!&(*input))) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_shiftleft( input: *const BigInt, @@ -184,9 +130,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_shiftleft( Rc::into_raw(Rc::new(&(*input) << amount)) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_shiftright( input: *const BigInt, @@ -195,9 +138,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_shiftright( Rc::into_raw(Rc::new(&(*input) >> amount)) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_equal( lhs: *const BigInt, @@ -206,9 +146,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_equal( (*lhs) == (*rhs) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_greater( lhs: *const BigInt, @@ -217,9 +154,6 @@ pub unsafe extern "C" fn __quantum__rt__bigint_greater( (*lhs) > (*rhs) } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_greater_eq( lhs: *const BigInt, @@ -275,15 +209,16 @@ mod tests { assert_eq!(*bigint_7, (-42).try_into().unwrap()); let bigint_8 = __quantum__rt__bigint_power(bigint_7, 3); assert_eq!(*bigint_8, (-74088).try_into().unwrap()); - __quantum__rt__bigint_update_reference_count(bigint_0, -1); - __quantum__rt__bigint_update_reference_count(bigint_1, -1); - __quantum__rt__bigint_update_reference_count(bigint_2, -1); - __quantum__rt__bigint_update_reference_count(bigint_3, -1); - __quantum__rt__bigint_update_reference_count(bigint_4, -1); - __quantum__rt__bigint_update_reference_count(bigint_5, -1); - __quantum__rt__bigint_update_reference_count(bigint_6, -1); - __quantum__rt__bigint_update_reference_count(bigint_7, -1); __quantum__rt__bigint_update_reference_count(bigint_8, -1); + __quantum__rt__bigint_update_reference_count(bigint_7, -1); + __quantum__rt__bigint_update_reference_count(bigint_6, -1); + __quantum__rt__bigint_update_reference_count(bigint_5, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_3, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); } } @@ -304,14 +239,15 @@ mod tests { assert_eq!(*bigint_6, (168).try_into().unwrap()); let bigint_7 = __quantum__rt__bigint_shiftright(bigint_6, 3); assert_eq!(*bigint_7, (21).try_into().unwrap()); - __quantum__rt__bigint_update_reference_count(bigint_0, -1); - __quantum__rt__bigint_update_reference_count(bigint_1, -1); - __quantum__rt__bigint_update_reference_count(bigint_2, -1); - __quantum__rt__bigint_update_reference_count(bigint_3, -1); - __quantum__rt__bigint_update_reference_count(bigint_4, -1); - __quantum__rt__bigint_update_reference_count(bigint_5, -1); - __quantum__rt__bigint_update_reference_count(bigint_6, -1); __quantum__rt__bigint_update_reference_count(bigint_7, -1); + __quantum__rt__bigint_update_reference_count(bigint_6, -1); + __quantum__rt__bigint_update_reference_count(bigint_5, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_4, -1); + __quantum__rt__bigint_update_reference_count(bigint_3, -1); + __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); } } @@ -327,9 +263,9 @@ mod tests { assert!(__quantum__rt__bigint_greater_eq(bigint_0, bigint_2)); assert!(__quantum__rt__bigint_greater_eq(bigint_1, bigint_2)); assert!(!__quantum__rt__bigint_greater_eq(bigint_0, bigint_1)); - __quantum__rt__bigint_update_reference_count(bigint_0, -1); - __quantum__rt__bigint_update_reference_count(bigint_1, -1); __quantum__rt__bigint_update_reference_count(bigint_2, -1); + __quantum__rt__bigint_update_reference_count(bigint_1, -1); + __quantum__rt__bigint_update_reference_count(bigint_0, -1); } } } diff --git a/src/Qir/Runtime/stdlib/src/callables.rs b/src/Qir/Runtime/stdlib/src/callables.rs index e305c1960e6..55d2816592d 100644 --- a/src/Qir/Runtime/stdlib/src/callables.rs +++ b/src/Qir/Runtime/stdlib/src/callables.rs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] use crate::{ arrays::{ @@ -17,7 +16,7 @@ pub struct Callable { mem_table: *mut *mut u8, cap_tuple: *mut u8, is_adj: RefCell, - is_ctl: RefCell, + ctls_count: RefCell, } #[no_mangle] @@ -31,13 +30,10 @@ pub extern "C" fn __quantum__rt__callable_create( mem_table, cap_tuple, is_adj: RefCell::new(false), - is_ctl: RefCell::new(0), + ctls_count: RefCell::new(0), })) } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn __quantum__rt__callable_invoke( @@ -47,7 +43,7 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( ) { let call = Rc::from_raw(callable); let index = (if *call.is_adj.borrow() { 1 } else { 0 }) - + (if *call.is_ctl.borrow() > 0 { 2 } else { 0 }); + + (if *call.ctls_count.borrow() > 0 { 2 } else { 0 }); // Collect any nested controls into a single control list. let mut args_copy: *mut *const Vec = std::ptr::null_mut(); @@ -55,13 +51,13 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( // Copy the tuple so we can potentially edit it. args_copy = __quantum__rt__tuple_copy(args_tup.cast::<*const Vec>(), true); - if *call.is_ctl.borrow() > 0 { + if *call.ctls_count.borrow() > 0 { // If there are any controls, increment the reference count on the control list. This is just // to balance the decrement that will happen in the loop and at the end of invoking the callable // to ensure the original, non-owned list does not get incorrectly cleaned up. __quantum__rt__array_update_reference_count(*args_copy.cast::<*const QirArray>(), 1); - let mut ctl_count = *call.is_ctl.borrow(); + let mut ctl_count = *call.ctls_count.borrow(); while ctl_count > 1 { let ctls = *args_copy.cast::<*const QirArray>(); let inner_tuple = *args_copy @@ -97,7 +93,7 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( args_copy.cast::(), res_tup, ); - if *call.is_ctl.borrow() > 0 { + if *call.ctls_count.borrow() > 0 { __quantum__rt__array_update_reference_count(*args_copy.cast::<*const QirArray>(), -1); } if !args_copy.is_null() { @@ -106,9 +102,6 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( let _ = Rc::into_raw(call); } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_copy( callable: *const Callable, @@ -126,9 +119,6 @@ pub unsafe extern "C" fn __quantum__rt__callable_copy( } } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_make_adjoint(callable: *const Callable) { let call = Rc::from_raw(callable); @@ -136,21 +126,13 @@ pub unsafe extern "C" fn __quantum__rt__callable_make_adjoint(callable: *const C let _ = Rc::into_raw(call); } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_make_controlled(callable: *const Callable) { let call = Rc::from_raw(callable); - let _ = call.is_ctl.replace_with(|&mut old| old + 1); + let _ = call.ctls_count.replace_with(|&mut old| old + 1); let _ = Rc::into_raw(call); } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. -/// If the reference count after update is less than or equal to zero, the callable is cleaned up -/// and the pointer is no longer valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_update_reference_count( callable: *const Callable, @@ -159,9 +141,6 @@ pub unsafe extern "C" fn __quantum__rt__callable_update_reference_count( update_counts(callable, update, false); } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_update_alias_count( callable: *const Callable, @@ -170,11 +149,6 @@ pub unsafe extern "C" fn __quantum__rt__callable_update_alias_count( update_counts(callable, update, true); } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. -/// If the reference count after update is less than or equal to zero, the capture tuple is cleaned up -/// and the pointer is no longer valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__capture_update_reference_count( callable: *const Callable, @@ -187,9 +161,6 @@ pub unsafe extern "C" fn __quantum__rt__capture_update_reference_count( let _ = Rc::into_raw(call); } -/// # Safety -/// -/// This function should only be called with a callable created by `__quantum__rt__callable_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__capture_update_alias_count( callable: *const Callable, diff --git a/src/Qir/Runtime/stdlib/src/lib.rs b/src/Qir/Runtime/stdlib/src/lib.rs index f88ef49b9aa..1ec69b6a1f0 100644 --- a/src/Qir/Runtime/stdlib/src/lib.rs +++ b/src/Qir/Runtime/stdlib/src/lib.rs @@ -1,8 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #![deny(clippy::all, clippy::pedantic)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::missing_panics_doc)] -// Rust Implementation for Quantum Intermediate Representation +//! # Rust Implementation for Quantum Intermediate Representation +//! This library implements the classical runtime functions described in the [QIR specification](https://github.com/qir-alliance/qir-spec). + +// FUTURE: We should add microbenchmarks to verify behavior of these APIs and have a baseline on how changes affect +// peformance of the APIs. pub mod arrays; pub mod bigints; @@ -62,29 +68,23 @@ pub enum Pauli { Y = 3, } -/// # Panics -/// -/// Will panic if unable to allocate memory. #[no_mangle] pub extern "C" fn __quantum__rt__memory_allocate(size: u64) -> *mut u8 { - (vec![0_u8; size.try_into().unwrap()]).leak().as_mut_ptr() + (vec![ + 0_u8; + size.try_into() + .expect("Memory size is too large for `usize` type on this platform.") + ]) + .leak() + .as_mut_ptr() } -/// # Safety -/// -/// This function should only be called with a string created by `__quantum__rt__string_*` functions. -/// # Panics -/// -/// Panics unconditionally with the given message. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__fail(str: *const CString) { __quantum__rt__message(str); panic!("{}", (*str).to_str().expect("Unable to convert string")); } -/// # Safety -/// -/// This function should only be called with a string created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__message(str: *const CString) { println!("{}", (*str).to_str().expect("Unable to convert string")); diff --git a/src/Qir/Runtime/stdlib/src/math.rs b/src/Qir/Runtime/stdlib/src/math.rs index 3a0aa17406e..327dbdfbfb1 100644 --- a/src/Qir/Runtime/stdlib/src/math.rs +++ b/src/Qir/Runtime/stdlib/src/math.rs @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] -// TODO: transition math functions to `__quantum__rt` once compiler support is ready. +// TODO: transition math functions to `__quantum__rt` once compiler support is ready (https://github.com/microsoft/qsharp-compiler/issues/1557). use rand::Rng; use std::os::raw::c_double; diff --git a/src/Qir/Runtime/stdlib/src/output_recording.rs b/src/Qir/Runtime/stdlib/src/output_recording.rs index e7b8443b1e4..31fcd06f493 100644 --- a/src/Qir/Runtime/stdlib/src/output_recording.rs +++ b/src/Qir/Runtime/stdlib/src/output_recording.rs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] use std::{ffi::CString, os::raw::c_double}; +use crate::strings::double_to_string; + #[no_mangle] pub extern "C" fn __quantum__rt__array_start_record_output() { println!("RESULT\tARRAY_START"); @@ -31,16 +32,7 @@ pub extern "C" fn __quantum__rt__int_record_output(val: i64) { #[no_mangle] pub extern "C" fn __quantum__rt__double_record_output(val: c_double) { - println!( - "RESULT\t{}", - if (val.floor() - val.ceil()).abs() < c_double::EPSILON { - // The value is a whole number, which by convention is displayed with one decimal point - // to differentiate it from an integer value. - format!("{:.1}", val) - } else { - format!("{}", val) - } - ); + println!("RESULT\t{}", double_to_string(val)); } #[no_mangle] @@ -48,9 +40,6 @@ pub extern "C" fn __quantum__rt__bool_record_output(val: bool) { println!("RESULT\t{}", val); } -/// # Safety -/// -/// This function should only be called with strings created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__message_record_output(str: *const CString) { println!( diff --git a/src/Qir/Runtime/stdlib/src/range_support.rs b/src/Qir/Runtime/stdlib/src/range_support.rs index 2c9a46ea44a..eaddc2e2e2f 100644 --- a/src/Qir/Runtime/stdlib/src/range_support.rs +++ b/src/Qir/Runtime/stdlib/src/range_support.rs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] // Functionality in this file can be removed when range support is dropped from the QIR runtime. @@ -25,20 +24,16 @@ pub extern "C" fn quantum__rt__range_to_string(input: Range) -> *const CString { convert(&range_str) } -/// # Safety -/// -/// This function should only be called with an array created by `__quantum__rt__array_*` functions. -/// # Panics -/// -/// This function will panic if the item size in the array or the range step size is larger than what can be stored in an -/// u64. This should never happen. #[no_mangle] pub unsafe extern "C" fn quantum__rt__array_slice_1d( arr: *const QirArray, range: Range, ) -> *const QirArray { let array = Rc::from_raw(arr); - let item_size: i64 = array.elem_size.try_into().unwrap(); + let item_size: i64 = array + .elem_size + .try_into() + .expect("Array element size too large for `usize` type on this platform."); let mut slice = QirArray { elem_size: array.elem_size, data: Vec::new(), @@ -50,8 +45,12 @@ pub unsafe extern "C" fn quantum__rt__array_slice_1d( }; let step: i64 = range.step.abs(); - for i in iter.step_by((step * item_size).try_into().unwrap()) { - let index = i.try_into().unwrap(); + for i in iter.step_by((step * item_size).try_into().expect( + "Range step multiplied by item size is too large for `usize` type on this platform", + )) { + let index = i + .try_into() + .expect("Item index too large for `usize` type on this platform."); let mut copy = Vec::new(); copy.resize(array.elem_size, 0_u8); copy.copy_from_slice(&array.data[index..index + array.elem_size]); diff --git a/src/Qir/Runtime/stdlib/src/strings.rs b/src/Qir/Runtime/stdlib/src/strings.rs index 2ea7dbd75dd..74bef8badf4 100644 --- a/src/Qir/Runtime/stdlib/src/strings.rs +++ b/src/Qir/Runtime/stdlib/src/strings.rs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] use crate::update_counts; use crate::Pauli; @@ -11,39 +10,26 @@ use std::{ rc::Rc, }; -/// # Safety -/// -/// This function should only be called with a valid, null-terminated, C-style string. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_create(str: *mut c_char) -> *const CString { let cstring = CString::new(CStr::from_ptr(str).to_owned()).expect("Failed to create %String"); Rc::into_raw(Rc::new(cstring)) } -/// # Safety -/// -/// This function should only be called with a string created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_get_data(str: *const CString) -> *const c_char { (*str).as_bytes_with_nul().as_ptr().cast::() } -/// # Safety -/// -/// This function should only be called with a string created by `__quantum__rt__string_*` functions. -/// # Panics -/// -/// Will panic if length is larger than will fit in an 32-bit integer. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_get_length(str: *const CString) -> u32 { - (*str).as_bytes().len().try_into().unwrap() + (*str) + .as_bytes() + .len() + .try_into() + .expect("String length is too large for 32-bit integer.") } -/// # Safety -/// -/// This function should only be called with a string created by `__quantum__rt__string_*` functions. -/// If the reference count after update is less than or equal to zero, the string is cleaned up -/// and the pointer is no longer valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_update_reference_count( str: *const CString, @@ -52,9 +38,6 @@ pub unsafe extern "C" fn __quantum__rt__string_update_reference_count( update_counts(str, update, false); } -/// # Safety -/// -/// This function should only be called with strings created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_concatenate( s1: *const CString, @@ -68,9 +51,6 @@ pub unsafe extern "C" fn __quantum__rt__string_concatenate( )) } -/// # Safety -/// -/// This function should only be called with strings created by `__quantum__rt__string_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__string_equal( s1: *const CString, @@ -86,7 +66,7 @@ where unsafe { __quantum__rt__string_create( CString::new(input.to_string()) - .unwrap() + .expect("Unable to allocate string for conversion.") .as_bytes_with_nul() .as_ptr() as *mut i8, ) @@ -98,17 +78,21 @@ pub extern "C" fn __quantum__rt__int_to_string(input: i64) -> *const CString { convert(&input) } -#[no_mangle] -pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString { +pub(crate) fn double_to_string(input: c_double) -> String { if (input.floor() - input.ceil()).abs() < c_double::EPSILON { // The value is a whole number, which by convention is displayed with one decimal point // to differentiate it from an integer value. - convert(&format!("{:.1}", input)) + format!("{:.1}", input) } else { - convert(&input) + format!("{}", input) } } +#[no_mangle] +pub extern "C" fn __quantum__rt__double_to_string(input: c_double) -> *const CString { + convert(&double_to_string(input)) +} + #[no_mangle] pub extern "C" fn __quantum__rt__bool_to_string(input: bool) -> *const CString { convert(&input) @@ -124,9 +108,6 @@ pub extern "C" fn __quantum__rt__pauli_to_string(input: Pauli) -> *const CString } } -/// # Safety -/// -/// This function should only be called with a QIR bigint created by the `__quantum__rt__bigint_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__bigint_to_string(input: *const BigInt) -> *const CString { convert(&*input) diff --git a/src/Qir/Runtime/stdlib/src/tuples.rs b/src/Qir/Runtime/stdlib/src/tuples.rs index e1ab5d15351..2aa46ec3516 100644 --- a/src/Qir/Runtime/stdlib/src/tuples.rs +++ b/src/Qir/Runtime/stdlib/src/tuples.rs @@ -1,19 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![deny(clippy::all, clippy::pedantic)] use crate::update_counts; use std::{mem::size_of, rc::Rc, usize}; -/// # Panics -/// -/// Will panic if the given size is larger than pointer width for the platform. #[allow(clippy::cast_ptr_alignment)] #[no_mangle] pub extern "C" fn __quantum__rt__tuple_create(size: u64) -> *mut *const Vec { let mut mem = vec![ 0_u8; - >::try_from(size).unwrap() + >::try_from(size) + .expect("Tuple size too large for `usize` type on this platform.") + size_of::<*const Vec>() ]; @@ -24,9 +21,6 @@ pub extern "C" fn __quantum__rt__tuple_create(size: u64) -> *mut *const Vec } } -/// # Safety -/// -/// This function should only be called with a tuple created by `__quantum__rt__tuple_*` functions. #[allow(clippy::cast_ptr_alignment)] #[no_mangle] pub unsafe extern "C" fn __quantum__rt__tuple_copy( @@ -47,11 +41,6 @@ pub unsafe extern "C" fn __quantum__rt__tuple_copy( } } -/// # Safety -/// -/// This function should only be called with a tuple created by `__quantum__rt__tuple_*` functions. -/// If the reference count after update is less than or equal to zero, the tuple is be cleaned up -/// and the pointer is no longer be valid. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__tuple_update_reference_count( raw_tup: *mut *const Vec, @@ -60,9 +49,6 @@ pub unsafe extern "C" fn __quantum__rt__tuple_update_reference_count( update_counts(*raw_tup.wrapping_sub(1), update, false); } -/// # Safety -/// -/// This function should only be called with a tuple created by `__quantum__rt__tuple_*` functions. #[no_mangle] pub unsafe extern "C" fn __quantum__rt__tuple_update_alias_count( raw_tup: *mut *const Vec, diff --git a/src/Qir/Runtime/test-qir-stdlib.ps1 b/src/Qir/Runtime/test-qir-stdlib.ps1 index fc22dc689ec..482b25d6a00 100644 --- a/src/Qir/Runtime/test-qir-stdlib.ps1 +++ b/src/Qir/Runtime/test-qir-stdlib.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. Push-Location (Join-Path $PSScriptRoot stdlib) From 637bfbc712a80c474877aa6cbebdafdbddcfd90b Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 15 Sep 2022 21:08:43 -0700 Subject: [PATCH 53/55] Fix double free typo in tests --- src/Qir/Runtime/stdlib/src/bigints.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/bigints.rs b/src/Qir/Runtime/stdlib/src/bigints.rs index 987b37a5010..f0f97a28337 100644 --- a/src/Qir/Runtime/stdlib/src/bigints.rs +++ b/src/Qir/Runtime/stdlib/src/bigints.rs @@ -214,7 +214,6 @@ mod tests { __quantum__rt__bigint_update_reference_count(bigint_6, -1); __quantum__rt__bigint_update_reference_count(bigint_5, -1); __quantum__rt__bigint_update_reference_count(bigint_4, -1); - __quantum__rt__bigint_update_reference_count(bigint_4, -1); __quantum__rt__bigint_update_reference_count(bigint_3, -1); __quantum__rt__bigint_update_reference_count(bigint_2, -1); __quantum__rt__bigint_update_reference_count(bigint_1, -1); @@ -243,7 +242,6 @@ mod tests { __quantum__rt__bigint_update_reference_count(bigint_6, -1); __quantum__rt__bigint_update_reference_count(bigint_5, -1); __quantum__rt__bigint_update_reference_count(bigint_4, -1); - __quantum__rt__bigint_update_reference_count(bigint_4, -1); __quantum__rt__bigint_update_reference_count(bigint_3, -1); __quantum__rt__bigint_update_reference_count(bigint_2, -1); __quantum__rt__bigint_update_reference_count(bigint_1, -1); From 986471e34b81fe0c47cb37590da37027e4feb520 Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Thu, 15 Sep 2022 22:43:29 -0700 Subject: [PATCH 54/55] More updates from PR feedback --- src/Qir/CommandLineTool/Program.cs | 8 +-- src/Qir/Runtime/stdlib/src/arrays.rs | 23 ++++----- src/Qir/Runtime/stdlib/src/callables.rs | 27 ++++------ src/Qir/Runtime/stdlib/src/lib.rs | 17 +++---- src/Qir/Runtime/stdlib/src/range_support.rs | 3 +- src/Qir/Runtime/stdlib/src/strings.rs | 23 ++++----- src/Qir/Runtime/stdlib/src/tuples.rs | 18 +++---- .../StandaloneInputReference/qir-driver.cpp | 2 - .../FullStateDriverGenerator/UseBoolArg.cpp | 1 - .../UseBoolArrayArg.cpp | 1 - .../FullStateDriverGenerator/UseDoubleArg.cpp | 1 - .../UseDoubleArrayArg.cpp | 1 - .../UseIntegerArg.cpp | 1 - .../UseIntegerArrayArg.cpp | 1 - .../FullStateDriverGenerator/UseMiscArgs.cpp | 1 - .../FullStateDriverGenerator/UseNoArgs.cpp | 1 - .../FullStateDriverGenerator/UsePauliArg.cpp | 1 - .../UsePauliArrayArg.cpp | 1 - .../FullStateDriverGenerator/UseRangeArg.cpp | 1 - .../UseRangeArrayArg.cpp | 1 - .../FullStateDriverGenerator/UseResultArg.cpp | 1 - .../UseResultArrayArg.cpp | 1 - .../FullStateDriverGenerator/UseStringArg.cpp | 1 - src/Qir/Tools/Driver/QirCppDriver.cs | 2 +- src/Qir/Tools/Driver/QirCppDriver.tt | 1 - .../Executable/QirFullStateExecutable.cs | 7 +-- src/Qir/Tools/QirTools.cs | 4 +- .../Native/build-native-simulator.ps1 | 2 +- src/Simulation/Native/src/CMakeLists.txt | 6 +++ src/Simulation/Native/src/simulator/qir.cpp | 5 -- src/Simulation/Native/src/simulator/qir.hpp | 50 +++++++++++-------- 31 files changed, 87 insertions(+), 126 deletions(-) diff --git a/src/Qir/CommandLineTool/Program.cs b/src/Qir/CommandLineTool/Program.cs index 85a09c8cb59..7d329ad9b19 100644 --- a/src/Qir/CommandLineTool/Program.cs +++ b/src/Qir/CommandLineTool/Program.cs @@ -46,8 +46,7 @@ private static Command CreateBuildCommand() settings.QSharpDll, settings.LibraryDirectories, settings.IncludeDirectories, - settings.ExecutablesDirectory, - settings.Debug)), + settings.ExecutablesDirectory)), TreatUnmatchedTokensAsErrors = true }; @@ -153,11 +152,6 @@ public sealed class BuildOptions /// The path to the output directory where the created executables will be placed. /// public DirectoryInfo ExecutablesDirectory { get; set; } - - /// - /// Enable additional debugging checks at runtime. - /// - public bool Debug { get; set; } } } } diff --git a/src/Qir/Runtime/stdlib/src/arrays.rs b/src/Qir/Runtime/stdlib/src/arrays.rs index 6ef3828c096..bdd1942317f 100644 --- a/src/Qir/Runtime/stdlib/src/arrays.rs +++ b/src/Qir/Runtime/stdlib/src/arrays.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use crate::{__quantum__rt__fail, strings::convert, update_counts}; -use std::{rc::Rc, usize}; +use std::{mem::ManuallyDrop, rc::Rc, usize}; #[derive(Debug, Clone)] pub struct QirArray { @@ -27,14 +27,14 @@ pub unsafe extern "C" fn __quantum__rt__array_copy( arr: *const QirArray, force: bool, ) -> *const QirArray { - let rc = Rc::from_raw(arr); + // Wrap the array in a `ManuallyDrop` to effectively borrow it and ensure the array + // won't be dropped, refcount decremented, and cleaned up. + let rc = ManuallyDrop::new(Rc::from_raw(arr)); if force || Rc::weak_count(&rc) > 0 { let copy = rc.as_ref().clone(); - Rc::into_raw(rc); Rc::into_raw(Rc::new(copy)) } else { Rc::into_raw(Rc::clone(&rc)); - Rc::into_raw(rc); arr } } @@ -44,8 +44,8 @@ pub unsafe extern "C" fn __quantum__rt__array_concatenate( arr1: *const QirArray, arr2: *const QirArray, ) -> *const QirArray { - let array1 = Rc::from_raw(arr1); - let array2 = Rc::from_raw(arr2); + let array1 = &*arr1; + let array2 = &*arr2; if array1.elem_size != array2.elem_size { __quantum__rt__fail(convert(&format!( "Cannot concatenate arrays with differing element sizes: {} vs {}", @@ -65,16 +65,13 @@ pub unsafe extern "C" fn __quantum__rt__array_concatenate( copy.copy_from_slice(array2.data.as_slice()); new_array.data.append(&mut copy); - Rc::into_raw(array1); - Rc::into_raw(array2); Rc::into_raw(Rc::new(new_array)) } #[no_mangle] pub unsafe extern "C" fn __quantum__rt__array_get_size_1d(arr: *const QirArray) -> u64 { - let array = Rc::from_raw(arr); + let array = &*arr; let len = array.data.len() / array.elem_size; - Rc::into_raw(array); len.try_into() .expect("Length of array should always fit in a 64-bit integer.") } @@ -84,13 +81,11 @@ pub unsafe extern "C" fn __quantum__rt__array_get_element_ptr_1d( arr: *const QirArray, index: u64, ) -> *mut i8 { - let array = Rc::from_raw(arr); + let array = &*arr; let index: usize = index .try_into() .expect("Indices into an array should fit into the `usize` "); - let ptr = array.data.as_ptr().add(array.elem_size * index) as *mut i8; - Rc::into_raw(array); - ptr + array.data.as_ptr().add(array.elem_size * index) as *mut i8 } #[no_mangle] diff --git a/src/Qir/Runtime/stdlib/src/callables.rs b/src/Qir/Runtime/stdlib/src/callables.rs index 55d2816592d..b33cf5bdc03 100644 --- a/src/Qir/Runtime/stdlib/src/callables.rs +++ b/src/Qir/Runtime/stdlib/src/callables.rs @@ -8,7 +8,7 @@ use crate::{ tuples::{__quantum__rt__tuple_copy, __quantum__rt__tuple_update_reference_count}, update_counts, }; -use std::{cell::RefCell, rc::Rc, usize}; +use std::{cell::RefCell, mem::ManuallyDrop, rc::Rc, usize}; #[derive(Clone)] pub struct Callable { @@ -41,7 +41,7 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( args_tup: *mut u8, res_tup: *mut u8, ) { - let call = Rc::from_raw(callable); + let call = &*callable; let index = (if *call.is_adj.borrow() { 1 } else { 0 }) + (if *call.ctls_count.borrow() > 0 { 2 } else { 0 }); @@ -99,7 +99,6 @@ pub unsafe extern "C" fn __quantum__rt__callable_invoke( if !args_copy.is_null() { __quantum__rt__tuple_update_reference_count(args_copy, -1); } - let _ = Rc::into_raw(call); } #[no_mangle] @@ -107,30 +106,26 @@ pub unsafe extern "C" fn __quantum__rt__callable_copy( callable: *const Callable, force: bool, ) -> *const Callable { - let rc = Rc::from_raw(callable); + let rc = ManuallyDrop::new(Rc::from_raw(callable)); if force || Rc::weak_count(&rc) > 0 { let copy = rc.as_ref().clone(); - let _ = Rc::into_raw(rc); Rc::into_raw(Rc::new(copy)) } else { - let _ = Rc::into_raw(Rc::clone(&rc)); - let _ = Rc::into_raw(rc); + Rc::into_raw(Rc::clone(&rc)); callable } } #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_make_adjoint(callable: *const Callable) { - let call = Rc::from_raw(callable); - let _ = call.is_adj.replace_with(|&mut old| !old); - let _ = Rc::into_raw(call); + let call = &*callable; + call.is_adj.replace_with(|&mut old| !old); } #[no_mangle] pub unsafe extern "C" fn __quantum__rt__callable_make_controlled(callable: *const Callable) { - let call = Rc::from_raw(callable); - let _ = call.ctls_count.replace_with(|&mut old| old + 1); - let _ = Rc::into_raw(call); + let call = &*callable; + call.ctls_count.replace_with(|&mut old| old + 1); } #[no_mangle] @@ -154,11 +149,10 @@ pub unsafe extern "C" fn __quantum__rt__capture_update_reference_count( callable: *const Callable, update: i32, ) { - let call = Rc::from_raw(callable); + let call = &*callable; if !call.mem_table.is_null() && !(*(call.mem_table)).is_null() { (*call.mem_table.cast::())(call.cap_tuple, update); } - let _ = Rc::into_raw(call); } #[no_mangle] @@ -166,7 +160,7 @@ pub unsafe extern "C" fn __quantum__rt__capture_update_alias_count( callable: *const Callable, update: i32, ) { - let call = Rc::from_raw(callable); + let call = &*callable; if !call.mem_table.is_null() && !(*(call.mem_table.wrapping_add(1))).is_null() { let _val = **(call.mem_table.cast::<*mut usize>().wrapping_add(1)); (*call @@ -174,5 +168,4 @@ pub unsafe extern "C" fn __quantum__rt__capture_update_alias_count( .wrapping_add(1) .cast::())(call.cap_tuple, update); } - let _ = Rc::into_raw(call); } diff --git a/src/Qir/Runtime/stdlib/src/lib.rs b/src/Qir/Runtime/stdlib/src/lib.rs index 1ec69b6a1f0..6cb2757a053 100644 --- a/src/Qir/Runtime/stdlib/src/lib.rs +++ b/src/Qir/Runtime/stdlib/src/lib.rs @@ -21,6 +21,7 @@ pub mod tuples; use std::{ ffi::CString, + mem::{self, ManuallyDrop}, rc::{Rc, Weak}, }; @@ -28,34 +29,30 @@ use std::{ unsafe fn update_counts(raw_rc: *const T, update: i32, is_alias: bool) { let mut remaining = update; while remaining != 0 { - let rc = Rc::from_raw(raw_rc); + let rc = ManuallyDrop::new(Rc::from_raw(raw_rc)); if remaining > 0 { - // Create and leak new instances to increment the count on the contained item. if is_alias { - let _ = Weak::into_raw(Rc::downgrade(&rc)); + // Create and leak new downgraded instances to increment the weak count on the contained item. + mem::forget(Rc::downgrade(&rc)); } else { - let _ = Rc::into_raw(Rc::clone(&rc)); + Rc::increment_strong_count(raw_rc); } remaining -= 1; } else { - // Create and drop instances to decrement the count on contained item. if is_alias { + // Create and drop downgraded instances to decrement the weak count on contained item. let w = Weak::into_raw(Rc::downgrade(&rc)); // Need to drop two for a net decrement, since above line increments. drop(Weak::from_raw(w)); drop(Weak::from_raw(w)); } else { - drop(Rc::from_raw(raw_rc)); + Rc::decrement_strong_count(raw_rc); } remaining += 1; } - - // To make sure the local Rc does not decrement the underlying count when it goes out of scope, - // it must be converted into a raw pointer first. - let _ = Rc::into_raw(rc); } } diff --git a/src/Qir/Runtime/stdlib/src/range_support.rs b/src/Qir/Runtime/stdlib/src/range_support.rs index eaddc2e2e2f..66b82252b47 100644 --- a/src/Qir/Runtime/stdlib/src/range_support.rs +++ b/src/Qir/Runtime/stdlib/src/range_support.rs @@ -29,7 +29,7 @@ pub unsafe extern "C" fn quantum__rt__array_slice_1d( arr: *const QirArray, range: Range, ) -> *const QirArray { - let array = Rc::from_raw(arr); + let array = &*arr; let item_size: i64 = array .elem_size .try_into() @@ -57,7 +57,6 @@ pub unsafe extern "C" fn quantum__rt__array_slice_1d( slice.data.append(&mut copy); } - let _ = Rc::into_raw(array); Rc::into_raw(Rc::new(slice)) } diff --git a/src/Qir/Runtime/stdlib/src/strings.rs b/src/Qir/Runtime/stdlib/src/strings.rs index 74bef8badf4..0b02ef92a95 100644 --- a/src/Qir/Runtime/stdlib/src/strings.rs +++ b/src/Qir/Runtime/stdlib/src/strings.rs @@ -115,6 +115,8 @@ pub unsafe extern "C" fn __quantum__rt__bigint_to_string(input: *const BigInt) - #[cfg(test)] mod tests { + use std::mem::ManuallyDrop; + use super::*; use crate::bigints::{ __quantum__rt__bigint_create_i64, __quantum__rt__bigint_update_reference_count, @@ -182,13 +184,12 @@ mod tests { let str = __quantum__rt__string_create( CString::new("Data").unwrap().as_bytes_with_nul().as_ptr() as *mut i8, ); - let rc = Rc::from_raw(str); + let rc = ManuallyDrop::new(Rc::from_raw(str)); assert_eq!(Rc::strong_count(&rc), 1); __quantum__rt__string_update_reference_count(str, 2); assert_eq!(Rc::strong_count(&rc), 3); __quantum__rt__string_update_reference_count(str, -2); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); __quantum__rt__string_update_reference_count(str, -1); } } @@ -207,9 +208,8 @@ mod tests { ); let str3 = __quantum__rt__string_concatenate(str1, str2); // Concatenated string should have combined value. - let rc = Rc::from_raw(str3); + let rc = ManuallyDrop::new(Rc::from_raw(str3)); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); assert_eq!( CStr::from_ptr(__quantum__rt__string_get_data(str3)) .to_str() @@ -218,9 +218,8 @@ mod tests { ); __quantum__rt__string_update_reference_count(str3, -1); // After decrement and drop, original strings should still be valid. - let rc = Rc::from_raw(str2); + let rc = ManuallyDrop::new(Rc::from_raw(str2)); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); assert_eq!( CStr::from_ptr(__quantum__rt__string_get_data(str2)) .to_str() @@ -228,9 +227,8 @@ mod tests { ", World!" ); __quantum__rt__string_update_reference_count(str2, -1); - let rc = Rc::from_raw(str1); + let rc = ManuallyDrop::new(Rc::from_raw(str1)); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); assert_eq!( CStr::from_ptr(__quantum__rt__string_get_data(str1)) .to_str() @@ -259,9 +257,8 @@ mod tests { assert!(__quantum__rt__string_equal(str1, str2)); assert!(!__quantum__rt__string_equal(str1, str3)); // Confirm data is still valid and not consumed by the check. - let rc = Rc::from_raw(str3); + let rc = ManuallyDrop::new(Rc::from_raw(str3)); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); assert_eq!( CStr::from_ptr(__quantum__rt__string_get_data(str3)) .to_str() @@ -269,9 +266,8 @@ mod tests { "Not Data" ); __quantum__rt__string_update_reference_count(str3, -1); - let rc = Rc::from_raw(str2); + let rc = ManuallyDrop::new(Rc::from_raw(str2)); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); assert_eq!( CStr::from_ptr(__quantum__rt__string_get_data(str2)) .to_str() @@ -279,9 +275,8 @@ mod tests { "Data" ); __quantum__rt__string_update_reference_count(str2, -1); - let rc = Rc::from_raw(str1); + let rc = ManuallyDrop::new(Rc::from_raw(str1)); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); assert_eq!( CStr::from_ptr(__quantum__rt__string_get_data(str1)) .to_str() diff --git a/src/Qir/Runtime/stdlib/src/tuples.rs b/src/Qir/Runtime/stdlib/src/tuples.rs index 2aa46ec3516..f3774f63d95 100644 --- a/src/Qir/Runtime/stdlib/src/tuples.rs +++ b/src/Qir/Runtime/stdlib/src/tuples.rs @@ -2,7 +2,11 @@ // Licensed under the MIT License. use crate::update_counts; -use std::{mem::size_of, rc::Rc, usize}; +use std::{ + mem::{size_of, ManuallyDrop}, + rc::Rc, + usize, +}; #[allow(clippy::cast_ptr_alignment)] #[no_mangle] @@ -27,16 +31,14 @@ pub unsafe extern "C" fn __quantum__rt__tuple_copy( raw_tup: *mut *const Vec, force: bool, ) -> *mut *const Vec { - let rc = Rc::from_raw(*(raw_tup).wrapping_sub(1)); + let rc = ManuallyDrop::new(Rc::from_raw(*(raw_tup).wrapping_sub(1))); if force || Rc::weak_count(&rc) > 0 { let mut copy = rc.as_ref().clone(); - let _ = Rc::into_raw(rc); let header = copy.as_mut_ptr().cast::<*const Vec>(); *header = Rc::into_raw(Rc::new(copy)); header.wrapping_add(1) } else { - let _ = Rc::into_raw(Rc::clone(&rc)); - let _ = Rc::into_raw(rc); + Rc::into_raw(Rc::clone(&rc)); raw_tup } } @@ -74,13 +76,12 @@ mod tests { fn test_tuple_update_reference_count() { let tup = __quantum__rt__tuple_create(size_of::() as u64); unsafe { - let rc = Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1)); + let rc = ManuallyDrop::new(Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1))); assert_eq!(Rc::strong_count(&rc), 1); __quantum__rt__tuple_update_reference_count(tup, 2); assert_eq!(Rc::strong_count(&rc), 3); __quantum__rt__tuple_update_reference_count(tup, -2); assert_eq!(Rc::strong_count(&rc), 1); - let _ = Rc::into_raw(rc); __quantum__rt__tuple_update_reference_count(tup, -1); } } @@ -89,14 +90,13 @@ mod tests { fn test_tuple_update_alias_count() { let tup = __quantum__rt__tuple_create(size_of::() as u64); unsafe { - let rc = Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1)); + let rc = ManuallyDrop::new(Rc::from_raw(*tup.cast::<*const Vec>().wrapping_sub(1))); assert_eq!(Rc::strong_count(&rc), 1); assert_eq!(Rc::weak_count(&rc), 0); __quantum__rt__tuple_update_alias_count(tup, 2); assert_eq!(Rc::weak_count(&rc), 2); __quantum__rt__tuple_update_alias_count(tup, -2); assert_eq!(Rc::weak_count(&rc), 0); - let _ = Rc::into_raw(rc); __quantum__rt__tuple_update_reference_count(tup, -1); } } diff --git a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp index 36253d57e4c..eb71153902d 100644 --- a/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp +++ b/src/Qir/Samples/StandaloneInputReference/qir-driver.cpp @@ -116,8 +116,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point Inputs Reference"); - // Initialize simulator. - // Add the options that correspond to the parameters that the QIR entry-point needs. // Option for a Q# Int type. int64_t intValue = 0; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp index 1855912aa69..0be0cbf61eb 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArg.cpp @@ -33,7 +33,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. char BoolArgCli; BoolArgCli = InteropFalseAsChar; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp index 5bfbc30196b..e8525958eed 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseBoolArrayArg.cpp @@ -58,7 +58,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. vector BoolArrayArgCli; app.add_option("--BoolArrayArg", BoolArrayArgCli, "Option to provide a value for the BoolArrayArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp index 6eefd32abbe..d36739d3a7b 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArg.cpp @@ -23,7 +23,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. double_t DoubleArgCli; DoubleArgCli = 0.0; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp index d6593923e71..1f1914ce79d 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseDoubleArrayArg.cpp @@ -48,7 +48,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. vector DoubleArrayArgCli; app.add_option("--DoubleArrayArg", DoubleArrayArgCli, "Option to provide a value for the DoubleArrayArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp index d6e6ebfd3f1..5e70b972c67 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArg.cpp @@ -23,7 +23,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. int64_t IntegerArgCli; IntegerArgCli = 0; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp index 73ef2abbbe6..4bcafda3a32 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseIntegerArrayArg.cpp @@ -48,7 +48,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. vector IntegerArrayArgCli; app.add_option("--IntegerArrayArg", IntegerArrayArgCli, "Option to provide a value for the IntegerArrayArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp index c43b4506176..72b9185064a 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseMiscArgs.cpp @@ -98,7 +98,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. char BoolArgCli; BoolArgCli = InteropFalseAsChar; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp index 56061656927..bffb97c35a7 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseNoArgs.cpp @@ -22,7 +22,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // After all the options have been added, parse arguments from the command line. CLI11_PARSE(app, argc, argv); diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp index cb923423b76..73e4a90fa7b 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArg.cpp @@ -31,7 +31,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. char PauliArgCli; PauliArgCli = 0; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp index c754efbb865..5ca5cc7de70 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UsePauliArrayArg.cpp @@ -56,7 +56,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. vector PauliArrayArgCli; app.add_option("--PauliArrayArg", PauliArrayArgCli, "Option to provide a value for the PauliArrayArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp index e05012edb4a..82790f8e1c4 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArg.cpp @@ -54,7 +54,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. RangeTuple RangeArgCli; app.add_option("--RangeArg", RangeArgCli, "Option to provide a value for the RangeArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp index 2ea36be72c2..2695c05d772 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseRangeArrayArg.cpp @@ -89,7 +89,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. vector RangeArrayArgCli; app.add_option("--RangeArrayArg", RangeArrayArgCli, "Option to provide a value for the RangeArrayArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp index c3757bff52f..dc51c0ea2d1 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArg.cpp @@ -33,7 +33,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. char ResultArgCli; ResultArgCli = InteropResultZeroAsChar; diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp index 1bc6992c838..cbbfd2f08cc 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseResultArrayArg.cpp @@ -58,7 +58,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. vector ResultArrayArgCli; app.add_option("--ResultArrayArg", ResultArrayArgCli, "Option to provide a value for the ResultArrayArg parameter") diff --git a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp index a1908a9c045..e883e1bca1a 100644 --- a/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp +++ b/src/Qir/Tests/Tools/TestCases/FullStateDriverGenerator/UseStringArg.cpp @@ -29,7 +29,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. // Add a command line option for each entry-point parameter. string StringArgCli; app.add_option("--StringArg", StringArgCli, "Option to provide a value for the StringArg parameter") diff --git a/src/Qir/Tools/Driver/QirCppDriver.cs b/src/Qir/Tools/Driver/QirCppDriver.cs index 726dd833ca9..ef666584511 100644 --- a/src/Qir/Tools/Driver/QirCppDriver.cs +++ b/src/Qir/Tools/Driver/QirCppDriver.cs @@ -152,7 +152,7 @@ unique_ptr CreateInteropRange(RangeTuple rangeTuple) this.Write("\r\n"); } this.Write("); // QIR interop function.\r\n\r\nint main(int argc, char* argv[])\r\n{\r\n CLI::App " + - "app(\"QIR Standalone Entry Point\");\r\n\r\n // Initialize runtime.\r\n"); + "app(\"QIR Standalone Entry Point\");\r\n\r\n"); var initializerReader = new StringReader(RuntimeInitializer.Generate()); string line; while((line = initializerReader.ReadLine()) != null) { diff --git a/src/Qir/Tools/Driver/QirCppDriver.tt b/src/Qir/Tools/Driver/QirCppDriver.tt index 01aa8b9ebe5..a2d68db0877 100644 --- a/src/Qir/Tools/Driver/QirCppDriver.tt +++ b/src/Qir/Tools/Driver/QirCppDriver.tt @@ -149,7 +149,6 @@ int main(int argc, char* argv[]) { CLI::App app("QIR Standalone Entry Point"); - // Initialize runtime. <# var initializerReader = new StringReader(RuntimeInitializer.Generate()); string line; while((line = initializerReader.ReadLine()) != null) { #> diff --git a/src/Qir/Tools/Executable/QirFullStateExecutable.cs b/src/Qir/Tools/Executable/QirFullStateExecutable.cs index 3ed680eacb9..8d161a5af38 100644 --- a/src/Qir/Tools/Executable/QirFullStateExecutable.cs +++ b/src/Qir/Tools/Executable/QirFullStateExecutable.cs @@ -20,9 +20,10 @@ public class QirFullStateExecutable : QirExecutable { public override string DriverFileExtension => "cpp"; - public override IList LinkLibraries => new List { - "Microsoft.Quantum.Simulator.Runtime" - }; + public override IList LinkLibraries => new List + { + "Microsoft.Quantum.Simulator.Runtime" + }; public override IList HeaderDirectories { get; } = new List(); diff --git a/src/Qir/Tools/QirTools.cs b/src/Qir/Tools/QirTools.cs index f26fc6bfaa1..d82960fc3c7 100644 --- a/src/Qir/Tools/QirTools.cs +++ b/src/Qir/Tools/QirTools.cs @@ -23,13 +23,11 @@ public static class QirTools /// Directory where the libraries to link to are located. /// Directory where the headers needed for compilation are located. /// Directory where the created executables are placed. - /// Ignored. public static async Task BuildFromQSharpDll( FileInfo qsharpDll, IList libraryDirectories, IList includeDirectories, - DirectoryInfo executablesDirectory, - bool debug) + DirectoryInfo executablesDirectory) { using var qirContentStream = new MemoryStream(); if (!AssemblyLoader.LoadQirBitcode(qsharpDll, qirContentStream)) diff --git a/src/Simulation/Native/build-native-simulator.ps1 b/src/Simulation/Native/build-native-simulator.ps1 index 257140024e7..a5bcbe2a4b9 100644 --- a/src/Simulation/Native/build-native-simulator.ps1 +++ b/src/Simulation/Native/build-native-simulator.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. & (Join-Path $PSScriptRoot .. .. .. build set-env.ps1) diff --git a/src/Simulation/Native/src/CMakeLists.txt b/src/Simulation/Native/src/CMakeLists.txt index 8cc793fdbad..5ede9d3af78 100644 --- a/src/Simulation/Native/src/CMakeLists.txt +++ b/src/Simulation/Native/src/CMakeLists.txt @@ -46,9 +46,12 @@ if (WIN32) Userenv ) # On Windows, use a .def file to force export stdlib functions from the static library. + # See https://docs.microsoft.com/en-us/cpp/build/reference/module-definition-dot-def-files target_sources(Microsoft.Quantum.Simulator.Runtime PUBLIC "${PROJECT_BINARY_DIR}/../../../Qir/drops/include/qir_stdlib.def") elseif (APPLE) # On MacOS, use -force_load and explicit path to pull in entire contents of stdlib static archive. + # See https://stackoverflow.com/questions/16926608/including-static-libraries-with-all-load-flag + # and https://github.com/bioinformatics-centre/kaiju/issues/30 target_link_libraries(Microsoft.Quantum.Simulator.Runtime "-Wl,-force_load,${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native/libqir_stdlib.a" pthread @@ -56,6 +59,9 @@ elseif (APPLE) ) else() # On Unix systems, use --whole-archive to to pull in entire contents of stdlib static archive. + # Note that this must be turned off with --no-whole-archive to prevent the setting from interfering with + # normal loading of other dependencies. + # See https://www.man7.org/linux/man-pages/man1/ld.1.html target_link_libraries(Microsoft.Quantum.Simulator.Runtime "-L${PROJECT_BINARY_DIR}/../../../Qir/drops/bin/$ENV{BUILD_PLATFORM}/native" "-Wl,--whole-archive" diff --git a/src/Simulation/Native/src/simulator/qir.cpp b/src/Simulation/Native/src/simulator/qir.cpp index 23c13891f07..41ebe2531d1 100644 --- a/src/Simulation/Native/src/simulator/qir.cpp +++ b/src/Simulation/Native/src/simulator/qir.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -/*============================================================================= - QIR assumes a single global execution context. - To support the dispatch over the qir-bridge, the clients must register their - Microsoft::Quantum::IRuntimeDriver* first. -=============================================================================*/ #include #include #include diff --git a/src/Simulation/Native/src/simulator/qir.hpp b/src/Simulation/Native/src/simulator/qir.hpp index 7bbf45d11cc..c40eb8d3f99 100644 --- a/src/Simulation/Native/src/simulator/qir.hpp +++ b/src/Simulation/Native/src/simulator/qir.hpp @@ -31,32 +31,38 @@ typedef intptr_t QubitIdType; class RESULT; -struct QirRTuple +extern "C" { - PauliId pauli; - double angle; - QUBIT* target; -}; + // This struct matches the argument tuple for a controlled R specialization in Q#. It must + // be kept in sync with src\Simulation\TargetDefinitions\Intrinsic\R.qs and Q# compiler behavior. + typedef struct + { + PauliId pauli; + double angle; + QUBIT* target; + } QirRTuple; -struct QirExpTuple -{ - QirArray* paulis; - double angle; - QirArray* targets; -}; + // This struct matches the argument tuple for a controlled Exp specialization in Q#. It must + // be kept in sync with src\Simulation\TargetDefinitions\Intrinsic\Exp.qs and Q# compiler behavior. + typedef struct + { + QirArray* paulis; + double angle; + QirArray* targets; + } QirExpTuple; -struct QirAssertMeasurementProbabilityTuple -{ - QirArray* bases; - QirArray* qubits; - RESULT* result; - double prob; - QirString* msg; - double tol; -}; + // This struct matches the argument tuple for a controlled AsssertMeasurementProbability specialization in Q#. It must + // be kept in sync with src\Simulation\QSharpFoundation\Diagnostics\Assert.qs and Q# compiler behavior. + typedef struct + { + QirArray* bases; + QirArray* qubits; + RESULT* result; + double prob; + QirString* msg; + double tol; + } QirAssertMeasurementProbabilityTuple; -extern "C" -{ // Quantum Runtime QIR_EXPORT_API QUBIT* __quantum__rt__qubit_allocate(); // NOLINT QIR_EXPORT_API QirArray* __quantum__rt__qubit_allocate_array(int64_t count); // NOLINT From 8d0635e1f4f58f959a7b5b8df379598b96a13cee Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Fri, 16 Sep 2022 09:59:18 -0700 Subject: [PATCH 55/55] Remove leftover debug line --- src/Qir/Runtime/stdlib/src/callables.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Qir/Runtime/stdlib/src/callables.rs b/src/Qir/Runtime/stdlib/src/callables.rs index b33cf5bdc03..30b8f38cf5a 100644 --- a/src/Qir/Runtime/stdlib/src/callables.rs +++ b/src/Qir/Runtime/stdlib/src/callables.rs @@ -8,7 +8,7 @@ use crate::{ tuples::{__quantum__rt__tuple_copy, __quantum__rt__tuple_update_reference_count}, update_counts, }; -use std::{cell::RefCell, mem::ManuallyDrop, rc::Rc, usize}; +use std::{cell::RefCell, mem::ManuallyDrop, rc::Rc}; #[derive(Clone)] pub struct Callable { @@ -162,7 +162,6 @@ pub unsafe extern "C" fn __quantum__rt__capture_update_alias_count( ) { let call = &*callable; if !call.mem_table.is_null() && !(*(call.mem_table.wrapping_add(1))).is_null() { - let _val = **(call.mem_table.cast::<*mut usize>().wrapping_add(1)); (*call .mem_table .wrapping_add(1)