|
3 | 3 | * functions are satisfied, and otherwise throws a user-readable error.
|
4 | 4 | */
|
5 | 5 |
|
6 |
| -#include "quest/include/environment.h" |
7 | 6 | #include "quest/include/modes.h"
|
| 7 | +#include "quest/include/environment.h" |
| 8 | +#include "quest/include/qureg.h" |
8 | 9 |
|
9 | 10 | #include "quest/src/core/errors.hpp"
|
10 | 11 | #include "quest/src/core/bitwise.hpp"
|
@@ -57,6 +58,35 @@ namespace report {
|
57 | 58 | std::string CANNOT_DISTRIB_ENV_BETWEEN_NON_POW_2_NODES =
|
58 | 59 | "Cannot distribute QuEST between ${NUM_NODES} nodes; must use a power-of-2 number of nodes.";
|
59 | 60 |
|
| 61 | + |
| 62 | + /* |
| 63 | + * QUREG CREATION |
| 64 | + */ |
| 65 | + |
| 66 | + std::string FAILED_ALLOC_OF_CPU_AMPS = |
| 67 | + "Allocation of memory to store the CPU amplitudes failed."; |
| 68 | + |
| 69 | + std::string FAILED_ALLOC_OF_GPU_AMPS = |
| 70 | + "Allocation of memory to store the GPU amplitudes failed."; |
| 71 | + |
| 72 | + std::string FAILED_ALLOC_OF_CPU_COMM_BUFFER = |
| 73 | + "Allocation of memory for the distributed CPU communication buffer failed."; |
| 74 | + |
| 75 | + std::string FAILED_ALLOC_OF_GPU_COMM_BUFFER = |
| 76 | + "Allocation of memory for the distributed GPU communication buffer failed."; |
| 77 | + |
| 78 | + |
| 79 | + std::string INVALID_ALLOC_OF_CPU_AMPS = |
| 80 | + "Invalid Qureg state. The CPU memory was seemingly unallocated."; |
| 81 | + |
| 82 | + std::string INVALID_ALLOC_OF_CPU_COMM_BUFFER = |
| 83 | + "Invalid Qureg state. The distributed CPU communication buffer was seemingly unallocated."; |
| 84 | + |
| 85 | + std::string INVALID_ALLOC_OF_GPU_AMPS = |
| 86 | + "Invalid Qureg state. The GPU memory was seemingly unallocated."; |
| 87 | + |
| 88 | + std::string INVALID_ALLOC_OF_GPU_COMM_BUFFER = |
| 89 | + "Invalid Qureg state. The distributed GPU communication buffer was seemingly unallocated."; |
60 | 90 | }
|
61 | 91 |
|
62 | 92 |
|
@@ -105,26 +135,35 @@ extern "C" {
|
105 | 135 | * UTILITIES
|
106 | 136 | */
|
107 | 137 |
|
108 |
| -// list like {{"${X}", 1}, {"${Y}", -1}} with max-size signed int values to prevent overflows |
109 |
| -using tokenSubs = std::initializer_list<std::map<std::string, long long int>::value_type>; |
| 138 | +// map like "${X}" -> 5, with max-size signed int values to prevent overflows. |
| 139 | +// in C++11, these can be initialised with {{"${X}", 5}, ...} |
| 140 | +using tokenSubs = std::map<std::string, long long int>; |
110 | 141 |
|
111 | 142 | std::string getStringWithSubstitutedVars(std::string oldStr, tokenSubs varsAndVals) {
|
112 | 143 |
|
113 | 144 | std::string newStr = oldStr;
|
114 | 145 |
|
| 146 | + // substitute every var,val pair into newStr |
115 | 147 | for (auto varAndVal : varsAndVals) {
|
116 | 148 |
|
| 149 | + // unpack var -> val |
117 | 150 | std::string var = std::get<0>(varAndVal);
|
118 | 151 | std::string val = std::to_string(std::get<1>(varAndVal));
|
119 | 152 |
|
| 153 | + // assert var is well-formed |
120 | 154 | if (var[0] != '$' || var[1] != '{' || var.back() != '}' )
|
121 | 155 | error_validationMessageVarWasIllFormed(newStr, var);
|
122 | 156 |
|
123 |
| - size_t ind = newStr.find(var); |
124 |
| - if (ind == std::string::npos) |
| 157 | + // assert var appears at least once in string |
| 158 | + if (newStr.find(var) == std::string::npos) |
125 | 159 | error_validationMessageVarNotSubstituted(newStr, var);
|
126 | 160 |
|
127 |
| - newStr = newStr.replace(ind, var.length(), val); |
| 161 | + // replace every occurrence of var with val |
| 162 | + size_t ind = newStr.find(var); |
| 163 | + while (ind != std::string::npos) { |
| 164 | + newStr = newStr.replace(ind, var.length(), val); |
| 165 | + ind = newStr.find(var, ind); |
| 166 | + } |
128 | 167 | }
|
129 | 168 |
|
130 | 169 | // assert there is no $ left in the strings
|
@@ -160,15 +199,16 @@ void validate_existingEnv(QuESTEnv env, const char* caller) {
|
160 | 199 |
|
161 | 200 | void validate_envNotYetInit(const char* caller) {
|
162 | 201 |
|
163 |
| - // how will I do this?? Keep track of a singleton?? |
| 202 | + // TODO: |
| 203 | + // consult a comm/ singleton? |
164 | 204 | }
|
165 | 205 |
|
166 | 206 | void validate_envDeploymentMode(int isDistrib, int isGpuAccel, int isMultithread, const char* caller) {
|
167 | 207 |
|
168 | 208 | // deployment flags must be boolean or auto
|
169 | 209 | tokenSubs subs = {{"${AUTO_DEPLOYMENT_FLAG}", modeflag::USE_AUTO}};
|
170 |
| - assertThat(isDistrib == 0 || isDistrib == 1 || isDistrib == modeflag::USE_AUTO, report::INVALID_OPTION_FOR_ENV_IS_DISTRIB, subs, caller); |
171 |
| - assertThat(isGpuAccel == 0 || isGpuAccel == 1 || isGpuAccel == modeflag::USE_AUTO, report::INVALID_OPTION_FOR_ENV_IS_GPU_ACCEL, subs, caller); |
| 210 | + assertThat(isDistrib == 0 || isDistrib == 1 || isDistrib == modeflag::USE_AUTO, report::INVALID_OPTION_FOR_ENV_IS_DISTRIB, subs, caller); |
| 211 | + assertThat(isGpuAccel == 0 || isGpuAccel == 1 || isGpuAccel == modeflag::USE_AUTO, report::INVALID_OPTION_FOR_ENV_IS_GPU_ACCEL, subs, caller); |
172 | 212 | assertThat(isMultithread == 0 || isMultithread == 1 || isMultithread == modeflag::USE_AUTO, report::INVALID_OPTION_FOR_ENV_IS_MULTITHREAD, subs, caller);
|
173 | 213 |
|
174 | 214 | // if a deployment is forced (i.e. not auto), assert that the backend binaries are compiled correctly
|
@@ -203,3 +243,64 @@ void validate_envDistributedBetweenPower2Nodes(int numNodes, const char* caller)
|
203 | 243 | assertThat(false, report::CANNOT_DISTRIB_ENV_BETWEEN_NON_POW_2_NODES, {{"${NUM_NODES}",numNodes}}, caller);
|
204 | 244 | }
|
205 | 245 |
|
| 246 | + |
| 247 | + |
| 248 | +/* |
| 249 | + * QUREG CREATION |
| 250 | + */ |
| 251 | + |
| 252 | +void validate_quregAllocs(Qureg qureg, bool isNewQureg, const char* caller) { |
| 253 | + |
| 254 | + // determine if all relevant arrays are correctly allocated (in order of report precedence)... |
| 255 | + bool isValid = true; |
| 256 | + std::string errMsg = ""; |
| 257 | + |
| 258 | + // CPU amps should always be allocated |
| 259 | + if (qureg.cpuAmps == NULL) { |
| 260 | + errMsg = (isNewQureg)? |
| 261 | + report::FAILED_ALLOC_OF_CPU_AMPS : |
| 262 | + report::INVALID_ALLOC_OF_CPU_AMPS; |
| 263 | + isValid = false; |
| 264 | + } |
| 265 | + |
| 266 | + // CPU communication buffer is only allocated if distributed to >1 nodes |
| 267 | + else if (qureg.isDistributed && qureg.cpuCommBuffer == NULL) { |
| 268 | + errMsg = (isNewQureg)? |
| 269 | + report::FAILED_ALLOC_OF_CPU_COMM_BUFFER : |
| 270 | + report::INVALID_ALLOC_OF_CPU_COMM_BUFFER; |
| 271 | + isValid = false; |
| 272 | + } |
| 273 | + |
| 274 | + // GPU amps are only allocated in GPU mode |
| 275 | + else if (qureg.isGpuAccelerated && qureg.gpuAmps == NULL) { |
| 276 | + errMsg = (isNewQureg)? |
| 277 | + report::FAILED_ALLOC_OF_GPU_AMPS : |
| 278 | + report::INVALID_ALLOC_OF_GPU_AMPS; |
| 279 | + isValid = false; |
| 280 | + } |
| 281 | + |
| 282 | + // GPU communication buffer is only allocated in GPU mode, and when distributed to >1 nodes |
| 283 | + else if (qureg.isGpuAccelerated && qureg.isDistributed && qureg.gpuCommBuffer == NULL) { |
| 284 | + errMsg = (isNewQureg)? |
| 285 | + report::FAILED_ALLOC_OF_GPU_COMM_BUFFER : |
| 286 | + report::INVALID_ALLOC_OF_GPU_COMM_BUFFER; |
| 287 | + isValid = false; |
| 288 | + } |
| 289 | + |
| 290 | + // if the qureg was only just (attemptedly) created, and one or more arrays were not correctly allocated... |
| 291 | + if (isNewQureg && !isValid) { |
| 292 | + |
| 293 | + // then de-allocate all the successfully allocated arrays to avoid memory leak in subsequent error |
| 294 | + if (qureg.cpuAmps != NULL) |
| 295 | + cpu_deallocAmps(qureg.cpuAmps); |
| 296 | + if (qureg.cpuCommBuffer != NULL) |
| 297 | + cpu_deallocAmps(qureg.cpuCommBuffer); |
| 298 | + if (qureg.gpuAmps != NULL) |
| 299 | + gpu_deallocAmps(qureg.gpuAmps); |
| 300 | + if (qureg.gpuCommBuffer != NULL) |
| 301 | + gpu_deallocAmps(qureg.gpuCommBuffer); |
| 302 | + } |
| 303 | + |
| 304 | + // throw error or continue |
| 305 | + assertThat(isValid, errMsg, caller); |
| 306 | +} |
0 commit comments