Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh remote seeds for executions within a client session #1318

Merged
merged 3 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions python/tests/remote/test_remote_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,41 @@ def parameterized_ansatz(theta: float):
check_multi_qpus(parameterized_ansatz)


# Check randomness and repeatability by setting the seed value
def test_seed():
bmhowe23 marked this conversation as resolved.
Show resolved Hide resolved
kernel = cudaq.make_kernel()
qubit = kernel.qalloc()
kernel.h(qubit)
kernel.mz(qubit)
# Set the runtime seed
cudaq.set_random_seed(123)
# First check: different executions after setting the seed value can produce randomized results.
# We don't expect to run these number of tests,
# the first time we encounter a different distribution, it will terminate.
max_num_tests = 100
zero_counts = []
found_different_result = False
for i in range(max_num_tests):
count = cudaq.sample(kernel)
zero_counts.append(count["0"])
for x in zero_counts:
if x != zero_counts[0]:
found_different_result = True
break
if found_different_result:
# Found a different distribution.
# No need to run any more simulation.
break
assert (found_different_result)
# Now, reset the seed
# Check that the new sequence of distributions exactly matches the prior.
cudaq.set_random_seed(123)
for i in range(len(zero_counts)):
# Rerun sampling
count = cudaq.sample(kernel)
assert (count["0"] == zero_counts[i])


# leave for gdb debugging
if __name__ == "__main__":
loc = os.path.abspath(__file__)
Expand Down
4 changes: 4 additions & 0 deletions runtime/common/BaseRemoteSimulatorQPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,9 @@ class BaseRemoteSimulatorQPU : public cudaq::QPU {
std::scoped_lock<std::mutex> lock(m_contextMutex);
m_contexts.erase(std::this_thread::get_id());
}

void onRandomSeedSet(std::size_t seed) override {
m_client->resetRemoteRandomSeed(seed);
}
};
} // namespace cudaq
24 changes: 23 additions & 1 deletion runtime/common/BaseRestRemoteClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <dlfcn.h>
#include <fstream>
#include <iostream>
#include <limits>
#include <streambuf>

namespace {
Expand Down Expand Up @@ -98,6 +99,8 @@ class BaseRemoteRestRuntimeClient : public cudaq::RemoteRuntimeClient {
"cse",
"quake-to-qir"};
static inline const std::vector<std::string> serverPasses = {};
// Random number generator.
std::mt19937 randEngine{std::random_device{}()};

public:
virtual void setConfig(
Expand Down Expand Up @@ -225,7 +228,21 @@ class BaseRemoteRestRuntimeClient : public cudaq::RemoteRuntimeClient {
request.code = constructKernelPayload(mlirContext, kernelName, kernelFunc,
kernelArgs, argsSize);
request.simulator = backendSimName;
request.seed = cudaq::get_random_seed();
// Remote server seed
// Note: unlike local executions whereby a static instance of the simulator
// is seeded once when `cudaq::set_random_seed` is called, thus not being
// re-seeded between executions. For remote executions, we use the runtime
// level seed value to seed a random number generator to seed the server.
// i.e., consecutive remote executions on the server from the same client
// session (where `cudaq::set_random_seed` is called), get new random seeds
// for each execution. The sequence is still deterministic based on the
// runtime-level seed value.
request.seed = [&]() {
std::uniform_int_distribution<std::size_t> seedGen(
std::numeric_limits<std::size_t>::min(),
std::numeric_limits<std::size_t>::max());
return seedGen(randEngine);
}();
return request;
}

Expand Down Expand Up @@ -282,5 +299,10 @@ class BaseRemoteRestRuntimeClient : public cudaq::RemoteRuntimeClient {
return false;
}
}

virtual void resetRemoteRandomSeed(std::size_t seed) override {
// Re-seed the generator, e.g., when `cudaq::set_random_seed` is called.
randEngine.seed(seed);
}
};
} // namespace cudaq
4 changes: 4 additions & 0 deletions runtime/common/RemoteKernelExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class RemoteRuntimeClient
virtual void
setConfig(const std::unordered_map<std::string, std::string> &configs) = 0;

// Reset the random seed sequence using for remote execution.
// This is triggered by a random seed value being set in CUDAQ runtime.
virtual void resetRemoteRandomSeed(std::size_t seed) = 0;

// Delegate/send kernel execution to a remote server.
// Subclass will implement necessary transport-layer serialization and
// communication protocols. The `ExecutionContext` will be updated in-place as
Expand Down
3 changes: 3 additions & 0 deletions runtime/cudaq/cudaq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ thread_local static std::size_t cudaq_random_seed = 0;
void set_random_seed(std::size_t seed) {
cudaq_random_seed = seed;
nvqir::setRandomSeed(seed);
auto &platform = cudaq::get_platform();
// Notify the platform that a new random seed value is set.
platform.onRandomSeedSet(seed);
}

std::size_t get_random_seed() { return cudaq_random_seed; }
Expand Down
1 change: 1 addition & 0 deletions runtime/cudaq/platform/mqpu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ target_link_libraries(${LIBRARY_NAME}
cudaq-spin
cudaq-common
PRIVATE
cudaq
mqpu_util
pthread
spdlog::spdlog
Expand Down
4 changes: 4 additions & 0 deletions runtime/cudaq/platform/qpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,5 +147,9 @@ class QPU : public registry::RegisteredType<QPU> {
/// as a struct-packed void pointer and its corresponding size.
virtual void launchKernel(const std::string &name, void (*kernelFunc)(void *),
void *args, std::uint64_t, std::uint64_t) = 0;

/// @brief Notify the QPU that a new random seed value is set.
/// By default do nothing, let subclasses override.
virtual void onRandomSeedSet(std::size_t seed) {}
};
} // namespace cudaq
5 changes: 5 additions & 0 deletions runtime/cudaq/platform/quantum_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ void quantum_platform::launchKernel(std::string kernelName,
qpu->launchKernel(kernelName, kernelFunc, args, voidStarSize, resultOffset);
}

void quantum_platform::onRandomSeedSet(std::size_t seed) {
// Send on the notification to all QPUs.
for (auto &qpu : platformQPUs)
qpu->onRandomSeedSet(seed);
}
} // namespace cudaq

void cudaq::altLaunchKernel(const char *kernelName, void (*kernelFunc)(void *),
Expand Down
4 changes: 4 additions & 0 deletions runtime/cudaq/platform/quantum_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ class quantum_platform {
/// @param name
virtual void setTargetBackend(const std::string &name) {}

/// @brief Called by the runtime to notify that a new random seed value is
/// set.
virtual void onRandomSeedSet(std::size_t seed);

protected:
/// The Platform QPUs, populated by concrete subtypes
std::vector<std::unique_ptr<QPU>> platformQPUs;
Expand Down
Loading