From e54ece364192a00f27259b68d45e1d91871be13d Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Thu, 11 Feb 2021 10:46:46 -0800 Subject: [PATCH 1/2] Better error reporting on failure to load the fullstate simulator --- .../lib/Simulators/FullstateSimulator.cpp | 115 ++++++++++-------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp b/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp index 104b1913e40..0eb50cf76c4 100644 --- a/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp +++ b/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp @@ -4,12 +4,13 @@ #include #include #include +#include #include #include #include -#include "SimFactory.hpp" #include "QuantumApi_I.hpp" +#include "SimFactory.hpp" using namespace std; @@ -21,14 +22,22 @@ typedef HMODULE QUANTUM_SIMULATOR; typedef void* QUANTUM_SIMULATOR; #endif +#ifdef _WIN32 +const char* FULLSTATESIMULATORLIB = "Microsoft.Quantum.Simulator.Runtime.dll"; +#elif __APPLE__ +const char* FULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.dylib"; +#else +const char* FULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.so"; +#endif + QUANTUM_SIMULATOR LoadQuantumSimulator() { #ifdef _WIN32 - return ::LoadLibraryA("Microsoft.Quantum.Simulator.Runtime.dll"); + return ::LoadLibraryA(FULLSTATESIMULATORLIB); #elif __APPLE__ - return ::dlopen("libMicrosoft.Quantum.Simulator.Runtime.dylib", RTLD_LAZY); + return ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); #else - return ::dlopen("libMicrosoft.Quantum.Simulator.Runtime.so", RTLD_LAZY); + return ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); #endif } @@ -78,7 +87,7 @@ namespace Quantum return static_cast(pauli); } - const QUANTUM_SIMULATOR handle; + const QUANTUM_SIMULATOR handle = 0; unsigned simulatorId = -1; unsigned nextQubitId = 0; // the QuantumSimulator expects contiguous ids, starting from 0 @@ -112,25 +121,43 @@ namespace Quantum std::cout << "*********************" << std::endl; } + void* GetProc(const char* name) + { + void* proc = LoadProc(this->handle, name); + if (proc == nullptr) + { + throw std::runtime_error(std::string("Failed to find '") + name + "' proc in " + FULLSTATESIMULATORLIB); + } + return proc; + } + public: CFullstateSimulator() : handle(LoadQuantumSimulator()) { + if (handle == 0) + { + throw std::runtime_error(std::string("Failed to load ") + FULLSTATESIMULATORLIB); + } typedef unsigned (*TInit)(); - static TInit initSimulatorInstance = reinterpret_cast(LoadProc(this->handle, "init")); + static TInit initSimulatorInstance = reinterpret_cast(this->GetProc("init")); this->simulatorId = initSimulatorInstance(); } ~CFullstateSimulator() { - typedef unsigned (*TDestroy)(unsigned); - static TDestroy destroySimulatorInstance = reinterpret_cast(LoadProc(this->handle, "destroy")); - - destroySimulatorInstance(this->simulatorId); - - // TODO: It seems that simulator might still be doing something on background threads so attempting to - // unload it might crash. - // UnloadQuantumSimulator(this->handle); + if (this->simulatorId != -1) + { + typedef unsigned (*TDestroy)(unsigned); + static TDestroy destroySimulatorInstance = + reinterpret_cast(LoadProc(this->handle, "destroy")); + assert(destroySimulatorInstance); + destroySimulatorInstance(this->simulatorId); + + // TODO: It seems that simulator might still be doing something on background threads so attempting to + // unload it might crash. + // UnloadQuantumSimulator(this->handle); + } } IQuantumGateSet* AsQuantumGateSet() override @@ -145,7 +172,7 @@ namespace Quantum void GetState(TGetStateCallback callback) override { typedef bool (*TDump)(unsigned, TGetStateCallback); - static TDump dump = reinterpret_cast(LoadProc(this->handle, "Dump")); + static TDump dump = reinterpret_cast(this->GetProc("Dump")); dump(this->simulatorId, callback); } @@ -157,8 +184,7 @@ namespace Quantum Qubit AllocateQubit() override { typedef void (*TAllocateQubit)(unsigned, unsigned); - static TAllocateQubit allocateQubit = - reinterpret_cast(LoadProc(this->handle, "allocateQubit")); + static TAllocateQubit allocateQubit = reinterpret_cast(this->GetProc("allocateQubit")); const unsigned id = this->nextQubitId; allocateQubit(this->simulatorId, id); @@ -169,7 +195,7 @@ namespace Quantum void ReleaseQubit(Qubit q) override { typedef void (*TReleaseQubit)(unsigned, unsigned); - static TReleaseQubit releaseQubit = reinterpret_cast(LoadProc(this->handle, "release")); + static TReleaseQubit releaseQubit = reinterpret_cast(this->GetProc("release")); releaseQubit(this->simulatorId, GetQubitId(q)); } @@ -177,7 +203,7 @@ namespace Quantum Result M(Qubit q) override { typedef unsigned (*TM)(unsigned, unsigned); - static TM m = reinterpret_cast(LoadProc(this->handle, "M")); + static TM m = reinterpret_cast(this->GetProc("M")); return reinterpret_cast(m(this->simulatorId, GetQubitId(q))); } @@ -185,7 +211,7 @@ namespace Quantum { assert(numBases == numTargets); typedef unsigned (*TMeasure)(unsigned, unsigned, unsigned*, unsigned*); - static TMeasure m = reinterpret_cast(LoadProc(this->handle, "Measure")); + static TMeasure m = reinterpret_cast(this->GetProc("Measure")); vector ids = GetQubitIds(numTargets, targets); return reinterpret_cast( m(this->simulatorId, numBases, reinterpret_cast(bases), ids.data())); @@ -217,112 +243,106 @@ namespace Quantum void X(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "X")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("X")); op(this->simulatorId, GetQubitId(q)); } void ControlledX(long numControls, Qubit controls[], Qubit target) override { - static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCX")); + static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCX")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void Y(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "Y")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("Y")); op(this->simulatorId, GetQubitId(q)); } void ControlledY(long numControls, Qubit controls[], Qubit target) override { - static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCY")); + static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCY")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void Z(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "Z")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("Z")); op(this->simulatorId, GetQubitId(q)); } void ControlledZ(long numControls, Qubit controls[], Qubit target) override { - static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCZ")); + static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCZ")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void H(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "H")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("H")); op(this->simulatorId, GetQubitId(q)); } void ControlledH(long numControls, Qubit controls[], Qubit target) override { - static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCH")); + static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCH")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void S(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "S")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("S")); op(this->simulatorId, GetQubitId(q)); } void ControlledS(long numControls, Qubit controls[], Qubit target) override { - static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCS")); + static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCS")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void AdjointS(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "AdjS")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("AdjS")); op(this->simulatorId, GetQubitId(q)); } void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override { static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCAdjS")); + reinterpret_cast(this->GetProc("MCAdjS")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void T(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "T")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("T")); op(this->simulatorId, GetQubitId(q)); } void ControlledT(long numControls, Qubit controls[], Qubit target) override { - static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCT")); + static TSingleQubitControlledGate op = reinterpret_cast(this->GetProc("MCT")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } void AdjointT(Qubit q) override { - static TSingleQubitGate op = reinterpret_cast(LoadProc(this->handle, "AdjT")); + static TSingleQubitGate op = reinterpret_cast(this->GetProc("AdjT")); op(this->simulatorId, GetQubitId(q)); } void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override { static TSingleQubitControlledGate op = - reinterpret_cast(LoadProc(this->handle, "MCAdjT")); + reinterpret_cast(this->GetProc("MCAdjT")); vector ids = GetQubitIds(numControls, controls); op(this->simulatorId, numControls, ids.data(), GetQubitId(target)); } @@ -330,7 +350,7 @@ namespace Quantum void R(PauliId axis, Qubit target, double theta) override { typedef unsigned (*TR)(unsigned, unsigned, double, unsigned); - static TR r = reinterpret_cast(LoadProc(this->handle, "R")); + static TR r = reinterpret_cast(this->GetProc("R")); r(this->simulatorId, GetBasis(axis), theta, GetQubitId(target)); } @@ -338,7 +358,7 @@ namespace Quantum void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override { typedef unsigned (*TMCR)(unsigned, unsigned, double, unsigned, unsigned*, unsigned); - static TMCR cr = reinterpret_cast(LoadProc(this->handle, "MCR")); + static TMCR cr = reinterpret_cast(this->GetProc("MCR")); vector ids = GetQubitIds(numControls, controls); cr(this->simulatorId, GetBasis(axis), theta, numControls, ids.data(), GetQubitId(target)); @@ -347,7 +367,7 @@ namespace Quantum void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override { typedef unsigned (*TExp)(unsigned, unsigned, unsigned*, double, unsigned*); - static TExp exp = reinterpret_cast(LoadProc(this->handle, "Exp")); + static TExp exp = reinterpret_cast(this->GetProc("Exp")); vector ids = GetQubitIds(numTargets, targets); exp(this->simulatorId, numTargets, reinterpret_cast(paulis), theta, ids.data()); } @@ -361,7 +381,7 @@ namespace Quantum double theta) override { typedef unsigned (*TMCExp)(unsigned, unsigned, unsigned*, double, unsigned, unsigned*, unsigned*); - static TMCExp cexp = reinterpret_cast(LoadProc(this->handle, "MCExp")); + static TMCExp cexp = reinterpret_cast(this->GetProc("MCExp")); vector idsTargets = GetQubitIds(numTargets, targets); vector idsControls = GetQubitIds(numControls, controls); cexp( @@ -384,8 +404,7 @@ namespace Quantum const char* failureMessage) override { typedef double (*TOp)(unsigned id, unsigned n, int* b, unsigned* q); - static TOp jointEnsembleProbability = - reinterpret_cast(LoadProc(this->handle, "JointEnsembleProbability")); + static TOp jointEnsembleProbability = reinterpret_cast(this->GetProc("JointEnsembleProbability")); vector ids = GetQubitIds(numTargets, targets); double actualProbability = From fe2c894d7779018fe0870f46501b85317b54ad71 Mon Sep 17 00:00:00 2001 From: Irina Yatsenko Date: Thu, 11 Feb 2021 12:01:59 -0800 Subject: [PATCH 2/2] Add error code to the exception message --- .../lib/Simulators/FullstateSimulator.cpp | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp b/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp index 0eb50cf76c4..d4d016da4e0 100644 --- a/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp +++ b/src/QirRuntime/lib/Simulators/FullstateSimulator.cpp @@ -22,6 +22,8 @@ typedef HMODULE QUANTUM_SIMULATOR; typedef void* QUANTUM_SIMULATOR; #endif +namespace +{ #ifdef _WIN32 const char* FULLSTATESIMULATORLIB = "Microsoft.Quantum.Simulator.Runtime.dll"; #elif __APPLE__ @@ -32,13 +34,24 @@ const char* FULLSTATESIMULATORLIB = "libMicrosoft.Quantum.Simulator.Runtime.so"; QUANTUM_SIMULATOR LoadQuantumSimulator() { + QUANTUM_SIMULATOR handle = 0; #ifdef _WIN32 - return ::LoadLibraryA(FULLSTATESIMULATORLIB); -#elif __APPLE__ - return ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); + handle = ::LoadLibraryA(FULLSTATESIMULATORLIB); + if (handle == NULL) + { + throw std::runtime_error( + std::string("Failed to load ") + FULLSTATESIMULATORLIB + + " (error code: " + std::to_string(GetLastError()) + ")"); + } #else - return ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); + handle = ::dlopen(FULLSTATESIMULATORLIB, RTLD_LAZY); + if (handle == nullptr) + { + throw std::runtime_error( + std::string("Failed to load ") + FULLSTATESIMULATORLIB + " (" + ::dlerror() + ")"); + } #endif + return handle; } bool UnloadQuantumSimulator(QUANTUM_SIMULATOR handle) @@ -58,6 +71,7 @@ void* LoadProc(QUANTUM_SIMULATOR handle, const char* procName) return ::dlsym(handle, procName); #endif } +} // namespace namespace Microsoft { @@ -135,10 +149,6 @@ namespace Quantum CFullstateSimulator() : handle(LoadQuantumSimulator()) { - if (handle == 0) - { - throw std::runtime_error(std::string("Failed to load ") + FULLSTATESIMULATORLIB); - } typedef unsigned (*TInit)(); static TInit initSimulatorInstance = reinterpret_cast(this->GetProc("init"));