From d28757cd1afd93f509a19a569f44c3860789f117 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 3 Sep 2021 13:18:53 -0700 Subject: [PATCH 01/11] Removed the src/Qir/Runtime/lib/{QIR,QSharpCore}/.clang-tidy to unify clang-tidy discipline --- src/Qir/Runtime/lib/QIR/.clang-tidy | 1 - src/Qir/Runtime/lib/QIR/QubitManager.cpp | 4 +- src/Qir/Runtime/lib/QIR/arrays.cpp | 55 +++++++++++----------- src/Qir/Runtime/lib/QSharpCore/.clang-tidy | 1 - 4 files changed, 29 insertions(+), 32 deletions(-) delete mode 100644 src/Qir/Runtime/lib/QIR/.clang-tidy delete mode 100644 src/Qir/Runtime/lib/QSharpCore/.clang-tidy diff --git a/src/Qir/Runtime/lib/QIR/.clang-tidy b/src/Qir/Runtime/lib/QIR/.clang-tidy deleted file mode 100644 index 8268b75c21d..00000000000 --- a/src/Qir/Runtime/lib/QIR/.clang-tidy +++ /dev/null @@ -1 +0,0 @@ -Checks: '-*,bugprone-*' \ No newline at end of file diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp index 80c9ac18c43..b858767b3f1 100644 --- a/src/Qir/Runtime/lib/QIR/QubitManager.cpp +++ b/src/Qir/Runtime/lib/QIR/QubitManager.cpp @@ -181,8 +181,8 @@ namespace Quantum sharedQubitStatusArray = new QubitIdType[(size_t)qubitCapacity]; // These objects are passed by value (copies are created) - QubitListInSharedArray FreeQubitsFresh(0, qubitCapacity - 1, sharedQubitStatusArray); - RestrictedReuseArea outermostArea(FreeQubitsFresh); + QubitListInSharedArray freeQubitsFresh(0, qubitCapacity - 1, sharedQubitStatusArray); + RestrictedReuseArea outermostArea(freeQubitsFresh); freeQubitsInAreas.PushToBack(outermostArea); freeQubitCount = qubitCapacity; diff --git a/src/Qir/Runtime/lib/QIR/arrays.cpp b/src/Qir/Runtime/lib/QIR/arrays.cpp index aae8ab454d1..6f3fab3cab7 100644 --- a/src/Qir/Runtime/lib/QIR/arrays.cpp +++ b/src/Qir/Runtime/lib/QIR/arrays.cpp @@ -54,8 +54,8 @@ int QirArray::Release() return rc; } -QirArray::QirArray(TItemCount qubits_count) - : count(qubits_count) +QirArray::QirArray(TItemCount qubitsCount) + : count(qubitsCount) , itemSizeInBytes((TItemSize)sizeof(void*)) , ownsQubits(true) , refCount(1) @@ -81,25 +81,25 @@ QirArray::QirArray(TItemCount qubits_count) } } -QirArray::QirArray(TItemCount count_items, TItemSize item_size_bytes, TDimCount dimCount, TDimContainer&& dimSizes) - : count(count_items) +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( - ((item_size_bytes == 1) || (item_size_bytes == 2) || (item_size_bytes == 4) || - ((item_size_bytes % sizeof(size_t)) == 0) // For built-in types or multiples of architecture alignment + ((itemSizeBytes == 1) || (itemSizeBytes == 2) || (itemSizeBytes == 4) || + ((itemSizeBytes % sizeof(size_t)) == 0) // For built-in types or multiples of architecture alignment ) - ? item_size_bytes // 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. - : item_size_bytes + sizeof(size_t) - (item_size_bytes % sizeof(size_t))) + ? 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(item_size_bytes != 0); + assert(itemSizeBytes != 0); assert(dimCount > 0); if (GlobalContext() != nullptr) @@ -112,18 +112,18 @@ QirArray::QirArray(TItemCount count_items, TItemSize item_size_bytes, TDimCount assert(this->dimensionSizes.empty() || this->dimensionSizes[0] == this->count); if (this->dimensionSizes.empty()) { - this->dimensionSizes.push_back(count_items); + 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 buffer_size = this->count * itemSizeInBytes; - if (buffer_size > 0) + const TBufSize bufferSize = this->count * itemSizeInBytes; + if (bufferSize > 0) { - this->buffer = new char[buffer_size]; - assert(buffer_size <= std::numeric_limits::max()); - memset(this->buffer, 0, (size_t)buffer_size); + this->buffer = new char[bufferSize]; + assert(bufferSize <= std::numeric_limits::max()); + memset(this->buffer, 0, (size_t)bufferSize); } else { @@ -178,26 +178,26 @@ void QirArray::Append(const QirArray* other) assert((TBufSize)(other->count) * other->itemSizeInBytes < std::numeric_limits::max()); // Using `<` rather than `<=` to calm down the compiler on 32-bit arch. - const TBufSize other_size = other->count * other->itemSizeInBytes; + const TBufSize otherSize = other->count * other->itemSizeInBytes; - if (other_size == 0) + 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 this_size = this->count * this->itemSizeInBytes; + const TBufSize thisSize = this->count * this->itemSizeInBytes; - char* new_buffer = new char[this_size + other_size]; - if (this_size) + char* newBuffer = new char[thisSize + otherSize]; + if (thisSize) { - memcpy(new_buffer, this->buffer, this_size); + memcpy(newBuffer, this->buffer, thisSize); } - memcpy(&new_buffer[this_size], other->buffer, other_size); + memcpy(&newBuffer[thisSize], other->buffer, otherSize); delete[] this->buffer; - this->buffer = new_buffer; + this->buffer = newBuffer; this->count += other->count; this->dimensionSizes[0] = this->count; } @@ -279,11 +279,10 @@ extern "C" __quantum__rt__qubit_release_array(qa); } - // TODO: Use `QirArray::TItemSize itemSizeInBytes, QirArray::TItemCount count_items` (breaking change): - QirArray* __quantum__rt__array_create_1d(int32_t itemSizeInBytes, int64_t count_items) + QirArray* __quantum__rt__array_create_1d(int32_t itemSizeInBytes, int64_t countItems) { assert(itemSizeInBytes > 0); - return new QirArray((QirArray::TItemCount)count_items, (QirArray::TItemSize)itemSizeInBytes); + 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 diff --git a/src/Qir/Runtime/lib/QSharpCore/.clang-tidy b/src/Qir/Runtime/lib/QSharpCore/.clang-tidy deleted file mode 100644 index 8268b75c21d..00000000000 --- a/src/Qir/Runtime/lib/QSharpCore/.clang-tidy +++ /dev/null @@ -1 +0,0 @@ -Checks: '-*,bugprone-*' \ No newline at end of file From e2d1836ba7d20acb2a07e62043b9ffc1e276d49d Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 3 Sep 2021 13:49:55 -0700 Subject: [PATCH 02/11] Corrected Samples/StandaloneInputReference/.clang-tidy --- src/Qir/Samples/StandaloneInputReference/.clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/Samples/StandaloneInputReference/.clang-tidy b/src/Qir/Samples/StandaloneInputReference/.clang-tidy index af1d9665a68..611ab95504e 100644 --- a/src/Qir/Samples/StandaloneInputReference/.clang-tidy +++ b/src/Qir/Samples/StandaloneInputReference/.clang-tidy @@ -1,5 +1,5 @@ InheritParentConfig: true Checks: - '-bugprone-exception-escape*' + '-bugprone-exception-escape' HeaderFilterRegex: '' From de526363196587fdfb3813736f648283d1eb384b Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 3 Sep 2021 14:02:29 -0700 Subject: [PATCH 03/11] Cleaned-up src/Qir/.clang-tidy a bit. --- src/Qir/.clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/.clang-tidy b/src/Qir/.clang-tidy index 8fb71d1375a..e2a17c5d08e 100644 --- a/src/Qir/.clang-tidy +++ b/src/Qir/.clang-tidy @@ -1,5 +1,5 @@ Checks: - '-*,bugprone-*,-readability-*,readability-identifier-*,readability-braces-around-statements' + '-*,bugprone-*,readability-identifier-*,readability-braces-around-statements' WarningsAsErrors: '*' HeaderFilterRegex: '.*' From 237ab82b6fac93b5c0494623780625605eb29d9e Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 3 Sep 2021 15:58:36 -0700 Subject: [PATCH 04/11] Added clang-analyzer-* check tothe clang-tidy. --- src/Qir/.clang-tidy | 2 +- src/Qir/Runtime/lib/QIR/callables.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Qir/.clang-tidy b/src/Qir/.clang-tidy index e2a17c5d08e..d0e66dbc240 100644 --- a/src/Qir/.clang-tidy +++ b/src/Qir/.clang-tidy @@ -1,5 +1,5 @@ Checks: - '-*,bugprone-*,readability-identifier-*,readability-braces-around-statements' + '-*,bugprone-*,readability-identifier-*,readability-braces-around-statements,clang-analyzer-*' WarningsAsErrors: '*' HeaderFilterRegex: '.*' diff --git a/src/Qir/Runtime/lib/QIR/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp index c8258512dab..93cd1dee466 100644 --- a/src/Qir/Runtime/lib/QIR/callables.cpp +++ b/src/Qir/Runtime/lib/QIR/callables.cpp @@ -102,7 +102,11 @@ extern "C" { for (int i = increment; i < 0; i++) { - (void)callable->Release(); + if (0 == callable->Release()) + { + assert(-1 == i && "Attempting to decrement reference count below zero!"); + break; + } } } } From d001d405c5345e648503b9edeb122551ffc42054 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Fri, 3 Sep 2021 17:46:37 -0700 Subject: [PATCH 05/11] Added cert* check to clang-tidy --- src/Qir/.clang-tidy | 2 +- src/Qir/Samples/StandaloneInputReference/.clang-tidy | 2 +- src/Qir/Tests/.clang-tidy | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 src/Qir/Tests/.clang-tidy diff --git a/src/Qir/.clang-tidy b/src/Qir/.clang-tidy index d0e66dbc240..9ace6ae116f 100644 --- a/src/Qir/.clang-tidy +++ b/src/Qir/.clang-tidy @@ -1,5 +1,5 @@ Checks: - '-*,bugprone-*,readability-identifier-*,readability-braces-around-statements,clang-analyzer-*' + '-*,bugprone-*,readability-identifier-*,readability-braces-around-statements,clang-analyzer-*,cert*' WarningsAsErrors: '*' HeaderFilterRegex: '.*' diff --git a/src/Qir/Samples/StandaloneInputReference/.clang-tidy b/src/Qir/Samples/StandaloneInputReference/.clang-tidy index 611ab95504e..eece2bb3472 100644 --- a/src/Qir/Samples/StandaloneInputReference/.clang-tidy +++ b/src/Qir/Samples/StandaloneInputReference/.clang-tidy @@ -1,5 +1,5 @@ InheritParentConfig: true Checks: - '-bugprone-exception-escape' + '-bugprone-exception-escape,-cert-err58-cpp' HeaderFilterRegex: '' diff --git a/src/Qir/Tests/.clang-tidy b/src/Qir/Tests/.clang-tidy new file mode 100644 index 00000000000..8b9489eeabb --- /dev/null +++ b/src/Qir/Tests/.clang-tidy @@ -0,0 +1,3 @@ +InheritParentConfig: true +Checks: + '-cert-err58-cpp' From 0547491f8855827a93aba21344fd70ec239317c9 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Sat, 4 Sep 2021 12:30:59 -0700 Subject: [PATCH 06/11] Stopped disabling default checks 'clang-diagnostic-*,clang-analyzer-*'. --- src/Qir/.clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Qir/.clang-tidy b/src/Qir/.clang-tidy index 9ace6ae116f..1453c2f444f 100644 --- a/src/Qir/.clang-tidy +++ b/src/Qir/.clang-tidy @@ -1,5 +1,5 @@ Checks: - '-*,bugprone-*,readability-identifier-*,readability-braces-around-statements,clang-analyzer-*,cert*' + 'bugprone-*,readability-identifier-*,readability-braces-around-statements,cert*' WarningsAsErrors: '*' HeaderFilterRegex: '.*' From 2aad17bf362d5144ad69c9d06a066b91c3fa230b Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Tue, 7 Sep 2021 16:13:33 -0700 Subject: [PATCH 07/11] Fixed statanaliz-reported issue --- src/Qir/Runtime/lib/QIR/arrays.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Qir/Runtime/lib/QIR/arrays.cpp b/src/Qir/Runtime/lib/QIR/arrays.cpp index 6f3fab3cab7..15859dc6629 100644 --- a/src/Qir/Runtime/lib/QIR/arrays.cpp +++ b/src/Qir/Runtime/lib/QIR/arrays.cpp @@ -559,6 +559,10 @@ extern "C" 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]; @@ -635,6 +639,10 @@ extern "C" 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]; @@ -642,6 +650,7 @@ extern "C" 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 = singleIndexRunCount * itemSizeInBytes; QirArray::TItemCount dst = 0; From de18941940edb6f6fedcf41d038f7b7afa7ba789 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Tue, 7 Sep 2021 16:30:11 -0700 Subject: [PATCH 08/11] Fixed the clang-format version for Linux. --- src/Qir/Runtime/prerequisites.ps1 | 4 ++-- src/Qir/check-sources-formatted.ps1 | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index 6d90aacf3a4..2df5a4b41c6 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -29,10 +29,10 @@ if ($Env:ENABLE_QIRRUNTIME -ne "false") { } else { if (Get-Command sudo -ErrorAction SilentlyContinue) { sudo apt update - sudo apt-get install -y ninja-build clang-11 clang-tidy-11 + sudo apt-get install -y ninja-build clang-11 clang-tidy-11 clang-format-11 } else { apt update - apt-get install -y ninja-build clang-11 clang-tidy-11 + apt-get install -y ninja-build clang-11 clang-tidy-11 clang-format-11 } } } diff --git a/src/Qir/check-sources-formatted.ps1 b/src/Qir/check-sources-formatted.ps1 index 9eafcad30ef..2d0ea63d040 100644 --- a/src/Qir/check-sources-formatted.ps1 +++ b/src/Qir/check-sources-formatted.ps1 @@ -12,6 +12,11 @@ param ( if (-not $IsMacOS) { # We do not control the clang-format version on MacOS, and that version (12.0.1) requires formatting contradicting the version on Win and Linux (11.1.0). $tmpFile = "format.log" + $clangFormatCommand = "clang-format" + if(($IsLinux) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Lin")))) { + $script:clangFormatCommand = "clang-format-11" + } + $OldErrorActionPreference = $ErrorActionPreference $ErrorActionPreference='Continue' "*.cpp", "*.c", "*.h", "*.hpp" ` @@ -24,7 +29,7 @@ if (-not $IsMacOS) { # We do not control the clang-format version on MacOS, an | Where-Object { $_ -notlike "*/bin/*" } ` | Where-Object {$_ -notlike "*/FullStateDriverGenerator/*"} ` | ForEach-Object { - clang-format -n -style=file $_ + & $clangFormatCommand -n -style=file $_ } 2>$tmpFile $ErrorActionPreference=$OldErrorActionPreference @@ -38,7 +43,7 @@ if (-not $IsMacOS) { # We do not control the clang-format version on MacOS, an Write-Host "##vso[task.logissue type=error;]Formatting check failed. The following files need to be formatted before compiling: " Write-Host "(You may use the Clang-Format extension in VSCode, clang-format in command line, or see https://clang.llvm.org/docs/ClangFormat.html)" $filesRequireFormatting | Format-Table - clang-format --version + & $clangFormatCommand --version throw "Formatting check failed for QIR Runtime sources" } } From 0d3fe1b0b661cbde3fe127aef32bc73d9e82d0b6 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Tue, 7 Sep 2021 17:23:54 -0700 Subject: [PATCH 09/11] Fixed Env:/AGENT_OS --- bootstrap.ps1 | 2 +- src/Qir/Runtime/prerequisites.ps1 | 2 +- src/Qir/check-sources-formatted.ps1 | 2 +- src/Qir/qir-utils.ps1 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bootstrap.ps1 b/bootstrap.ps1 index c0498281eb0..44db7e6c17a 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -19,7 +19,7 @@ Push-Location (Join-Path $PSScriptRoot "./src/Simulation/qdk_sim_rs") --version $Env:NUGET_VERSION; 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 (-not (Test-Path Env:/AGENT_OS)) { # If not CI build, i.e. local build (if AGENT_OS envvar is not defined) if ($Env:ENABLE_NATIVE -ne "false") { Write-Host "Build release flavor of the native simulator" $Env:BUILD_CONFIGURATION = "Release" diff --git a/src/Qir/Runtime/prerequisites.ps1 b/src/Qir/Runtime/prerequisites.ps1 index 2df5a4b41c6..74b9607269d 100644 --- a/src/Qir/Runtime/prerequisites.ps1 +++ b/src/Qir/Runtime/prerequisites.ps1 @@ -4,7 +4,7 @@ #Requires -Version 6.0 if ($Env:ENABLE_QIRRUNTIME -ne "false") { - if (($IsWindows) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Win")))) { + if (($IsWindows) -or ((Test-Path Env:/AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Win")))) { if (!(Get-Command clang -ErrorAction SilentlyContinue) -or ` !(Get-Command clang-format -ErrorAction SilentlyContinue)) { choco install llvm --version=11.1.0 diff --git a/src/Qir/check-sources-formatted.ps1 b/src/Qir/check-sources-formatted.ps1 index 2d0ea63d040..d99f1441556 100644 --- a/src/Qir/check-sources-formatted.ps1 +++ b/src/Qir/check-sources-formatted.ps1 @@ -13,7 +13,7 @@ if (-not $IsMacOS) { # We do not control the clang-format version on MacOS, an $tmpFile = "format.log" $clangFormatCommand = "clang-format" - if(($IsLinux) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Lin")))) { + if(($IsLinux) -or ((Test-Path Env:/AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Lin")))) { $script:clangFormatCommand = "clang-format-11" } diff --git a/src/Qir/qir-utils.ps1 b/src/Qir/qir-utils.ps1 index 4f326c6634b..2d507d87e3e 100644 --- a/src/Qir/qir-utils.ps1 +++ b/src/Qir/qir-utils.ps1 @@ -173,7 +173,7 @@ function Build-CMakeProject { } # if ($Env:BUILD_CONFIGURATION -eq "Debug") - if (($IsMacOS) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Darwin")))) + if (($IsMacOS) -or ((Test-Path Env:/AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Darwin")))) { Write-Host "On MacOS build $Name using the default C/C++ compiler (should be AppleClang)" } From 8dbc625662971733256f0ba1e17b69ace6fd729c Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Mon, 13 Sep 2021 12:03:04 -0700 Subject: [PATCH 10/11] Fixed some more checks --- src/Qir/.clang-tidy | 8 +- src/Qir/Runtime/lib/QIR/QubitManager.cpp | 783 +++++++++++------------ src/Qir/Runtime/lib/QIR/strings.cpp | 6 +- src/Qir/Runtime/public/CoreTypes.hpp | 2 +- src/Qir/Runtime/public/OutputStream.hpp | 8 +- src/Qir/Runtime/public/QirRuntime.hpp | 4 +- src/Qir/Runtime/public/QirTypes.hpp | 22 +- src/Qir/Runtime/public/QubitManager.hpp | 18 +- 8 files changed, 429 insertions(+), 422 deletions(-) diff --git a/src/Qir/.clang-tidy b/src/Qir/.clang-tidy index 1453c2f444f..184f559ddff 100644 --- a/src/Qir/.clang-tidy +++ b/src/Qir/.clang-tidy @@ -1,5 +1,11 @@ Checks: - 'bugprone-*,readability-identifier-*,readability-braces-around-statements,cert*' + 'bugprone-*,readability-identifier-*,readability-braces-around-statements,cert*,\ + -llvmlibc-callee-namespace,-llvmlibc-implementation-in-namespace,\ + -llvmlibc-restrict-system-libc-headers,-modernize-use-trailing-return-type,\ + -fuchsia-default-arguments-calls,-fuchsia-default-arguments-declarations, + -google-readability-casting' +# TODO(rokuzmin): '*, . . .' + WarningsAsErrors: '*' HeaderFilterRegex: '.*' diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp index b858767b3f1..d0013d4106f 100644 --- a/src/Qir/Runtime/lib/QIR/QubitManager.cpp +++ b/src/Qir/Runtime/lib/QIR/QubitManager.cpp @@ -5,513 +5,506 @@ #include "QirRuntime.hpp" // For quantum__rt__fail_cstr #include // For memcpy -namespace Microsoft -{ -namespace Quantum +namespace Microsoft::Quantum { - // - // Failing in case of errors - // +// +// Failing in case of errors +// + +[[noreturn]] static void FailNow(const char* message) +{ + __quantum__rt__fail_cstr(message); +} - [[noreturn]] static void FailNow(const char* message) +static void FailIf(bool condition, const char* message) +{ + if (condition) { __quantum__rt__fail_cstr(message); } +} + +// +// QubitListInSharedArray +// - static void FailIf(bool condition, const char* message) +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++) { - if (condition) - { - __quantum__rt__fail_cstr(message); - } + 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; +} - // - // QubitListInSharedArray - // +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."); - CQubitManager::QubitListInSharedArray::QubitListInSharedArray(QubitIdType startId, QubitIdType endId, - QubitIdType* sharedQbitStatusArray) - : firstElement(startId) - , lastElement(endId) + // If the list is empty, we initialize it with the new element. + if (IsEmpty()) { - FailIf(startId > endId || startId < 0 || endId == MaximumQubitCapacity, - "Incorrect boundaries in the linked list initialization."); - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); + firstElement = id; + lastElement = id; + sharedQbitStatusArray[id] = NoneMarker; // List with a single elemenet in the chain. + return; + } - 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. + 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. +} + +CQubitManager::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. } - bool CQubitManager::QubitListInSharedArray::IsEmpty() const + // Drop pointer to the last element if list becomes empty. + if (IsEmpty()) { - return firstElement == NoneMarker; + lastElement = NoneMarker; } - void CQubitManager::QubitListInSharedArray::AddQubit(QubitIdType id, QubitIdType* sharedQbitStatusArray) + if (result != NoneMarker) { - FailIf(id == NoneMarker, "Incorrect qubit id, cannot add it to the list."); - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); + sharedQbitStatusArray[result] = AllocatedMarker; + } - // 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; - } + return result; +} + +void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, + QubitIdType* sharedQbitStatusArray) +{ + FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - 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. + // No need to do anthing if source is empty. + if (source.IsEmpty()) + { + return; } - CQubitManager::QubitIdType CQubitManager::QubitListInSharedArray::TakeQubitFromFront( - QubitIdType* sharedQbitStatusArray) + if (this->IsEmpty()) + { + // If this list is empty, we'll just set it to the source list. + lastElement = source.lastElement; + } + else { - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); + // 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. - // First element will be returned. It is 'NoneMarker' if the list is empty. - QubitIdType result = firstElement; + // Remove all elements from source. + source.firstElement = NoneMarker; + source.lastElement = NoneMarker; +} - // 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; - } +// +// RestrictedReuseArea +// - if (result != NoneMarker) - { - sharedQbitStatusArray[result] = AllocatedMarker; - } +CQubitManager::RestrictedReuseArea::RestrictedReuseArea(QubitListInSharedArray freeQubits) + : FreeQubitsReuseProhibited() // Default costructor + , FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. +{ +} - return result; - } - void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, - QubitIdType* sharedQbitStatusArray) - { - FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); +// +// CRestrictedReuseAreaStack +// - // No need to do anthing if source is empty. - if (source.IsEmpty()) - { - return; - } +void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) +{ + FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); + this->insert(this->end(), area); +} - 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. +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; +} - // Remove all elements from source. - source.firstElement = NoneMarker; - source.lastElement = NoneMarker; - } +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()); +} - // - // RestrictedReuseArea - // +// +// CQubitManager +// - CQubitManager::RestrictedReuseArea::RestrictedReuseArea(QubitListInSharedArray freeQubits) - : FreeQubitsReuseProhibited() // Default costructor - , FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. - { - } +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); - // - // CRestrictedReuseAreaStack - // + freeQubitCount = qubitCapacity; +} - void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) +CQubitManager::~CQubitManager() +{ + if (sharedQubitStatusArray != nullptr) { - FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); - this->insert(this->end(), area); + delete[] sharedQubitStatusArray; + sharedQubitStatusArray = nullptr; } + // freeQubitsInAreas - direct member of the class, no need to delete. +} - CQubitManager::RestrictedReuseArea CQubitManager::CRestrictedReuseAreaStack::PopFromBack() +// 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()) { - FailIf(this->empty(), "Cannot remove restricted reuse area from an empty set."); - RestrictedReuseArea result = this->back(); - this->pop_back(); - return result; + areaAboutToBegin.prevAreaWithFreeQubits = currentArea.prevAreaWithFreeQubits; } - - CQubitManager::RestrictedReuseArea& CQubitManager::CRestrictedReuseAreaStack::PeekBack() + else { - return this->back(); + areaAboutToBegin.prevAreaWithFreeQubits = freeQubitsInAreas.Count() - 1; } + freeQubitsInAreas.PushToBack(areaAboutToBegin); +} - int32_t CQubitManager::CRestrictedReuseAreaStack::Count() const +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) { - // The size should never exceed int32_t. - return static_cast(this->size()); + 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); +} - // - // CQubitManager - // +Qubit CQubitManager::Allocate() +{ + QubitIdType newQubitId = AllocateQubitId(); + FailIf(newQubitId == NoneMarker, "Not enough qubits."); + return CreateQubitObject(newQubitId); +} - CQubitManager::CQubitManager(QubitIdType initialQubitCapacity, bool mayExtendCap) - : mayExtendCapacity(mayExtendCap) - , qubitCapacity(initialQubitCapacity) +void CQubitManager::Allocate(Qubit* qubitsToAllocate, int32_t qubitCountToAllocate) +{ + if (qubitCountToAllocate == 0) { - 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; + return; } + FailIf(qubitCountToAllocate < 0, "Cannot allocate negative number of qubits."); + FailIf(qubitsToAllocate == nullptr, "No array provided for qubits to be allocated."); - CQubitManager::~CQubitManager() + // Consider optimization for initial allocation of a large array at once + for (int32_t i = 0; i < qubitCountToAllocate; i++) { - 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 + QubitIdType newQubitId = AllocateQubitId(); + if (newQubitId == NoneMarker) { - areaAboutToBegin.prevAreaWithFreeQubits = freeQubitsInAreas.Count() - 1; + for (int32_t k = 0; k < i; k++) + { + Release(qubitsToAllocate[k]); + } + FailNow("Not enough qubits."); } - freeQubitsInAreas.PushToBack(areaAboutToBegin); + qubitsToAllocate[i] = CreateQubitObject(newQubitId); } +} - 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::Release(Qubit qubit) +{ + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + ReleaseQubitId(QubitToId(qubit)); + DeleteQubitObject(qubit); +} - void CQubitManager::EndRestrictedReuseArea() +void CQubitManager::Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease) +{ + if (qubitCountToRelease == 0) { - 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); + return; } + FailIf(qubitCountToRelease < 0, "Cannot release negative number of qubits."); + FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); - Qubit CQubitManager::Allocate() + for (int32_t i = 0; i < qubitCountToRelease; i++) { - QubitIdType newQubitId = AllocateQubitId(); - FailIf(newQubitId == NoneMarker, "Not enough qubits."); - return CreateQubitObject(newQubitId); + Release(qubitsToRelease[i]); + qubitsToRelease[i] = nullptr; } +} - void CQubitManager::Allocate(Qubit* 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."); +Qubit CQubitManager::Borrow() +{ + // We don't support true borrowing/returning at the moment. + return Allocate(); +} - // 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 (int32_t k = 0; k < i; k++) - { - Release(qubitsToAllocate[k]); - } - FailNow("Not enough qubits."); - } - qubitsToAllocate[i] = CreateQubitObject(newQubitId); - } - } +void CQubitManager::Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow) +{ + // We don't support true borrowing/returning at the moment. + return Allocate(qubitsToBorrow, qubitCountToBorrow); +} - void CQubitManager::Release(Qubit qubit) - { - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - ReleaseQubitId(QubitToId(qubit)); - DeleteQubitObject(qubit); - } +void CQubitManager::Return(Qubit qubit) +{ + // We don't support true borrowing/returning at the moment. + Release(qubit); +} - void CQubitManager::Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease) - { - if (qubitCountToRelease == 0) - { - return; - } - FailIf(qubitCountToRelease < 0, "Cannot release negative number of qubits."); - FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); +void CQubitManager::Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn) +{ + // We don't support true borrowing/returning at the moment. + Release(qubitsToReturn, qubitCountToReturn); +} - for (int32_t i = 0; i < qubitCountToRelease; i++) - { - Release(qubitsToRelease[i]); - qubitsToRelease[i] = nullptr; - } - } +void CQubitManager::Disable(Qubit qubit) +{ + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + QubitIdType id = QubitToId(qubit); - Qubit CQubitManager::Borrow() - { - // We don't support true borrowing/returning at the moment. - return Allocate(); - } + // We can only disable explicitly allocated qubits that were not borrowed. + FailIf(!IsExplicitlyAllocatedId(id), "Cannot disable qubit that is not explicitly allocated."); + sharedQubitStatusArray[id] = DisabledMarker; - void CQubitManager::Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow) - { - // We don't support true borrowing/returning at the moment. - return Allocate(qubitsToBorrow, qubitCountToBorrow); - } + disabledQubitCount++; + FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); +} - void CQubitManager::Return(Qubit qubit) +void CQubitManager::Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable) +{ + if (qubitCountToDisable == 0) { - // We don't support true borrowing/returning at the moment. - Release(qubit); + return; } + FailIf(qubitCountToDisable < 0, "Cannot disable negative number of qubits."); + FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); - void CQubitManager::Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn) + for (int32_t i = 0; i < qubitCountToDisable; i++) { - // We don't support true borrowing/returning at the moment. - Release(qubitsToReturn, qubitCountToReturn); + Disable(qubitsToDisable[i]); } +} - void CQubitManager::Disable(Qubit qubit) - { - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - QubitIdType id = QubitToId(qubit); +bool CQubitManager::IsValidId(QubitIdType id) const +{ + return (id >= 0) && (id < qubitCapacity); +} - // We can only disable explicitly allocated qubits that were not borrowed. - FailIf(!IsExplicitlyAllocatedId(id), "Cannot disable qubit that is not explicitly allocated."); - sharedQubitStatusArray[id] = DisabledMarker; +bool CQubitManager::IsDisabledId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] == DisabledMarker; +} - disabledQubitCount++; - FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); - } +bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] == AllocatedMarker; +} - void CQubitManager::Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable) - { - if (qubitCountToDisable == 0) - { - return; - } - FailIf(qubitCountToDisable < 0, "Cannot disable negative number of qubits."); - FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); +bool CQubitManager::IsFreeId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] >= 0; +} - for (int32_t i = 0; i < qubitCountToDisable; i++) - { - Disable(qubitsToDisable[i]); - } - } - bool CQubitManager::IsValidId(QubitIdType id) const - { - return (id >= 0) && (id < qubitCapacity); - } +bool CQubitManager::IsValidQubit(Qubit qubit) const +{ + return IsValidId(QubitToId(qubit)); +} - bool CQubitManager::IsDisabledId(QubitIdType id) const - { - return sharedQubitStatusArray[id] == DisabledMarker; - } +bool CQubitManager::IsDisabledQubit(Qubit qubit) const +{ + return IsValidQubit(qubit) && IsDisabledId(QubitToId(qubit)); +} - bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const - { - return sharedQubitStatusArray[id] == AllocatedMarker; - } +bool CQubitManager::IsExplicitlyAllocatedQubit(Qubit qubit) const +{ + return IsValidQubit(qubit) && IsExplicitlyAllocatedId(QubitToId(qubit)); +} - bool CQubitManager::IsFreeId(QubitIdType id) const - { - return sharedQubitStatusArray[id] >= 0; - } +bool CQubitManager::IsFreeQubitId(QubitIdType id) const +{ + return IsValidId(id) && IsFreeId(id); +} +CQubitManager::QubitIdType CQubitManager::GetQubitId(Qubit qubit) const +{ + FailIf(!IsValidQubit(qubit), "Not a valid qubit."); + return QubitToId(qubit); +} - bool CQubitManager::IsValidQubit(Qubit qubit) const - { - return IsValidId(QubitToId(qubit)); - } - bool CQubitManager::IsDisabledQubit(Qubit qubit) const - { - return IsValidQubit(qubit) && IsDisabledId(QubitToId(qubit)); - } +Qubit CQubitManager::CreateQubitObject(QubitIdType id) +{ + // Make sure the static_cast won't overflow: + FailIf(id < 0 || id >= MaximumQubitCapacity, "Qubit id is out of range."); + intptr_t pointerSizedId = static_cast(id); + return reinterpret_cast(pointerSizedId); +} - bool CQubitManager::IsExplicitlyAllocatedQubit(Qubit qubit) const - { - return IsValidQubit(qubit) && IsExplicitlyAllocatedId(QubitToId(qubit)); - } +void CQubitManager::DeleteQubitObject(Qubit /*qubit*/) +{ + // Do nothing. By default we store qubit Id in place of a pointer to a qubit. +} - bool CQubitManager::IsFreeQubitId(QubitIdType id) const - { - return IsValidId(id) && IsFreeId(id); - } +CQubitManager::QubitIdType CQubitManager::QubitToId(Qubit qubit) const +{ + intptr_t pointerSizedId = reinterpret_cast(qubit); + // Make sure the static_cast won't overflow: + FailIf(pointerSizedId < 0 || pointerSizedId > std::numeric_limits::max(), "Qubit id is out of range."); + return static_cast(pointerSizedId); +} - CQubitManager::QubitIdType CQubitManager::GetQubitId(Qubit qubit) const +void CQubitManager::EnsureCapacity(QubitIdType requestedCapacity) +{ + FailIf(requestedCapacity <= 0, "Requested qubit capacity must be positive."); + if (requestedCapacity <= qubitCapacity) { - FailIf(!IsValidQubit(qubit), "Not a valid qubit."); - return QubitToId(qubit); + 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); - Qubit CQubitManager::CreateQubitObject(QubitIdType id) - { - // Make sure the static_cast won't overflow: - FailIf(id < 0 || id >= MaximumQubitCapacity, "Qubit id is out of range."); - intptr_t pointerSizedId = static_cast(id); - return reinterpret_cast(pointerSizedId); - } - - void CQubitManager::DeleteQubitObject(Qubit /*qubit*/) - { - // Do nothing. By default we store qubit Id in place of a pointer to a qubit. - } + // 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); +} - CQubitManager::QubitIdType CQubitManager::QubitToId(Qubit qubit) const - { - intptr_t pointerSizedId = reinterpret_cast(qubit); - // Make sure the static_cast won't overflow: - FailIf(pointerSizedId < 0 || pointerSizedId > std::numeric_limits::max(), - "Qubit id is out of range."); - return static_cast(pointerSizedId); - } +CQubitManager::QubitIdType CQubitManager::TakeFreeQubitId() +{ + // First we try to take qubit from the current (innermost) area. + QubitIdType id = freeQubitsInAreas.PeekBack().FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); - void CQubitManager::EnsureCapacity(QubitIdType requestedCapacity) + // Then, if no free qubits available for reuse, we scan outer areas (if they exist). + if (id == NoneMarker && freeQubitsInAreas.Count() >= 2) { - FailIf(requestedCapacity <= 0, "Requested qubit capacity must be positive."); - if (requestedCapacity <= qubitCapacity) + int32_t areaIndex = freeQubitsInAreas.Count() - 1; + do { - 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 - QubitIdType* newStatusArray = new QubitIdType[(size_t)requestedCapacity]; - memcpy(newStatusArray, sharedQubitStatusArray, (size_t)qubitCapacity * sizeof(newStatusArray[0])); - QubitListInSharedArray newFreeQubits(qubitCapacity, requestedCapacity - 1, newStatusArray); + areaIndex = freeQubitsInAreas[(size_t)areaIndex].prevAreaWithFreeQubits; + id = freeQubitsInAreas[(size_t)areaIndex].FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); + } while ((areaIndex != 0) && (id == NoneMarker)); - // 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); + // We remember previous area where a free qubit was found or 0 if none were found. + freeQubitsInAreas.PeekBack().prevAreaWithFreeQubits = areaIndex; } - CQubitManager::QubitIdType CQubitManager::TakeFreeQubitId() + if (id != NoneMarker) { - // 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; + 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."); } - CQubitManager::QubitIdType CQubitManager::AllocateQubitId() + return id; +} + +CQubitManager::QubitIdType CQubitManager::AllocateQubitId() +{ + QubitIdType newQubitId = TakeFreeQubitId(); + if (newQubitId == NoneMarker && mayExtendCapacity) { - QubitIdType newQubitId = TakeFreeQubitId(); - if (newQubitId == NoneMarker && mayExtendCapacity) - { - QubitIdType newQubitCapacity = qubitCapacity * 2; - FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); - EnsureCapacity(newQubitCapacity); - newQubitId = TakeFreeQubitId(); - } - return newQubitId; + QubitIdType newQubitCapacity = qubitCapacity * 2; + FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); + EnsureCapacity(newQubitCapacity); + newQubitId = TakeFreeQubitId(); } + return newQubitId; +} - void CQubitManager::ReleaseQubitId(QubitIdType id) +void CQubitManager::ReleaseQubitId(QubitIdType id) +{ + FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Cannot release an invalid qubit."); + if (IsDisabledId(id)) { - FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Cannot release an invalid qubit."); - if (IsDisabledId(id)) - { - // Nothing to do. Qubit will stay disabled. - return; - } + // Nothing to do. Qubit will stay disabled. + return; + } - FailIf(!IsExplicitlyAllocatedId(id), "Attempt to free qubit that has not been allocated."); + 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); + // 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."); - } + freeQubitCount++; + FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); +} -} // namespace Quantum -} // namespace Microsoft +} // namespace Microsoft::Quantum diff --git a/src/Qir/Runtime/lib/QIR/strings.cpp b/src/Qir/Runtime/lib/QIR/strings.cpp index 7165004309f..7fc034bce7c 100644 --- a/src/Qir/Runtime/lib/QIR/strings.cpp +++ b/src/Qir/Runtime/lib/QIR/strings.cpp @@ -28,7 +28,7 @@ static QirString* CreateOrReuseAlreadyAllocated(std::string&& str) if (alreadyAllocated != AllocatedStrings().end()) { qstr = alreadyAllocated->second; - assert(qstr->str.compare(str) == 0); + assert(qstr->str == str); qstr->refCount++; } else @@ -95,7 +95,7 @@ extern "C" // 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.compare(right->str) == 0)); + assert((left == right) == (left->str == right->str)); return left == right; } @@ -115,7 +115,7 @@ extern "C" // 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"); + std::size_t pos1 = str.find_last_not_of('0'); if (pos1 != std::string::npos) { str.erase(pos1 + 1); diff --git a/src/Qir/Runtime/public/CoreTypes.hpp b/src/Qir/Runtime/public/CoreTypes.hpp index 39ad836335c..d0817b41beb 100644 --- a/src/Qir/Runtime/public/CoreTypes.hpp +++ b/src/Qir/Runtime/public/CoreTypes.hpp @@ -36,7 +36,7 @@ class QUBIT; typedef QUBIT* Qubit; // Not a pointer to a memory location, just an integer - qubit id. class RESULT; -typedef RESULT* Result; // TODO: Replace with `typedef uintXX_t Result`, where XX is 8|16|32|64. +typedef RESULT* Result; // TODO(rokuzmin): Replace with `typedef uintXX_t Result`, where XX is 8|16|32|64. // Remove all the `RESULT`. enum ResultValue diff --git a/src/Qir/Runtime/public/OutputStream.hpp b/src/Qir/Runtime/public/OutputStream.hpp index 06ca71118ac..76a2547f170 100644 --- a/src/Qir/Runtime/public/OutputStream.hpp +++ b/src/Qir/Runtime/public/OutputStream.hpp @@ -15,9 +15,15 @@ namespace Quantum { struct QIR_SHARED_API ScopedRedirector { - ScopedRedirector(std::ostream& newOstream); + 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; }; diff --git a/src/Qir/Runtime/public/QirRuntime.hpp b/src/Qir/Runtime/public/QirRuntime.hpp index da45f952556..36c17e74d89 100644 --- a/src/Qir/Runtime/public/QirRuntime.hpp +++ b/src/Qir/Runtime/public/QirRuntime.hpp @@ -4,7 +4,7 @@ #pragma once #include -#include // for va_list +#include // for va_list #include "CoreTypes.hpp" #include "QirTypes.hpp" @@ -310,7 +310,7 @@ extern "C" // TODO QIR_SHARED_API bool __quantum__rt__bigint_greater_eq(QirBigInt*, QirBigInt*); // NOLINT } -// TODO: Consider separating the `extern "C"` exports and C++ exports. +// 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 diff --git a/src/Qir/Runtime/public/QirTypes.hpp b/src/Qir/Runtime/public/QirTypes.hpp index 301ffafeda9..3de19d2b2e0 100644 --- a/src/Qir/Runtime/public/QirTypes.hpp +++ b/src/Qir/Runtime/public/QirTypes.hpp @@ -43,13 +43,13 @@ struct QIR_SHARED_API QirArray int AddRef(); int Release(); - QirArray(TItemCount cQubits); + explicit QirArray(TItemCount cQubits); QirArray(TItemCount cItems, TItemSize itemSizeInBytes, TDimCount dimCount = 1, TDimContainer&& dimSizes = {}); QirArray(const QirArray& other); ~QirArray(); - char* GetItemPointer(TItemCount index) const; + [[nodiscard]] char* GetItemPointer(TItemCount index) const; void Append(const QirArray* other); }; @@ -61,8 +61,8 @@ struct QIR_SHARED_API QirString long refCount = 1; std::string str; - QirString(std::string&& str); - QirString(const char* cstr); + explicit QirString(std::string&& str); + explicit QirString(const char* cstr); }; /*====================================================================================================================== @@ -72,10 +72,10 @@ struct QIR_SHARED_API QirString a header that contains the relevant data. The header immediately precedes the tuple's buffer in memory when the tuple is created. ======================================================================================================================*/ -// TODO: Move these types to inside of `QirTupleHeader`. +// TODO (rokuzmin): Move these types to inside of `QirTupleHeader`. using PTuplePointedType = uint8_t; -using PTuple = PTuplePointedType*; // TODO: consider replacing `uint8_t*` with `void*` in order to block the accidental - // {dereferencing and pointer arithmtic}. +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 { @@ -90,7 +90,7 @@ struct QIR_SHARED_API QirTupleHeader PTuple AsTuple() { - return data; + return (PTuple)data; } int AddRef(); @@ -139,12 +139,12 @@ static_assert(sizeof(TupleWithControls) == 2 * sizeof(void*), /*====================================================================================================================== QirCallable ======================================================================================================================*/ -typedef void (*t_CallableEntry)(PTuple, PTuple, PTuple); // TODO: Move to `QirCallable::t_CallableEntry`. -typedef void (*t_CaptureCallback)(PTuple, int32_t); // TODO: Move to `QirCallable::t_CaptureCallback`. +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 = 1 << 1; + static int constexpr Controlled = 1u << 1; private: static int constexpr TableSize = 4; diff --git a/src/Qir/Runtime/public/QubitManager.hpp b/src/Qir/Runtime/public/QubitManager.hpp index 2beaec7e2cf..d1cb70b173f 100644 --- a/src/Qir/Runtime/public/QubitManager.hpp +++ b/src/Qir/Runtime/public/QubitManager.hpp @@ -208,7 +208,7 @@ namespace Quantum int32_t prevAreaWithFreeQubits = 0; RestrictedReuseArea() = default; - RestrictedReuseArea(QubitListInSharedArray freeQubits); + explicit RestrictedReuseArea(QubitListInSharedArray freeQubits); }; // This is NOT a pure stack! We modify it only by push/pop, but we also iterate over elements. @@ -219,15 +219,16 @@ namespace Quantum CRestrictedReuseAreaStack() = default; CRestrictedReuseAreaStack(const CRestrictedReuseAreaStack&) = delete; CRestrictedReuseAreaStack& operator=(const CRestrictedReuseAreaStack&) = delete; - ~CRestrictedReuseAreaStack() = default; + CRestrictedReuseAreaStack(CRestrictedReuseAreaStack&&) = delete; + CRestrictedReuseAreaStack& operator=(CRestrictedReuseAreaStack&&) = delete; + ~CRestrictedReuseAreaStack() = default; void PushToBack(RestrictedReuseArea area); RestrictedReuseArea PopFromBack(); RestrictedReuseArea& PeekBack(); - int32_t Count() const; + [[nodiscard]] int32_t Count() const; }; - private: void EnsureCapacity(QubitIdType requestedCapacity); // Take free qubit id from a free list without extending capacity. @@ -238,11 +239,12 @@ namespace Quantum // Put qubit id back into a free list for the current restricted reuse area. void ReleaseQubitId(QubitIdType id); - bool IsValidId(QubitIdType id) const; - bool IsDisabledId(QubitIdType id) const; - bool IsFreeId(QubitIdType id) const; - bool IsExplicitlyAllocatedId(QubitIdType id) const; + [[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; From cf87d70d1cd411a1c1295533204a3f6db1ed4cf4 Mon Sep 17 00:00:00 2001 From: Robin Kuzmin Date: Mon, 13 Sep 2021 12:52:02 -0700 Subject: [PATCH 11/11] Minor clean-up. --- src/Qir/Runtime/lib/QIR/QubitManager.cpp | 783 ++++++++++++----------- 1 file changed, 395 insertions(+), 388 deletions(-) diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp index d0013d4106f..9e44aaf33e0 100644 --- a/src/Qir/Runtime/lib/QIR/QubitManager.cpp +++ b/src/Qir/Runtime/lib/QIR/QubitManager.cpp @@ -5,506 +5,513 @@ #include "QirRuntime.hpp" // For quantum__rt__fail_cstr #include // For memcpy -namespace Microsoft::Quantum +namespace Microsoft { - -// -// Failing in case of errors -// - -[[noreturn]] static void FailNow(const char* message) +namespace Quantum { - __quantum__rt__fail_cstr(message); -} -static void FailIf(bool condition, const char* message) -{ - if (condition) + // + // Failing in case of errors + // + + [[noreturn]] static void FailNow(const char* message) { __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++) + static void FailIf(bool condition, const char* message) { - sharedQbitStatusArray[i] = i + 1; // Current element points to the next element. + if (condition) + { + __quantum__rt__fail_cstr(message); + } } - 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."); + // + // QubitListInSharedArray + // - // If the list is empty, we initialize it with the new element. - if (IsEmpty()) + CQubitManager::QubitListInSharedArray::QubitListInSharedArray(QubitIdType startId, QubitIdType endId, + QubitIdType* sharedQbitStatusArray) + : firstElement(startId) + , lastElement(endId) { - 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. -} - -CQubitManager::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; + FailIf(startId > endId || startId < 0 || endId == MaximumQubitCapacity, + "Incorrect boundaries in the linked list initialization."); + FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - // Advance list start to the next element if list is not empty. - if (!IsEmpty()) - { - firstElement = sharedQbitStatusArray[firstElement]; // The second element will be the first. + 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. } - // Drop pointer to the last element if list becomes empty. - if (IsEmpty()) + bool CQubitManager::QubitListInSharedArray::IsEmpty() const { - lastElement = NoneMarker; + return firstElement == NoneMarker; } - if (result != NoneMarker) + void CQubitManager::QubitListInSharedArray::AddQubit(QubitIdType id, QubitIdType* sharedQbitStatusArray) { - sharedQbitStatusArray[result] = AllocatedMarker; - } - - return result; -} + FailIf(id == NoneMarker, "Incorrect qubit id, cannot add it to the list."); + FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); -void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, - QubitIdType* sharedQbitStatusArray) -{ - 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; + } - // No need to do anthing if source is empty. - if (source.IsEmpty()) - { - 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. } - if (this->IsEmpty()) - { - // If this list is empty, we'll just set it to the source list. - lastElement = source.lastElement; - } - else + CQubitManager::QubitIdType CQubitManager::QubitListInSharedArray::TakeQubitFromFront( + QubitIdType* sharedQbitStatusArray) { - // 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. + FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); - // Remove all elements from source. - source.firstElement = NoneMarker; - source.lastElement = NoneMarker; -} + // 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. + } -// -// RestrictedReuseArea -// + // Drop pointer to the last element if list becomes empty. + if (IsEmpty()) + { + lastElement = NoneMarker; + } -CQubitManager::RestrictedReuseArea::RestrictedReuseArea(QubitListInSharedArray freeQubits) - : FreeQubitsReuseProhibited() // Default costructor - , FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. -{ -} + if (result != NoneMarker) + { + sharedQbitStatusArray[result] = AllocatedMarker; + } + return result; + } -// -// CRestrictedReuseAreaStack -// + void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, + QubitIdType* sharedQbitStatusArray) + { + FailIf(sharedQbitStatusArray == nullptr, "Shared status array is not provided."); -void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) -{ - FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); - this->insert(this->end(), area); -} + // No need to do anthing if source is empty. + if (source.IsEmpty()) + { + return; + } -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; -} + 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. -CQubitManager::RestrictedReuseArea& CQubitManager::CRestrictedReuseAreaStack::PeekBack() -{ - return this->back(); -} + // Remove all elements from source. + source.firstElement = NoneMarker; + source.lastElement = NoneMarker; + } -int32_t CQubitManager::CRestrictedReuseAreaStack::Count() const -{ - // The size should never exceed int32_t. - return static_cast(this->size()); -} -// -// CQubitManager -// + // + // RestrictedReuseArea + // -CQubitManager::CQubitManager(QubitIdType initialQubitCapacity, bool mayExtendCap) - : mayExtendCapacity(mayExtendCap) - , qubitCapacity(initialQubitCapacity) -{ - FailIf(qubitCapacity <= 0, "Qubit capacity must be positive."); - sharedQubitStatusArray = new QubitIdType[(size_t)qubitCapacity]; + CQubitManager::RestrictedReuseArea::RestrictedReuseArea(QubitListInSharedArray freeQubits) + : FreeQubitsReuseProhibited() // Default costructor + , FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. + { + } - // These objects are passed by value (copies are created) - QubitListInSharedArray freeQubitsFresh(0, qubitCapacity - 1, sharedQubitStatusArray); - RestrictedReuseArea outermostArea(freeQubitsFresh); - freeQubitsInAreas.PushToBack(outermostArea); - freeQubitCount = qubitCapacity; -} + // + // CRestrictedReuseAreaStack + // -CQubitManager::~CQubitManager() -{ - if (sharedQubitStatusArray != nullptr) + void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) { - delete[] sharedQubitStatusArray; - sharedQubitStatusArray = nullptr; + FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); + this->insert(this->end(), area); } - // 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()) + CQubitManager::RestrictedReuseArea CQubitManager::CRestrictedReuseAreaStack::PopFromBack() { - areaAboutToBegin.prevAreaWithFreeQubits = currentArea.prevAreaWithFreeQubits; + FailIf(this->empty(), "Cannot remove restricted reuse area from an empty set."); + RestrictedReuseArea result = this->back(); + this->pop_back(); + return result; } - else + + CQubitManager::RestrictedReuseArea& CQubitManager::CRestrictedReuseAreaStack::PeekBack() { - areaAboutToBegin.prevAreaWithFreeQubits = freeQubitsInAreas.Count() - 1; + return this->back(); } - 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) + int32_t CQubitManager::CRestrictedReuseAreaStack::Count() const { - containingArea.prevAreaWithFreeQubits = areaAboutToEnd.prevAreaWithFreeQubits; + // The size should never exceed int32_t. + return static_cast(this->size()); } - // 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); -} -Qubit CQubitManager::Allocate() -{ - QubitIdType newQubitId = AllocateQubitId(); - FailIf(newQubitId == NoneMarker, "Not enough qubits."); - return CreateQubitObject(newQubitId); -} + // + // CQubitManager + // -void CQubitManager::Allocate(Qubit* qubitsToAllocate, int32_t qubitCountToAllocate) -{ - if (qubitCountToAllocate == 0) + CQubitManager::CQubitManager(QubitIdType initialQubitCapacity, bool mayExtendCap) + : mayExtendCapacity(mayExtendCap) + , qubitCapacity(initialQubitCapacity) { - return; + 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; } - 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++) + CQubitManager::~CQubitManager() { - QubitIdType newQubitId = AllocateQubitId(); - if (newQubitId == NoneMarker) + if (sharedQubitStatusArray != nullptr) { - for (int32_t k = 0; k < i; k++) - { - Release(qubitsToAllocate[k]); - } - FailNow("Not enough qubits."); + delete[] sharedQubitStatusArray; + sharedQubitStatusArray = nullptr; } - qubitsToAllocate[i] = CreateQubitObject(newQubitId); + // freeQubitsInAreas - direct member of the class, no need to delete. } -} -void CQubitManager::Release(Qubit qubit) -{ - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - ReleaseQubitId(QubitToId(qubit)); - DeleteQubitObject(qubit); -} - -void CQubitManager::Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease) -{ - if (qubitCountToRelease == 0) + // Although it is not necessary to pass area IDs to these functions, such support may be added for extra checks. + void CQubitManager::StartRestrictedReuseArea() { - return; + 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); } - FailIf(qubitCountToRelease < 0, "Cannot release negative number of qubits."); - FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); - for (int32_t i = 0; i < qubitCountToRelease; i++) + void CQubitManager::NextRestrictedReuseSegment() { - Release(qubitsToRelease[i]); - qubitsToRelease[i] = nullptr; + 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); } -} -Qubit CQubitManager::Borrow() -{ - // We don't support true borrowing/returning at the moment. - return Allocate(); -} + 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); + } -void CQubitManager::Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow) -{ - // We don't support true borrowing/returning at the moment. - return Allocate(qubitsToBorrow, qubitCountToBorrow); -} + Qubit CQubitManager::Allocate() + { + QubitIdType newQubitId = AllocateQubitId(); + FailIf(newQubitId == NoneMarker, "Not enough qubits."); + return CreateQubitObject(newQubitId); + } -void CQubitManager::Return(Qubit qubit) -{ - // We don't support true borrowing/returning at the moment. - Release(qubit); -} + void CQubitManager::Allocate(Qubit* 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."); -void CQubitManager::Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn) -{ - // We don't support true borrowing/returning at the moment. - Release(qubitsToReturn, qubitCountToReturn); -} + // 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 (int32_t k = 0; k < i; k++) + { + Release(qubitsToAllocate[k]); + } + FailNow("Not enough qubits."); + } + qubitsToAllocate[i] = CreateQubitObject(newQubitId); + } + } -void CQubitManager::Disable(Qubit qubit) -{ - FailIf(!IsValidQubit(qubit), "Qubit is not valid."); - QubitIdType id = QubitToId(qubit); + void CQubitManager::Release(Qubit qubit) + { + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + ReleaseQubitId(QubitToId(qubit)); + DeleteQubitObject(qubit); + } - // We can only disable explicitly allocated qubits that were not borrowed. - FailIf(!IsExplicitlyAllocatedId(id), "Cannot disable qubit that is not explicitly allocated."); - sharedQubitStatusArray[id] = DisabledMarker; + void CQubitManager::Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease) + { + if (qubitCountToRelease == 0) + { + return; + } + FailIf(qubitCountToRelease < 0, "Cannot release negative number of qubits."); + FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); - disabledQubitCount++; - FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); -} + for (int32_t i = 0; i < qubitCountToRelease; i++) + { + Release(qubitsToRelease[i]); + qubitsToRelease[i] = nullptr; + } + } -void CQubitManager::Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable) -{ - if (qubitCountToDisable == 0) + Qubit CQubitManager::Borrow() { - return; + // We don't support true borrowing/returning at the moment. + return Allocate(); } - FailIf(qubitCountToDisable < 0, "Cannot disable negative number of qubits."); - FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); - for (int32_t i = 0; i < qubitCountToDisable; i++) + void CQubitManager::Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow) { - Disable(qubitsToDisable[i]); + // We don't support true borrowing/returning at the moment. + return Allocate(qubitsToBorrow, qubitCountToBorrow); } -} -bool CQubitManager::IsValidId(QubitIdType id) const -{ - return (id >= 0) && (id < qubitCapacity); -} + void CQubitManager::Return(Qubit qubit) + { + // We don't support true borrowing/returning at the moment. + Release(qubit); + } -bool CQubitManager::IsDisabledId(QubitIdType id) const -{ - return sharedQubitStatusArray[id] == DisabledMarker; -} + void CQubitManager::Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn) + { + // We don't support true borrowing/returning at the moment. + Release(qubitsToReturn, qubitCountToReturn); + } -bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const -{ - return sharedQubitStatusArray[id] == AllocatedMarker; -} + void CQubitManager::Disable(Qubit qubit) + { + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + QubitIdType id = QubitToId(qubit); -bool CQubitManager::IsFreeId(QubitIdType id) const -{ - return sharedQubitStatusArray[id] >= 0; -} + // We can only disable explicitly allocated qubits that were not borrowed. + FailIf(!IsExplicitlyAllocatedId(id), "Cannot disable qubit that is not explicitly allocated."); + sharedQubitStatusArray[id] = DisabledMarker; + disabledQubitCount++; + FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); + } -bool CQubitManager::IsValidQubit(Qubit qubit) const -{ - return IsValidId(QubitToId(qubit)); -} + void CQubitManager::Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable) + { + if (qubitCountToDisable == 0) + { + return; + } + FailIf(qubitCountToDisable < 0, "Cannot disable negative number of qubits."); + FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); -bool CQubitManager::IsDisabledQubit(Qubit qubit) const -{ - return IsValidQubit(qubit) && IsDisabledId(QubitToId(qubit)); -} + for (int32_t i = 0; i < qubitCountToDisable; i++) + { + Disable(qubitsToDisable[i]); + } + } -bool CQubitManager::IsExplicitlyAllocatedQubit(Qubit qubit) const -{ - return IsValidQubit(qubit) && IsExplicitlyAllocatedId(QubitToId(qubit)); -} + bool CQubitManager::IsValidId(QubitIdType id) const + { + return (id >= 0) && (id < qubitCapacity); + } -bool CQubitManager::IsFreeQubitId(QubitIdType id) const -{ - return IsValidId(id) && IsFreeId(id); -} + bool CQubitManager::IsDisabledId(QubitIdType id) const + { + return sharedQubitStatusArray[id] == DisabledMarker; + } -CQubitManager::QubitIdType CQubitManager::GetQubitId(Qubit qubit) const -{ - FailIf(!IsValidQubit(qubit), "Not a valid qubit."); - return QubitToId(qubit); -} + bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const + { + return sharedQubitStatusArray[id] == AllocatedMarker; + } + bool CQubitManager::IsFreeId(QubitIdType id) const + { + return sharedQubitStatusArray[id] >= 0; + } -Qubit CQubitManager::CreateQubitObject(QubitIdType id) -{ - // Make sure the static_cast won't overflow: - FailIf(id < 0 || id >= MaximumQubitCapacity, "Qubit id is out of range."); - intptr_t pointerSizedId = static_cast(id); - return reinterpret_cast(pointerSizedId); -} -void CQubitManager::DeleteQubitObject(Qubit /*qubit*/) -{ - // Do nothing. By default we store qubit Id in place of a pointer to a qubit. -} + bool CQubitManager::IsValidQubit(Qubit qubit) const + { + return IsValidId(QubitToId(qubit)); + } -CQubitManager::QubitIdType CQubitManager::QubitToId(Qubit qubit) const -{ - intptr_t pointerSizedId = reinterpret_cast(qubit); - // Make sure the static_cast won't overflow: - FailIf(pointerSizedId < 0 || pointerSizedId > std::numeric_limits::max(), "Qubit id is out of range."); - return static_cast(pointerSizedId); -} + bool CQubitManager::IsDisabledQubit(Qubit qubit) const + { + return IsValidQubit(qubit) && IsDisabledId(QubitToId(qubit)); + } -void CQubitManager::EnsureCapacity(QubitIdType requestedCapacity) -{ - FailIf(requestedCapacity <= 0, "Requested qubit capacity must be positive."); - if (requestedCapacity <= qubitCapacity) + bool CQubitManager::IsExplicitlyAllocatedQubit(Qubit qubit) const { - return; + return IsValidQubit(qubit) && IsExplicitlyAllocatedId(QubitToId(qubit)); } - // 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); + bool CQubitManager::IsFreeQubitId(QubitIdType id) const + { + return IsValidId(id) && IsFreeId(id); + } - // 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); -} + CQubitManager::QubitIdType CQubitManager::GetQubitId(Qubit qubit) const + { + FailIf(!IsValidQubit(qubit), "Not a valid qubit."); + return QubitToId(qubit); + } -CQubitManager::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) + Qubit CQubitManager::CreateQubitObject(QubitIdType id) { - 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)); + // Make sure the static_cast won't overflow: + FailIf(id < 0 || id >= MaximumQubitCapacity, "Qubit id is out of range."); + intptr_t pointerSizedId = static_cast(id); + return reinterpret_cast(pointerSizedId); + } - // We remember previous area where a free qubit was found or 0 if none were found. - freeQubitsInAreas.PeekBack().prevAreaWithFreeQubits = areaIndex; + void CQubitManager::DeleteQubitObject(Qubit /*qubit*/) + { + // Do nothing. By default we store qubit Id in place of a pointer to a qubit. } - if (id != NoneMarker) + CQubitManager::QubitIdType CQubitManager::QubitToId(Qubit qubit) const { - 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."); + intptr_t pointerSizedId = reinterpret_cast(qubit); + // Make sure the static_cast won't overflow: + FailIf(pointerSizedId < 0 || pointerSizedId > std::numeric_limits::max(), + "Qubit id is out of range."); + return static_cast(pointerSizedId); } - return 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). -CQubitManager::QubitIdType CQubitManager::AllocateQubitId() -{ - QubitIdType newQubitId = TakeFreeQubitId(); - if (newQubitId == NoneMarker && mayExtendCapacity) + // 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); + } + + CQubitManager::QubitIdType CQubitManager::TakeFreeQubitId() { - QubitIdType newQubitCapacity = qubitCapacity * 2; - FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); - EnsureCapacity(newQubitCapacity); - newQubitId = 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; } - return newQubitId; -} -void CQubitManager::ReleaseQubitId(QubitIdType id) -{ - FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Cannot release an invalid qubit."); - if (IsDisabledId(id)) + CQubitManager::QubitIdType CQubitManager::AllocateQubitId() { - // Nothing to do. Qubit will stay disabled. - return; + QubitIdType newQubitId = TakeFreeQubitId(); + if (newQubitId == NoneMarker && mayExtendCapacity) + { + QubitIdType newQubitCapacity = qubitCapacity * 2; + FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); + EnsureCapacity(newQubitCapacity); + newQubitId = TakeFreeQubitId(); + } + return newQubitId; } - FailIf(!IsExplicitlyAllocatedId(id), "Attempt to free qubit that has not been allocated."); + 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; + } - // 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); + FailIf(!IsExplicitlyAllocatedId(id), "Attempt to free qubit that has not been allocated."); - freeQubitCount++; - FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); - allocatedQubitCount--; - FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); -} + // 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 Microsoft::Quantum +} // namespace Quantum +} // namespace Microsoft