Skip to content

Commit

Permalink
QInterfaceNoisy (#1014)
Browse files Browse the repository at this point in the history
* Incomplete work towards QInterfaceNoisy

* Nearly compiles

* Compiles (does not apply noise)

* Apply1QbNoise() method

* Fix copy constructor

* Add/fix noise

* Auto fidelity calculation (note last commit had ChatGPT help)

* Noisy interface in shared library

* Default 1% noise per gate

* Debug

* SetNoiseParameter()

* Fix edge case in applying noise
  • Loading branch information
WrathfulSpatula authored Jul 12, 2024
1 parent 1865d57 commit f9a5941
Show file tree
Hide file tree
Showing 12 changed files with 493 additions and 12 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required (VERSION 3.9)
project (Qrack VERSION 9.8.3 DESCRIPTION "High Performance Quantum Bit Simulation" LANGUAGES CXX)
project (Qrack VERSION 9.9.0 DESCRIPTION "High Performance Quantum Bit Simulation" LANGUAGES CXX)

# Installation commands
include (GNUInstallDirs)
Expand Down Expand Up @@ -44,6 +44,7 @@ add_library (qrack STATIC
src/qstabilizerhybrid.cpp
src/qcircuit.cpp
src/qtensornetwork.cpp
src/qinterface_noisy.cpp
)
set_property(TARGET qrack PROPERTY POSITION_INDEPENDENT_CODE ON)
if (ENABLE_PTHREAD AND ENABLE_QUNIT_CPU_PARALLEL)
Expand Down Expand Up @@ -389,6 +390,7 @@ install (FILES
include/qengine_cuda.hpp
include/qtensornetwork.hpp
include/qinterface.hpp
include/qinterface_noisy.hpp
include/qalu.hpp
include/qparity.hpp
include/qneuron.hpp
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,13 +250,15 @@ Set the maximum allowed allocation (in MB) for the global OpenCL pool with `QRAC

Note that this controls total direct Qrack OpenCL buffer allocation, not total Qrack library allocation. Total OpenCL buffer allocation is **not** fully indicative of total library allocation.

## Approximation options
## Approximation and noise options
`QUnit` can optionally round qubit subsystems proactively or on-demand to the nearest single or double qubit eigenstate with the `QRACK_QUNIT_SEPARABILITY_THRESHOLD=[0.0 - 1.0]` environment variable, with a value between `0.0` and `1.0`. When trying to find separable subsystems, Qrack will start by making 3-axis (independent or conditional) probability measurements. Based on the probability measurements, under the assumption that the state _is_ separable, an inverse state preparation to |0> procedure is fixed. If inverse state preparation would bring any single qubit Bloch sphere projection within parameter range of the edge of the Bloch sphere, (with unit length, `1.0`,) then the subsystem will be rounded to that state, normalized, and then "uncomputed" with the corresponding (forward) state preparation, effectively "hyperpolarizing" one and two qubit separable substates by replacing entanglement with local qubit Bloch sphere extent. (If 3-axis probability is _not_ within rounding range, nothing is done directly to the substate.)

Similarly functionality to above is available for `QBdt` with `QRACK_QBDT_SEPARABILITY_THRESHOLD=[0.0 - 0.5]`. In the case of this parameter, any branch with less than the parameter value for probability is rounded to 0, and its partner branch is renormalized to unit length. This same value is also used for branch equality comparison.

Environment variable `QRACK_NONCLIFFORD_ROUNDING_THRESHOLD` sets the non-Clifford phase gate magnitude, as a fraction of `T` gate phase angle, (from the closest Clifford state Bloch sphere orientation,) that will be rounded to 0 in terminal measurement and sampling operations. (For `0`/default value, all non-Clifford phase gates are exactly preserved.)

For single-qubit depolarizing noise channels, `QInterfaceNoisy` exposes environment variable `QRACK_GATE_DEPOLARIZATION`, which is a per-gate, single-qubit (applied to all qubits in gate) noise parameter for depolarizing noise, typically taking a floating-point value between `0` and `1`.

## Vectorization optimization

```sh
Expand Down
2 changes: 1 addition & 1 deletion doxygen.config
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ PROJECT_NAME = "Qrack"
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 9.8
PROJECT_NUMBER = 9.9

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
3 changes: 2 additions & 1 deletion include/pinvoke_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extern "C" {
// non-quantum
MICROSOFT_QUANTUM_DECL int get_error(_In_ uintq sid);
MICROSOFT_QUANTUM_DECL uintq init_count_type(_In_ uintq q, _In_ bool tn, _In_ bool md, _In_ bool sd, _In_ bool sh,
_In_ bool bdt, _In_ bool pg, _In_ bool zxf, _In_ bool hy, _In_ bool oc, _In_ bool dm);
_In_ bool bdt, _In_ bool pg, _In_ bool nw, _In_ bool hy, _In_ bool oc, _In_ bool dm);
MICROSOFT_QUANTUM_DECL uintq init_count(_In_ uintq q, _In_ bool dm);
MICROSOFT_QUANTUM_DECL uintq init_count_pager(_In_ uintq q, _In_ bool dm);
MICROSOFT_QUANTUM_DECL uintq init() { return init_count(0, false); }
Expand Down Expand Up @@ -343,6 +343,7 @@ MICROSOFT_QUANTUM_DECL void SetSdrp(_In_ uintq sid, _In_ double sdrp);
MICROSOFT_QUANTUM_DECL void SetNcrp(_In_ uintq sid, _In_ double ncrp);
MICROSOFT_QUANTUM_DECL void SetReactiveSeparate(_In_ uintq sid, _In_ bool irs);
MICROSOFT_QUANTUM_DECL void SetTInjection(_In_ uintq sid, _In_ bool iti);
MICROSOFT_QUANTUM_DECL void SetNoiseParameter(_In_ uintq sid, _In_ double np);

#if !(FPPOW < 6 && !defined(ENABLE_COMPLEX_X2))
MICROSOFT_QUANTUM_DECL void TimeEvolve(_In_ uintq sid, _In_ double t, _In_ uintq n,
Expand Down
22 changes: 21 additions & 1 deletion include/qfactory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
#include "qbdthybrid.hpp"
#endif

#include "qinterface_noisy.hpp"

namespace Qrack {

/** Factory method to create specific engine implementations. */
Expand Down Expand Up @@ -83,6 +85,8 @@ QInterfacePtr CreateQuantumInterface(
#endif
case QINTERFACE_CPU:
return std::make_shared<QEngineCPU>(args...);
case QINTERFACE_NOISY:
return std::make_shared<QInterfaceNoisy>(engines, args...);
default:
throw std::invalid_argument("CreateQuantumInterface received a request to create a nonexistent type instance!");
}
Expand Down Expand Up @@ -129,6 +133,8 @@ QInterfacePtr CreateQuantumInterface(QInterfaceEngine engine1, QInterfaceEngine
#endif
case QINTERFACE_CPU:
return std::make_shared<QEngineCPU>(args...);
case QINTERFACE_NOISY:
return std::make_shared<QInterfaceNoisy>(engines, args...);
default:
throw std::invalid_argument("CreateQuantumInterface received a request to create a nonexistent type instance!");
}
Expand Down Expand Up @@ -171,6 +177,8 @@ template <typename... Ts> QInterfacePtr CreateQuantumInterface(QInterfaceEngine
#endif
case QINTERFACE_CPU:
return std::make_shared<QEngineCPU>(args...);
case QINTERFACE_NOISY:
return std::make_shared<QInterfaceNoisy>(args...);
default:
throw std::invalid_argument("CreateQuantumInterface received a request to create a nonexistent type instance!");
}
Expand Down Expand Up @@ -237,6 +245,8 @@ template <typename... Ts> QInterfacePtr CreateQuantumInterface(std::vector<QInte
#endif
case QINTERFACE_CPU:
return std::make_shared<QEngineCPU>(args...);
case QINTERFACE_NOISY:
return std::make_shared<QInterfaceNoisy>(engines, args...);
default:
throw std::invalid_argument("CreateQuantumInterface received a request to create a nonexistent type instance!");
}
Expand All @@ -248,7 +258,8 @@ template <typename... Ts> QInterfacePtr CreateQuantumInterface(std::vector<QInte
#define DEVICE_COUNT (CUDAEngine::Instance().GetDeviceCount())
#endif
template <typename... Ts>
QInterfacePtr CreateArrangedLayers(bool md, bool sd, bool sh, bool bdt, bool pg, bool tn, bool hy, bool oc, Ts... args)
QInterfacePtr CreateArrangedLayersFull(
bool nw, bool md, bool sd, bool sh, bool bdt, bool pg, bool tn, bool hy, bool oc, Ts... args)
{
#if ENABLE_OPENCL || ENABLE_CUDA
bool isOcl = oc && (DEVICE_COUNT > 0);
Expand Down Expand Up @@ -294,6 +305,10 @@ QInterfacePtr CreateArrangedLayers(bool md, bool sd, bool sh, bool bdt, bool pg,
simulatorType.push_back(QINTERFACE_TENSOR_NETWORK);
}

if (nw) {
simulatorType.push_back(QINTERFACE_NOISY);
}

// (...then reverse:)
std::reverse(simulatorType.begin(), simulatorType.end());

Expand All @@ -315,5 +330,10 @@ QInterfacePtr CreateArrangedLayers(bool md, bool sd, bool sh, bool bdt, bool pg,

return CreateQuantumInterface(simulatorType, args...);
}
template <typename... Ts>
QInterfacePtr CreateArrangedLayers(bool md, bool sd, bool sh, bool bdt, bool pg, bool tn, bool hy, bool oc, Ts... args)
{
return CreateArrangedLayersFull(false, md, sd, sh, bdt, pg, tn, hy, oc, args...);
}

} // namespace Qrack
19 changes: 18 additions & 1 deletion include/qinterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,15 @@ enum QInterfaceEngine {
QINTERFACE_QUNIT_CLIFFORD,

/**
* Circuit-simplification layer, with (optional) recourse to cuTensorNetwork
* Circuit-simplification layer
*/
QINTERFACE_TENSOR_NETWORK,

/**
* Noisy wrapper layer
*/
QINTERFACE_NOISY,

#if ENABLE_OPENCL || ENABLE_CUDA
QINTERFACE_OPTIMAL_SCHROEDINGER = QINTERFACE_QPAGER,

Expand Down Expand Up @@ -2910,6 +2915,18 @@ class QInterface : public ParallelFor {
* helps.
*/
virtual bool GetTInjection() { return false; }
/**
* Set the noise level option (only for a noisy interface)
*
* If this is a QInterfaceNoisy, adjust the noise level per gate.
*/
virtual void SetNoiseParameter(real1_f lambda) {}
/**
* Get the noise level option (only for a noisy interface)
*
* If this is a QInterfaceNoisy, return the noise level per gate.
*/
virtual real1_f GetNoiseParameter() { return ZERO_R1_F; }

/**
* Clone this QInterface
Expand Down
Loading

0 comments on commit f9a5941

Please sign in to comment.