Skip to content
Open
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
38 changes: 27 additions & 11 deletions quest/src/api/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@ void printQuregAutoDeployments(bool isDensMatr) {
prevGpuAccel = 0;
prevMulti = 0;

// assume 1 qubit is deployable, so that an undeployable first row is still reported
bool prevCanDeploy = true;

// test to theoretically max #qubits, surpassing max that can fit in RAM and GPUs, because
// auto-deploy will still try to deploy there to (then subsequent validation will fail)
int maxQubits = mem_getMaxNumQuregQubitsBeforeGlobalMemSizeofOverflow(isDensMatr, globalEnvPtr->numNodes);
Expand All @@ -350,22 +353,34 @@ void printQuregAutoDeployments(bool isDensMatr) {
useDistrib = modeflag::USE_AUTO;
useGpuAccel = modeflag::USE_AUTO;
useMulti = modeflag::USE_AUTO;;
autodep_chooseQuregDeployment(numQubits, isDensMatr, useDistrib, useGpuAccel, useMulti, *globalEnvPtr);

// skip if deployments are unchanged
if (useDistrib == prevDistrib &&
// mustUtiliseAllNodes=false: this merely queries sizes, so must never abort upon
// those which createQureg() would reject; we report them as undeployable below
autodep_chooseQuregDeployment(numQubits, isDensMatr, useDistrib, useGpuAccel, useMulti, *globalEnvPtr, false, __func__);

// createQureg() rejects auto-deployments which would replicate the Qureg between nodes
bool canDeploy = (globalEnvPtr->numNodes == 1) || useDistrib;

// skip if deployments are unchanged, or remain unavailable (the other deployments
// still vary while unavailable, but are not reported, so must not open a new row)
if (!canDeploy && !prevCanDeploy)
continue;
if (canDeploy == prevCanDeploy &&
useDistrib == prevDistrib &&
useGpuAccel == prevGpuAccel &&
useMulti == prevMulti)
continue;

// else prepare string summarising the new deployments (trailing space is fine)
string value = "";
if (useMulti)
value += "[omp] "; // ordered by #qubits to attempt consistent printed columns
if (useGpuAccel)
value += "[gpu] ";
if (useDistrib)
value += "[mpi] ";
string value = "(no automatic deployment available)";
if (canDeploy) {
value = "";
if (useMulti)
value += "[omp] "; // ordered by #qubits to attempt consistent printed columns
if (useGpuAccel)
value += "[gpu] ";
if (useDistrib)
value += "[mpi] ";
}

// log the #qubits of the deployment change
rows.push_back({printer_toStr(numQubits) + " qubits", value});
Expand All @@ -374,6 +389,7 @@ void printQuregAutoDeployments(bool isDensMatr) {
prevDistrib = useDistrib;
prevGpuAccel = useGpuAccel;
prevMulti = useMulti;
prevCanDeploy = canDeploy;
}

// tailor table title to type of Qureg
Expand Down
2 changes: 1 addition & 1 deletion quest/src/api/qureg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Qureg validateAndCreateCustomQureg(int numQubits, int isDensMatr, int useDistrib
validate_newQuregParams(numQubits, isDensMatr, useDistrib, useGpuAccel, useMultithread, env, caller);

// automatically overwrite distrib, GPU, and multithread fields which were left as modeflag::USE_AUTO
autodep_chooseQuregDeployment(numQubits, isDensMatr, useDistrib, useGpuAccel, useMultithread, env);
autodep_chooseQuregDeployment(numQubits, isDensMatr, useDistrib, useGpuAccel, useMultithread, env, true, caller);

Qureg qureg = qureg_populateNonHeapFields(numQubits, isDensMatr, useDistrib, useGpuAccel, useMultithread);

Expand Down
57 changes: 55 additions & 2 deletions quest/src/core/autodeployer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "quest/include/environment.h"

#include "quest/src/core/memory.hpp"
#include "quest/src/core/validation.hpp"
#include "quest/src/core/autodeployer.hpp"
#include "quest/src/comm/comm_config.hpp"
#include "quest/src/cpu/cpu_config.hpp"
Expand Down Expand Up @@ -87,6 +88,44 @@ void chooseWhetherToDistributeQureg(int numQubits, int isDensMatr, int &useDistr
}


void assertAutoDeploymentIsDistributed(int numQubits, int isDensMatr, int numEnvNodes, int useDistrib, const char* caller) {

bool dividesEvenly = (numQubits >= mem_getMinNumQubitsForDistribution(numEnvNodes));

static const string indivisibleMsg =
"Automatic deployment cannot distribute this ${NUM_QUBITS} qubit state between the "
"environment's ${NUM_NODES} nodes, because it cannot be divided evenly between them; that "
"requires at least ${MIN_NODE_QUBITS} qubits. Every node would instead redundantly simulate "
"the entire state. Specify the deployment explicitly in order to deliberately forgo "
"distribution, or launch the environment with fewer nodes.";

static const string undersizedMsg =
"Automatic deployment cannot distribute this ${NUM_QUBITS} qubit state between the "
"environment's ${NUM_NODES} nodes, because each node would then store only 2^${LOCAL_QUBITS} "
"amplitudes, fewer than the 2^${MIN_QUBITS} below which distribution is not automatically "
"chosen. Every node would instead redundantly simulate the entire state. Specify the "
"deployment explicitly in order to deliberately forgo distribution, or launch the "
"environment with fewer nodes.";

const string& msg = (dividesEvenly)? undersizedMsg : indivisibleMsg;

tokenSubs vars = (dividesEvenly)?
tokenSubs{
{"${NUM_QUBITS}", numQubits},
{"${NUM_NODES}", numEnvNodes},
{"${LOCAL_QUBITS}", mem_getEffectiveNumStateVecQubitsPerNode(numQubits, isDensMatr, numEnvNodes)},
{"${MIN_QUBITS}", MIN_NUM_LOCAL_QUBITS_FOR_AUTO_QUREG_DISTRIBUTION}} :
tokenSubs{
{"${NUM_QUBITS}", numQubits},
{"${NUM_NODES}", numEnvNodes},
{"${MIN_NODE_QUBITS}", mem_getMinNumQubitsForDistribution(numEnvNodes)}};

// nodes can disagree since deployment consulted their own RAM and VRAM; a lone
// failing node would hang the others inside the error handler's synchronisation
assertAllNodesAgreeThat(useDistrib == 1, msg, vars, caller);
}


void chooseWhetherToGpuAccelQureg(int numQubits, int isDensMatr, int &useGpuAccel, int numQuregNodes) {

// if the flag is already set, don't change it
Expand Down Expand Up @@ -121,12 +160,15 @@ void chooseWhetherToMultithreadQureg(int numQubits, int isDensMatr, int &useMult
}


void autodep_chooseQuregDeployment(int numQubits, int isDensMatr, int &useDistrib, int &useGpuAccel, int &useMultithread, QuESTEnv env) {
void autodep_chooseQuregDeployment(int numQubits, int isDensMatr, int &useDistrib, int &useGpuAccel, int &useMultithread, QuESTEnv env, bool mustUtiliseAllNodes, const char* caller) {

// preconditions:
// - the given configuration is compatible with env (assured by prior validation)
// - this means no deployment is forced (=1) which is incompatible with env

// record whether the user deferred distribution to us, since explicit replication is deliberate
bool wasAutoDistrib = (useDistrib == modeflag::USE_AUTO);

// disable any automatic deployments not permitted by env (it's gauranteed we never overwrite =1 to =0)
if (!env.isDistributed)
useDistrib = 0;
Expand All @@ -142,6 +184,13 @@ void autodep_chooseQuregDeployment(int numQubits, int isDensMatr, int &useDistri

// overwrite useDistrib
chooseWhetherToDistributeQureg(numQubits, isDensMatr, useDistrib, useGpuAccel, env.numNodes);

// an automatic Qureg deployment must make use of every node, else each would redundantly
// simulate the entire state; only an explicit deployment may forgo distribution. note every
// node reaches this call, so that the assertion within can seek consensus over useDistrib
if (mustUtiliseAllNodes && wasAutoDistrib && env.numNodes > 1)
assertAutoDeploymentIsDistributed(numQubits, isDensMatr, env.numNodes, useDistrib, caller);

int numQuregNodes = (useDistrib)? env.numNodes : 1;

// overwrite useGpuAccel
Expand Down Expand Up @@ -170,5 +219,9 @@ void autodep_chooseFullStateDiagMatrDeployment(int numQubits, int &useDistrib, i
// the FullStateDiagMatr is a statevector Qureg.
int isDensMatr = 0;

autodep_chooseQuregDeployment(numQubits, isDensMatr, useDistrib, useGpuAccel, useMultithread, env);
// unlike a Qureg, a replicated matrix wastes no nodes; it is merely a local copy of the
// diagonal which every node consults while distributedly modifying its own Qureg partition
bool mustUtiliseAllNodes = false;

autodep_chooseQuregDeployment(numQubits, isDensMatr, useDistrib, useGpuAccel, useMultithread, env, mustUtiliseAllNodes, __func__);
}
5 changes: 4 additions & 1 deletion quest/src/core/autodeployer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@

void autodep_chooseQuESTEnvDeployment(int &useDistrib, int &useGpuAccel, int &useMultithread);

void autodep_chooseQuregDeployment(int numQubits, int isDensMatr, int &useDistrib, int &useGpuAccel, int &useMultithread, QuESTEnv env);
// mustUtiliseAllNodes=true forbids automatically replicating the Qureg between the environment's
// nodes, which would leave each redundantly simulating the same state. it is false for
// reportQuESTEnv()'s enumeration, which merely queries every size rather than deploying them
void autodep_chooseQuregDeployment(int numQubits, int isDensMatr, int &useDistrib, int &useGpuAccel, int &useMultithread, QuESTEnv env, bool mustUtiliseAllNodes, const char* caller);

void autodep_chooseFullStateDiagMatrDeployment(int numQubits, int &useDistrib, int &useGpuAccel, int &useMultithread, QuESTEnv env);

Expand Down
14 changes: 14 additions & 0 deletions quest/src/core/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <vector>
#include <string>
#include <map>

using std::vector;
using std::string;
Expand All @@ -31,6 +32,19 @@ const int validate_STRUCT_PROPERTY_UNKNOWN_FLAG = -1;



/*
* REPORTING
*/

// map like "${X}" -> 5, with max-size signed int values to prevent overflows.
// in C++11, these can be initialised with {{"${X}", 5}, ...}
using tokenSubs = std::map<string, long long int>;

// exposed beyond validation.cpp so the autodeployer can raise its own errors
void assertAllNodesAgreeThat(bool valid, string msg, tokenSubs vars, const char* func);



/*
* VALIDATION ERROR HANDLER
*/
Expand Down