From d1335319ceb12118a7d3bd231ae247579b6ea072 Mon Sep 17 00:00:00 2001 From: Thien Nguyen <58006629+1tnguyen@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:41:59 +1100 Subject: [PATCH] Support measurement sampling seed for `cutensornet` backends (#2398) * Support measurement sampling seed for cutensornet backends This feature has been added in 24.08 (cutensornet 2.5), hence adding the support for it and re-enable the skipped test. Signed-off-by: Thien Nguyen * Fix a code comment Signed-off-by: Thien Nguyen * Code review: edit variable names and code comments Signed-off-by: Thien Nguyen * Code format Signed-off-by: Thien Nguyen --------- Signed-off-by: Thien Nguyen --- runtime/nvqir/cutensornet/CMakeLists.txt | 4 +- .../cutensornet/mps_simulation_state.cpp | 38 +++++++------- .../nvqir/cutensornet/mps_simulation_state.h | 7 ++- .../cutensornet/simulator_cutensornet.cpp | 4 +- .../nvqir/cutensornet/simulator_cutensornet.h | 6 +-- .../cutensornet/simulator_mps_register.cpp | 35 ++++++------- .../simulator_tensornet_register.cpp | 18 +++---- runtime/nvqir/cutensornet/tensornet_state.cpp | 49 ++++++++++++------- runtime/nvqir/cutensornet/tensornet_state.h | 21 +++++--- .../nvqir/cutensornet/tn_simulation_state.cpp | 12 ++--- .../nvqir/cutensornet/tn_simulation_state.h | 4 +- unittests/integration/ghz_nisq_tester.cpp | 4 -- 12 files changed, 113 insertions(+), 89 deletions(-) diff --git a/runtime/nvqir/cutensornet/CMakeLists.txt b/runtime/nvqir/cutensornet/CMakeLists.txt index 5537a3b053..86ec08cc75 100644 --- a/runtime/nvqir/cutensornet/CMakeLists.txt +++ b/runtime/nvqir/cutensornet/CMakeLists.txt @@ -60,8 +60,8 @@ set(CUTENSORNET_PATCH ${CMAKE_MATCH_1}) set(CUTENSORNET_VERSION ${CUTENSORNET_MAJOR}.${CUTENSORNET_MINOR}.${CUTENSORNET_PATCH}) message(STATUS "Found cutensornet version: ${CUTENSORNET_VERSION}") -# We need cutensornet v2.3+ -if (${CUTENSORNET_VERSION} VERSION_GREATER_EQUAL "2.3") +# We need cutensornet v2.5.0+ +if (${CUTENSORNET_VERSION} VERSION_GREATER_EQUAL "2.5") set (BASE_TENSOR_BACKEND_SRS simulator_cutensornet.cpp tensornet_spin_op.cpp diff --git a/runtime/nvqir/cutensornet/mps_simulation_state.cpp b/runtime/nvqir/cutensornet/mps_simulation_state.cpp index 284239461c..00bb45bf6a 100644 --- a/runtime/nvqir/cutensornet/mps_simulation_state.cpp +++ b/runtime/nvqir/cutensornet/mps_simulation_state.cpp @@ -26,9 +26,11 @@ std::size_t MPSSimulationState::getNumQubits() const { MPSSimulationState::MPSSimulationState(std::unique_ptr inState, const std::vector &mpsTensors, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t cutnHandle) + cutensornetHandle_t cutnHandle, + std::mt19937 &randomEngine) : m_cutnHandle(cutnHandle), state(std::move(inState)), - m_mpsTensors(mpsTensors), scratchPad(inScratchPad) {} + m_mpsTensors(mpsTensors), scratchPad(inScratchPad), + m_randomEngine(randomEngine) {} MPSSimulationState::~MPSSimulationState() { deallocate(); } @@ -266,8 +268,8 @@ MPSSimulationState::getAmplitude(const std::vector &basisState) { } if (getNumQubits() > 1) { - TensorNetState basisTensorNetState(basisState, scratchPad, - state->getInternalContext()); + TensorNetState basisTensorNetState( + basisState, scratchPad, state->getInternalContext(), m_randomEngine); // Note: this is a basis state, hence bond dim == 1 std::vector basisStateTensors = basisTensorNetState.factorizeMPS( 1, std::numeric_limits::min(), @@ -369,7 +371,8 @@ static Eigen::MatrixXcd reshapeStateVec(const Eigen::VectorXcd &stateVec) { MPSSimulationState::MpsStateData MPSSimulationState::createFromStateVec( cutensornetHandle_t cutnHandle, ScratchDeviceMem &inScratchPad, - std::size_t size, std::complex *ptr, int bondDim) { + std::size_t size, std::complex *ptr, int bondDim, + std::mt19937 &randomEngine) { const std::size_t numQubits = std::log2(size); // Reverse the qubit order to match cutensornet convention auto newStateVec = TensorNetState::reverseQubitOrder( @@ -386,8 +389,8 @@ MPSSimulationState::MpsStateData MPSSimulationState::createFromStateVec( MPSTensor stateTensor; stateTensor.deviceData = d_tensor; stateTensor.extents = std::vector{2}; - auto state = TensorNetState::createFromMpsTensors({stateTensor}, - inScratchPad, cutnHandle); + auto state = TensorNetState::createFromMpsTensors( + {stateTensor}, inScratchPad, cutnHandle, randomEngine); return {std::move(state), std::vector{stateTensor}}; } @@ -460,7 +463,7 @@ MPSSimulationState::MpsStateData MPSSimulationState::createFromStateVec( mpsTensors.emplace_back(stateTensor); assert(mpsTensors.size() == numQubits); auto state = TensorNetState::createFromMpsTensors(mpsTensors, inScratchPad, - cutnHandle); + cutnHandle, randomEngine); return {std::move(state), mpsTensors}; } @@ -486,16 +489,17 @@ MPSSimulationState::createFromSizeAndPtr(std::size_t size, void *ptr, MPSTensor stateTensor{d_tensor, mpsExtents}; mpsTensors.emplace_back(stateTensor); } - auto state = TensorNetState::createFromMpsTensors(mpsTensors, scratchPad, - m_cutnHandle); - return std::make_unique(std::move(state), mpsTensors, - scratchPad, m_cutnHandle); + auto state = TensorNetState::createFromMpsTensors( + mpsTensors, scratchPad, m_cutnHandle, m_randomEngine); + return std::make_unique( + std::move(state), mpsTensors, scratchPad, m_cutnHandle, m_randomEngine); } - auto [state, mpsTensors] = createFromStateVec( - m_cutnHandle, scratchPad, size, - reinterpret_cast *>(ptr), MPSSettings().maxBond); - return std::make_unique(std::move(state), mpsTensors, - scratchPad, m_cutnHandle); + auto [state, mpsTensors] = + createFromStateVec(m_cutnHandle, scratchPad, size, + reinterpret_cast *>(ptr), + MPSSettings().maxBond, m_randomEngine); + return std::make_unique( + std::move(state), mpsTensors, scratchPad, m_cutnHandle, m_randomEngine); } MPSSettings::MPSSettings() { diff --git a/runtime/nvqir/cutensornet/mps_simulation_state.h b/runtime/nvqir/cutensornet/mps_simulation_state.h index 95f10ee978..3c5b9d9fd6 100644 --- a/runtime/nvqir/cutensornet/mps_simulation_state.h +++ b/runtime/nvqir/cutensornet/mps_simulation_state.h @@ -35,7 +35,8 @@ class MPSSimulationState : public cudaq::SimulationState { MPSSimulationState(std::unique_ptr inState, const std::vector &mpsTensors, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t cutnHandle); + cutensornetHandle_t cutnHandle, + std::mt19937 &randomEngine); MPSSimulationState(const MPSSimulationState &) = delete; MPSSimulationState &operator=(const MPSSimulationState &) = delete; @@ -84,7 +85,8 @@ class MPSSimulationState : public cudaq::SimulationState { ScratchDeviceMem &inScratchPad, std::size_t size, std::complex *data, - int bondDim); + int bondDim, + std::mt19937 &randomEngine); /// Retrieve the MPS tensors std::vector getMpsTensors() const { return m_mpsTensors; } @@ -105,6 +107,7 @@ class MPSSimulationState : public cudaq::SimulationState { // This speeds up sequential state amplitude accessors for small states. static constexpr std::size_t g_maxQubitsForStateContraction = 30; std::vector> m_contractedStateVec; + std::mt19937 &m_randomEngine; }; } // namespace nvqir \ No newline at end of file diff --git a/runtime/nvqir/cutensornet/simulator_cutensornet.cpp b/runtime/nvqir/cutensornet/simulator_cutensornet.cpp index 3519e07d2c..a251f555c8 100644 --- a/runtime/nvqir/cutensornet/simulator_cutensornet.cpp +++ b/runtime/nvqir/cutensornet/simulator_cutensornet.cpp @@ -307,8 +307,8 @@ void SimulatorTensorNetBase::setToZeroState() { const auto numQubits = m_state->getNumQubits(); m_state.reset(); // Re-create a zero state of the same size - m_state = - std::make_unique(numQubits, scratchPad, m_cutnHandle); + m_state = std::make_unique(numQubits, scratchPad, + m_cutnHandle, m_randomEngine); } void SimulatorTensorNetBase::swap(const std::vector &ctrlBits, diff --git a/runtime/nvqir/cutensornet/simulator_cutensornet.h b/runtime/nvqir/cutensornet/simulator_cutensornet.h index 5c57316de3..f662fa1fcc 100644 --- a/runtime/nvqir/cutensornet/simulator_cutensornet.h +++ b/runtime/nvqir/cutensornet/simulator_cutensornet.h @@ -93,10 +93,8 @@ class SimulatorTensorNetBase : public nvqir::CircuitSimulatorBase { std::unique_ptr m_state; std::unordered_map m_gateDeviceMemCache; ScratchDeviceMem scratchPad; - // Note: cutensornet sample API uses an internal random engine that doesn't - // support random seed. This engine only affects the mid-circuit measurements - // whereby this simulator generates a random probability value. - // See also: https://github.com/NVIDIA/cuda-quantum/issues/895 + // Random number generator for generating 32-bit numbers with a state size of + // 19937 bits for measurements. std::mt19937 m_randomEngine; }; diff --git a/runtime/nvqir/cutensornet/simulator_mps_register.cpp b/runtime/nvqir/cutensornet/simulator_mps_register.cpp index dd73aa8bf7..1218667e47 100644 --- a/runtime/nvqir/cutensornet/simulator_mps_register.cpp +++ b/runtime/nvqir/cutensornet/simulator_mps_register.cpp @@ -47,8 +47,8 @@ class SimulatorMPS : public SimulatorTensorNetBase { throw std::invalid_argument( "[SimulatorMPS simulator] Incompatible state input"); if (!m_state) { - m_state = TensorNetState::createFromMpsTensors(casted->getMpsTensors(), - scratchPad, m_cutnHandle); + m_state = TensorNetState::createFromMpsTensors( + casted->getMpsTensors(), scratchPad, m_cutnHandle, m_randomEngine); } else { // Expand an existing state: Append MPS tensors // Factor the existing state @@ -76,8 +76,8 @@ class SimulatorMPS : public SimulatorTensorNetBase { tensorSizeBytes, cudaMemcpyDefault)); tensors.emplace_back(MPSTensor(mpsTensor, extents)); } - m_state = TensorNetState::createFromMpsTensors(tensors, scratchPad, - m_cutnHandle); + m_state = TensorNetState::createFromMpsTensors( + tensors, scratchPad, m_cutnHandle, m_randomEngine); } } @@ -164,13 +164,13 @@ class SimulatorMPS : public SimulatorTensorNetBase { LOG_API_TIME(); if (!m_state) { if (!ptr) { - m_state = std::make_unique(numQubits, scratchPad, - m_cutnHandle); + m_state = std::make_unique( + numQubits, scratchPad, m_cutnHandle, m_randomEngine); } else { auto [state, mpsTensors] = MPSSimulationState::createFromStateVec( m_cutnHandle, scratchPad, 1ULL << numQubits, reinterpret_cast *>(const_cast(ptr)), - m_settings.maxBond); + m_settings.maxBond, m_randomEngine); m_state = std::move(state); } } else { @@ -194,14 +194,14 @@ class SimulatorMPS : public SimulatorTensorNetBase { cudaMemcpyHostToDevice)); tensors.emplace_back(MPSTensor(mpsTensor, extents)); } - m_state = TensorNetState::createFromMpsTensors(tensors, scratchPad, - m_cutnHandle); + m_state = TensorNetState::createFromMpsTensors( + tensors, scratchPad, m_cutnHandle, m_randomEngine); } else { // Non-zero state needs to be factorized and appended. auto [state, mpsTensors] = MPSSimulationState::createFromStateVec( m_cutnHandle, scratchPad, 1ULL << numQubits, reinterpret_cast *>(const_cast(ptr)), - m_settings.maxBond); + m_settings.maxBond, m_randomEngine); auto tensors = m_state->factorizeMPS(m_settings.maxBond, m_settings.absCutoff, m_settings.relCutoff, m_settings.svdAlgo); @@ -214,8 +214,8 @@ class SimulatorMPS : public SimulatorTensorNetBase { mpsTensors.front().extents = extents; // Combine the list tensors.insert(tensors.end(), mpsTensors.begin(), mpsTensors.end()); - m_state = TensorNetState::createFromMpsTensors(tensors, scratchPad, - m_cutnHandle); + m_state = TensorNetState::createFromMpsTensors( + tensors, scratchPad, m_cutnHandle, m_randomEngine); } } } @@ -224,16 +224,17 @@ class SimulatorMPS : public SimulatorTensorNetBase { LOG_API_TIME(); if (!m_state || m_state->getNumQubits() == 0) - return std::make_unique(std::move(m_state), - std::vector{}, - scratchPad, m_cutnHandle); + return std::make_unique( + std::move(m_state), std::vector{}, scratchPad, + m_cutnHandle, m_randomEngine); if (m_state->getNumQubits() > 1) { std::vector tensors = m_state->factorizeMPS(m_settings.maxBond, m_settings.absCutoff, m_settings.relCutoff, m_settings.svdAlgo); return std::make_unique(std::move(m_state), tensors, - scratchPad, m_cutnHandle); + scratchPad, m_cutnHandle, + m_randomEngine); } auto [d_tensor, numElements] = m_state->contractStateVectorInternal({}); @@ -244,7 +245,7 @@ class SimulatorMPS : public SimulatorTensorNetBase { return std::make_unique( std::move(m_state), std::vector{stateTensor}, scratchPad, - m_cutnHandle); + m_cutnHandle, m_randomEngine); } virtual ~SimulatorMPS() noexcept { diff --git a/runtime/nvqir/cutensornet/simulator_tensornet_register.cpp b/runtime/nvqir/cutensornet/simulator_tensornet_register.cpp index 5998f99ef7..3aa3bad831 100644 --- a/runtime/nvqir/cutensornet/simulator_tensornet_register.cpp +++ b/runtime/nvqir/cutensornet/simulator_tensornet_register.cpp @@ -47,22 +47,22 @@ class SimulatorTensorNet : public SimulatorTensorNetBase { std::unique_ptr getSimulationState() override { LOG_API_TIME(); - return std::make_unique(std::move(m_state), - scratchPad, m_cutnHandle); + return std::make_unique( + std::move(m_state), scratchPad, m_cutnHandle, m_randomEngine); } void addQubitsToState(std::size_t numQubits, const void *ptr) override { LOG_API_TIME(); if (!m_state) { if (!ptr) { - m_state = std::make_unique(numQubits, scratchPad, - m_cutnHandle); + m_state = std::make_unique( + numQubits, scratchPad, m_cutnHandle, m_randomEngine); } else { auto *casted = reinterpret_cast *>(const_cast(ptr)); std::span> stateVec(casted, 1ULL << numQubits); - m_state = TensorNetState::createFromStateVector(stateVec, scratchPad, - m_cutnHandle); + m_state = TensorNetState::createFromStateVector( + stateVec, scratchPad, m_cutnHandle, m_randomEngine); } } else { if (!ptr) { @@ -85,9 +85,9 @@ class SimulatorTensorNet : public SimulatorTensorNetBase { throw std::invalid_argument( "[Tensornet simulator] Incompatible state input"); if (!m_state) { - m_state = TensorNetState::createFromOpTensors(in_state.getNumQubits(), - casted->getAppliedTensors(), - scratchPad, m_cutnHandle); + m_state = TensorNetState::createFromOpTensors( + in_state.getNumQubits(), casted->getAppliedTensors(), scratchPad, + m_cutnHandle, m_randomEngine); } else { // Expand an existing state: // (1) Create a blank tensor network with combined number of qubits diff --git a/runtime/nvqir/cutensornet/tensornet_state.cpp b/runtime/nvqir/cutensornet/tensornet_state.cpp index 32c242963e..ef2dc40e05 100644 --- a/runtime/nvqir/cutensornet/tensornet_state.cpp +++ b/runtime/nvqir/cutensornet/tensornet_state.cpp @@ -15,8 +15,10 @@ namespace nvqir { TensorNetState::TensorNetState(std::size_t numQubits, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle) - : m_numQubits(numQubits), m_cutnHandle(handle), scratchPad(inScratchPad) { + cutensornetHandle_t handle, + std::mt19937 &randomEngine) + : m_numQubits(numQubits), m_cutnHandle(handle), scratchPad(inScratchPad), + m_randomEngine(randomEngine) { const std::vector qubitDims(m_numQubits, 2); HANDLE_CUTN_ERROR(cutensornetCreateState( m_cutnHandle, CUTENSORNET_STATE_PURITY_PURE, m_numQubits, @@ -25,8 +27,9 @@ TensorNetState::TensorNetState(std::size_t numQubits, TensorNetState::TensorNetState(const std::vector &basisState, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle) - : TensorNetState(basisState.size(), inScratchPad, handle) { + cutensornetHandle_t handle, + std::mt19937 &randomEngine) + : TensorNetState(basisState.size(), inScratchPad, handle, randomEngine) { constexpr std::complex h_xGate[4] = {0.0, 1.0, 1.0, 0.0}; constexpr auto sizeBytes = 4 * sizeof(std::complex); void *d_gate{nullptr}; @@ -43,8 +46,8 @@ TensorNetState::TensorNetState(const std::vector &basisState, } std::unique_ptr TensorNetState::clone() const { - return createFromOpTensors(m_numQubits, m_tensorOps, scratchPad, - m_cutnHandle); + return createFromOpTensors(m_numQubits, m_tensorOps, scratchPad, m_cutnHandle, + m_randomEngine); } void TensorNetState::applyGate(const std::vector &controlQubits, @@ -166,6 +169,16 @@ TensorNetState::sample(const std::vector &measuredBitIds, HANDLE_CUTN_ERROR(cutensornetSamplerConfigure( m_cutnHandle, sampler, CUTENSORNET_SAMPLER_OPT_NUM_HYPER_SAMPLES, &numHyperSamples, sizeof(numHyperSamples))); + + // Generate a random seed from the backend simulator's random engine. + // Note: Even after a random seed setting at the user's level, + // consecutive `cudaq::sample` calls will still return different results + // (yet deterministic), i.e., the seed that we send to cutensornet should + // not be the user's seed. + const int32_t rndSeed = m_randomEngine(); + HANDLE_CUTN_ERROR(cutensornetSamplerConfigure( + m_cutnHandle, sampler, CUTENSORNET_SAMPLER_CONFIG_DETERMINISTIC, + &rndSeed, sizeof(rndSeed))); } // Prepare the quantum circuit sampler @@ -687,12 +700,12 @@ std::vector> TensorNetState::computeExpVals( std::unique_ptr TensorNetState::createFromMpsTensors( const std::vector &in_mpsTensors, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle) { + cutensornetHandle_t handle, std::mt19937 &randomEngine) { LOG_API_TIME(); if (in_mpsTensors.empty()) throw std::invalid_argument("Empty MPS tensor list"); - auto state = std::make_unique(in_mpsTensors.size(), - inScratchPad, handle); + auto state = std::make_unique( + in_mpsTensors.size(), inScratchPad, handle, randomEngine); std::vector extents; std::vector tensorData; for (const auto &tensor : in_mpsTensors) { @@ -709,10 +722,11 @@ std::unique_ptr TensorNetState::createFromMpsTensors( /// operators. std::unique_ptr TensorNetState::createFromOpTensors( std::size_t numQubits, const std::vector &opTensors, - ScratchDeviceMem &inScratchPad, cutensornetHandle_t handle) { + ScratchDeviceMem &inScratchPad, cutensornetHandle_t handle, + std::mt19937 &randomEngine) { LOG_API_TIME(); - auto state = - std::make_unique(numQubits, inScratchPad, handle); + auto state = std::make_unique(numQubits, inScratchPad, handle, + randomEngine); for (const auto &op : opTensors) if (op.isUnitary) state->applyGate(op.controlQubitIds, op.targetQubitIds, op.deviceData, @@ -737,14 +751,13 @@ TensorNetState::reverseQubitOrder(std::span> stateVec) { return ket; } -std::unique_ptr -TensorNetState::createFromStateVector(std::span> stateVec, - ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle) { +std::unique_ptr TensorNetState::createFromStateVector( + std::span> stateVec, ScratchDeviceMem &inScratchPad, + cutensornetHandle_t handle, std::mt19937 &randomEngine) { LOG_API_TIME(); const std::size_t numQubits = std::log2(stateVec.size()); - auto state = - std::make_unique(numQubits, inScratchPad, handle); + auto state = std::make_unique(numQubits, inScratchPad, handle, + randomEngine); // Support initializing the tensor network in a specific state vector state. // Note: this is not intended for large state vector but for relatively small diff --git a/runtime/nvqir/cutensornet/tensornet_state.h b/runtime/nvqir/cutensornet/tensornet_state.h index 3ee74328da..e3057e652a 100644 --- a/runtime/nvqir/cutensornet/tensornet_state.h +++ b/runtime/nvqir/cutensornet/tensornet_state.h @@ -52,15 +52,20 @@ class TensorNetState { // Tensor ops that have been applied to the state. std::vector m_tensorOps; ScratchDeviceMem &scratchPad; + // Random number generator measurement sampling. + // This is a reference to the backend random number generator, which can be + // reseeded by users. + std::mt19937 &m_randomEngine; public: /// @brief Constructor TensorNetState(std::size_t numQubits, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle); + cutensornetHandle_t handle, std::mt19937 &randomEngine); /// @brief Constructor (specific basis state) TensorNetState(const std::vector &basisState, - ScratchDeviceMem &inScratchPad, cutensornetHandle_t handle); + ScratchDeviceMem &inScratchPad, cutensornetHandle_t handle, + std::mt19937 &randomEngine); std::unique_ptr clone() const; @@ -68,13 +73,15 @@ class TensorNetState { static std::unique_ptr createFromMpsTensors(const std::vector &mpsTensors, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle); + cutensornetHandle_t handle, std::mt19937 &randomEngine); /// Reconstruct/initialize a tensor network state from a list of tensor /// operators. - static std::unique_ptr createFromOpTensors( - std::size_t numQubits, const std::vector &opTensors, - ScratchDeviceMem &inScratchPad, cutensornetHandle_t handle); + static std::unique_ptr + createFromOpTensors(std::size_t numQubits, + const std::vector &opTensors, + ScratchDeviceMem &inScratchPad, + cutensornetHandle_t handle, std::mt19937 &randomEngine); // Create a tensor network state from the input state vector. // Note: this is not the most efficient mode of initialization. However, this @@ -83,7 +90,7 @@ class TensorNetState { static std::unique_ptr createFromStateVector(std::span> stateVec, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t handle); + cutensornetHandle_t handle, std::mt19937 &randomEngine); /// @brief Apply a unitary gate /// @param controlQubits Controlled qubit operands diff --git a/runtime/nvqir/cutensornet/tn_simulation_state.cpp b/runtime/nvqir/cutensornet/tn_simulation_state.cpp index 3a6f09ebd7..74f1526e8e 100644 --- a/runtime/nvqir/cutensornet/tn_simulation_state.cpp +++ b/runtime/nvqir/cutensornet/tn_simulation_state.cpp @@ -22,9 +22,9 @@ std::size_t TensorNetSimulationState::getNumQubits() const { TensorNetSimulationState::TensorNetSimulationState( std::unique_ptr inState, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t cutnHandle) + cutensornetHandle_t cutnHandle, std::mt19937 &randomEngine) : m_state(std::move(inState)), scratchPad(inScratchPad), - m_cutnHandle(cutnHandle) {} + m_cutnHandle(cutnHandle), m_randomEngine(randomEngine) {} TensorNetSimulationState::~TensorNetSimulationState() {} @@ -247,11 +247,11 @@ TensorNetSimulationState::createFromSizeAndPtr(std::size_t size, void *ptr, std::vector> vec( reinterpret_cast *>(ptr), reinterpret_cast *>(ptr) + size); - auto tensorNetState = - TensorNetState::createFromStateVector(vec, scratchPad, m_cutnHandle); + auto tensorNetState = TensorNetState::createFromStateVector( + vec, scratchPad, m_cutnHandle, m_randomEngine); - return std::make_unique(std::move(tensorNetState), - scratchPad, m_cutnHandle); + return std::make_unique( + std::move(tensorNetState), scratchPad, m_cutnHandle, m_randomEngine); } void TensorNetSimulationState::destroyState() { diff --git a/runtime/nvqir/cutensornet/tn_simulation_state.h b/runtime/nvqir/cutensornet/tn_simulation_state.h index ea08cc10dc..e66512f902 100644 --- a/runtime/nvqir/cutensornet/tn_simulation_state.h +++ b/runtime/nvqir/cutensornet/tn_simulation_state.h @@ -23,7 +23,8 @@ class TensorNetSimulationState : public cudaq::SimulationState { public: TensorNetSimulationState(std::unique_ptr inState, ScratchDeviceMem &inScratchPad, - cutensornetHandle_t cutnHandle); + cutensornetHandle_t cutnHandle, + std::mt19937 &randomEngine); TensorNetSimulationState(const TensorNetSimulationState &) = delete; TensorNetSimulationState & @@ -78,5 +79,6 @@ class TensorNetSimulationState : public cudaq::SimulationState { // This speeds up sequential state amplitude accessors for small states. static constexpr std::size_t g_maxQubitsForStateContraction = 30; std::vector> m_contractedStateVec; + std::mt19937 &m_randomEngine; }; } // namespace nvqir \ No newline at end of file diff --git a/unittests/integration/ghz_nisq_tester.cpp b/unittests/integration/ghz_nisq_tester.cpp index 004b9a4c1f..ab19e3deb8 100644 --- a/unittests/integration/ghz_nisq_tester.cpp +++ b/unittests/integration/ghz_nisq_tester.cpp @@ -94,9 +94,6 @@ CUDAQ_TEST(GHZSampleTester, checkBroadcast) { } } -// Enable once https://github.com/NVIDIA/cuda-quantum/issues/895 is fixed. -#ifndef CUDAQ_BACKEND_TENSORNET -// Tensornet backend doesn't support seed. CUDAQ_TEST(GHZSampleTester, checkBroadcastRepeatability) { std::vector sizeVals(8); std::iota(sizeVals.begin(), sizeVals.end(), 3); @@ -113,4 +110,3 @@ CUDAQ_TEST(GHZSampleTester, checkBroadcastRepeatability) { EXPECT_EQ(allCounts1, allCounts2); // these should match EXPECT_NE(allCounts1, allCounts3); // these should NOT match } -#endif