//+------------------------------------------------------------------+ //| NeuronDirectML.mqh | //| AnimateDread | //| https://www.mql5.com | //+------------------------------------------------------------------+ //| CDirectMLMy + the WarriorDML.dll/WarriorCPU.dll #import blocks | //| it wraps - the DirectML/D3D12 GPU and multithreaded-CPU fallback | //| compute tiers used when OpenCL is unavailable. Self-contained - | //| nothing outside this class touches DML_*/CPU_* directly. | //| Extracted verbatim out of AI\Network.mqh (SOLID cleanup) - no | //| logic changes. | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Native compute DLLs (WarriorDML.dll / WarriorCPU.dll) cannot be | //| embedded via #resource - MQL5 refuses to compile any #resource | //| pointing at a PE executable ("unsupported resource type ... | //| executable file prohibited"). They must instead be copied | //| manually into \MQL5\Libraries\ alongside | //| the compiled EA. | //+------------------------------------------------------------------+ //| DirectML/D3D12 GPU fallback tier - used only when OpenCL is | //| unavailable. Mirrors the subset of COpenCLMy's interface that | //| CBufferDouble/CNeuronBaseOCL need (buffer CRUD + the 5 kernels). | //| Backed by DirectML\WarriorDML.dll (build it with DirectML\build.bat).| //+------------------------------------------------------------------+ // Every function below (besides DML_Init) takes `ctx`, the opaque per-instance handle DML_Init() // returns - see DirectML\WarriorDML.h's DmlHandle for why: the DLL keeps no global state of its own, // so each CDirectMLMy instance owns an independent D3D12 device and the DLL can be loaded/used by any // number of instances or threads in parallel with zero cross-talk between them. #import "WarriorDML.dll" long DML_Init(int &error); int DML_GetLastError(long ctx); void DML_Shutdown(long ctx); int DML_BufferCreate(long ctx, int elementCount); int DML_BufferWrite(long ctx, int handle, const double &data[], int count); int DML_BufferRead(long ctx, int handle, double &data[], int count); void DML_BufferFree(long ctx, int handle); int DML_FeedForward(long ctx, int wHandle, int iHandle, int oHandle, int inputs, int activation); int DML_CalcOutputGradient(long ctx, int tHandle, int oHandle, int igHandle, int activation, int count); int DML_CalcHiddenGradient(long ctx, int wHandle, int gHandle, int oHandle, int igHandle, int outputs, int activation, int count); int DML_UpdateWeightsMomentum(long ctx, int wHandle, int gHandle, int iHandle, int dwHandle, int inputs, double learningRate, double momentumRate, int neurons); int DML_UpdateWeightsAdam(long ctx, int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int neurons); int DML_FeedForwardConv(long ctx, int wHandle, int iHandle, int oHandle, int inputs, int step, int windowIn, int windowOut, int activation, int positions); int DML_CalcHiddenGradientConv(long ctx, int wHandle, int gHandle, int oHandle, int igHandle, int outputs, int step, int windowIn, int windowOut, int activation, int inputCount); int DML_UpdateWeightsConvMomentum(long ctx, int wHandle, int gHandle, int iHandle, int dwHandle, int inputs, double learningRate, double momentumRate, int windowIn, int windowOut, int step); int DML_UpdateWeightsConvAdam(long ctx, int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int windowIn, int windowOut, int step); int DML_LSTMGates(long ctx, int wHandle, int hiddenPrevHandle, int inputsHandle, int concatenatedHandle, int hiddenSize, int inputSize); int DML_LSTMState(long ctx, int concatenatedHandle, int memoryHandle, int hiddenPrevHandle, int hiddenCacheHandle, int outputHandle, int hiddenSize); int DML_LSTMGateGradient(long ctx, int gradientHandle, int memoryHandle, int concatenatedHandle, int concatenatedGradientHandle, int hiddenSize); int DML_LSTMWeightsGradient(long ctx, int concatenatedGradientHandle, int hiddenCacheHandle, int inputsHandle, int weightsGradientHandle, int hiddenSize, int inputSize); int DML_LSTMInputsGradient(long ctx, int concatenatedGradientHandle, int wHandle, int inputsGradientHandle, int hiddenSize, int inputSize); int DML_LSTMUpdateWeightsAdam(long ctx, int wHandle, int weightsGradientHandle, int mHandle, int vHandle, double l, double b1v, double b2v, int total); int DML_LSTMUpdateWeightsMomentum(long ctx, int wHandle, int weightsGradientHandle, int dwHandle, double learningRate, double momentumRate, int total); int DML_FeedForwardProof(long ctx, int iHandle, int oHandle, int inputs, int window, int step, int outputs); int DML_CalcInputGradientProof(long ctx, int iHandle, int gHandle, int oHandle, int igHandle, int outputs, int window, int step, int inputs); #import //+------------------------------------------------------------------+ //| CPU thread-pool fallback tier - used only when neither OpenCL nor | //| DirectML/D3D12 are available (e.g. a VM with no GPU passthrough). | //| Same buffer-handle model as WarriorDML.dll, full double precision,| //| work spread across a configurable pool of worker threads instead | //| of a device. Backed by DirectML\WarriorCPU.dll (build it with | //| DirectML\build_cpu.bat). | //+------------------------------------------------------------------+ // Same per-instance-handle model as WarriorDML.dll above - every function besides // CPU_Init/CPU_GetHardwareConcurrency takes `ctx`, the handle CPU_Init() returns. #import "WarriorCPU.dll" long CPU_Init(int threads); int CPU_GetLastError(long ctx); int CPU_GetThreadCount(long ctx); int CPU_GetHardwareConcurrency(); void CPU_Shutdown(long ctx); int CPU_BufferCreate(long ctx, int elementCount); int CPU_BufferWrite(long ctx, int handle, const double &data[], int count); int CPU_BufferRead(long ctx, int handle, double &data[], int count); void CPU_BufferFree(long ctx, int handle); int CPU_FeedForward(long ctx, int wHandle, int iHandle, int oHandle, int inputs, int activation); int CPU_CalcOutputGradient(long ctx, int tHandle, int oHandle, int igHandle, int activation, int count); int CPU_CalcHiddenGradient(long ctx, int wHandle, int gHandle, int oHandle, int igHandle, int outputs, int activation, int count); int CPU_UpdateWeightsMomentum(long ctx, int wHandle, int gHandle, int iHandle, int dwHandle, int inputs, double learningRate, double momentumRate, int neurons); int CPU_UpdateWeightsAdam(long ctx, int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int neurons); int CPU_FeedForwardConv(long ctx, int wHandle, int iHandle, int oHandle, int inputs, int step, int windowIn, int windowOut, int activation, int positions); int CPU_CalcHiddenGradientConv(long ctx, int wHandle, int gHandle, int oHandle, int igHandle, int outputs, int step, int windowIn, int windowOut, int activation, int inputCount); int CPU_UpdateWeightsConvMomentum(long ctx, int wHandle, int gHandle, int iHandle, int dwHandle, int inputs, double learningRate, double momentumRate, int windowIn, int windowOut, int step); int CPU_UpdateWeightsConvAdam(long ctx, int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int windowIn, int windowOut, int step); int CPU_LSTMGates(long ctx, int wHandle, int hiddenPrevHandle, int inputsHandle, int concatenatedHandle, int hiddenSize, int inputSize); int CPU_LSTMState(long ctx, int concatenatedHandle, int memoryHandle, int hiddenPrevHandle, int hiddenCacheHandle, int outputHandle, int hiddenSize); int CPU_LSTMGateGradient(long ctx, int gradientHandle, int memoryHandle, int concatenatedHandle, int concatenatedGradientHandle, int hiddenSize); int CPU_LSTMWeightsGradient(long ctx, int concatenatedGradientHandle, int hiddenCacheHandle, int inputsHandle, int weightsGradientHandle, int hiddenSize, int inputSize); int CPU_LSTMInputsGradient(long ctx, int concatenatedGradientHandle, int wHandle, int inputsGradientHandle, int hiddenSize, int inputSize); int CPU_LSTMUpdateWeightsAdam(long ctx, int wHandle, int weightsGradientHandle, int mHandle, int vHandle, double l, double b1v, double b2v, int total); int CPU_LSTMUpdateWeightsMomentum(long ctx, int wHandle, int weightsGradientHandle, int dwHandle, double learningRate, double momentumRate, int total); int CPU_FeedForwardProof(long ctx, int iHandle, int oHandle, int inputs, int window, int step, int outputs); int CPU_CalcInputGradientProof(long ctx, int iHandle, int gHandle, int oHandle, int igHandle, int outputs, int window, int step, int inputs); #import //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum ENUM_COMPUTE_TIER { COMPUTE_TIER_NONE = 0, COMPUTE_TIER_GPU = 1, // D3D12/DirectML - WarriorDML.dll COMPUTE_TIER_CPU = 2 // multithreaded fallback - WarriorCPU.dll }; //+------------------------------------------------------------------+ //| Second/third-tier fallback wrapper. Tries the D3D12/DirectML GPU | //| tier first; if that fails (no HW accel, e.g. a VM) it tries the | //| multithreaded CPU tier instead, so callers just get "some kind of | //| buffer-based acceleration, or NULL" without caring which backend | //| is actually doing the work. | //+------------------------------------------------------------------+ class CDirectMLMy { private: ENUM_COMPUTE_TIER m_tier; int m_cpuLoadPercent; // 100 = auto/all cores; 10-90 scales down from the detected count //--- opaque per-instance context handle for whichever backend is active (0 when m_tier==NONE). //--- Neither WarriorDML.dll nor WarriorCPU.dll keep any global state of their own anymore - each //--- DML_Init()/CPU_Init() call heap-allocates an independent context (own device/pool, own buffer //--- table) and this handle is the caller's only reference to it, threaded through every single //--- call below. This is what makes the DLLs safe to load/reload and use from any number of //--- CNet instances at once: a fault or a watchdog-killed call against one instance's context can //--- never poison another chart's or another CNet's calls the way a shared/global singleton could. long m_ctx; //--- DML_Init()'s failure reason, captured immediately since a failed Init() hands back no context //--- to read it from afterward (see WarriorDML.h's DmlHandle/DML_Init comment) - LastError() falls //--- back to this when m_tier is NONE (both backends failed). int m_initError; public: CDirectMLMy(void) : m_tier(COMPUTE_TIER_NONE), m_cpuLoadPercent(100), m_ctx(0), m_initError(0) {}; ~CDirectMLMy(void) { if(m_tier == COMPUTE_TIER_GPU) DML_Shutdown(m_ctx); else if(m_tier == COMPUTE_TIER_CPU) CPU_Shutdown(m_ctx); } //--- pct <= 0 or >= 100 means "let WarriorCPU.dll pick hardware_concurrency() (all cores)"; //--- 10-90 targets that percentage of the auto-detected core count instead. Only affects the //--- CPU DLL fallback tier (COMPUTE_TIER_CPU) - has no effect when the GPU (DirectML) tier is //--- active, since that path never touches WarriorCPU.dll at all. void SetCpuLoadPercent(int pct) { m_cpuLoadPercent = pct; } ENUM_COMPUTE_TIER Tier(void) { return m_tier; } int CpuThreadsUsed(void) { return (m_tier == COMPUTE_TIER_CPU) ? CPU_GetThreadCount(m_ctx) : 0; } bool Initialize(void) { int dmlError = 0; long dmlCtx = DML_Init(dmlError); if(dmlCtx != 0) { m_ctx = dmlCtx; m_tier = COMPUTE_TIER_GPU; return true; } m_initError = dmlError; int threads = 0; // 0 = auto (hardware_concurrency, i.e. all cores) if(m_cpuLoadPercent > 0 && m_cpuLoadPercent < 100) { // CPU_GetHardwareConcurrency() is a stateless OS query, independent of any other // instance's pool size - each CNet gets its own CPU_Init() context now, so there is no // shared pool for this to be scaled down by the way there used to be. int detected = CPU_GetHardwareConcurrency(); threads = (int)MathMax(1, MathRound(detected * m_cpuLoadPercent / 100.0)); } long cpuCtx = CPU_Init(threads); if(cpuCtx != 0) { m_ctx = cpuCtx; m_tier = COMPUTE_TIER_CPU; return true; } m_tier = COMPUTE_TIER_NONE; return false; } int LastError(void) { return (m_tier == COMPUTE_TIER_NONE) ? m_initError : ((m_tier == COMPUTE_TIER_CPU) ? CPU_GetLastError(m_ctx) : DML_GetLastError(m_ctx)); } int BufferCreate(int count) { return (m_tier == COMPUTE_TIER_CPU) ? CPU_BufferCreate(m_ctx, count) : DML_BufferCreate(m_ctx, count); } bool BufferWrite(int handle, double &data[], int count) { return (m_tier == COMPUTE_TIER_CPU ? CPU_BufferWrite(m_ctx, handle, data, count) : DML_BufferWrite(m_ctx, handle, data, count)) != 0; } bool BufferRead(int handle, double &data[], int count) { return (m_tier == COMPUTE_TIER_CPU ? CPU_BufferRead(m_ctx, handle, data, count) : DML_BufferRead(m_ctx, handle, data, count)) != 0; } void BufferFree(int handle) { if(m_tier == COMPUTE_TIER_CPU) CPU_BufferFree(m_ctx, handle); else DML_BufferFree(m_ctx, handle); } bool FeedForward(int wHandle, int iHandle, int oHandle, int inputs, int activation) { return (m_tier == COMPUTE_TIER_CPU ? CPU_FeedForward(m_ctx, wHandle, iHandle, oHandle, inputs, activation) : DML_FeedForward(m_ctx, wHandle, iHandle, oHandle, inputs, activation)) != 0; } bool CalcOutputGradient(int tHandle, int oHandle, int igHandle, int activation, int count) { return (m_tier == COMPUTE_TIER_CPU ? CPU_CalcOutputGradient(m_ctx, tHandle, oHandle, igHandle, activation, count) : DML_CalcOutputGradient(m_ctx, tHandle, oHandle, igHandle, activation, count)) != 0; } bool CalcHiddenGradient(int wHandle, int gHandle, int oHandle, int igHandle, int outputs, int activation, int count) { return (m_tier == COMPUTE_TIER_CPU ? CPU_CalcHiddenGradient(m_ctx, wHandle, gHandle, oHandle, igHandle, outputs, activation, count) : DML_CalcHiddenGradient(m_ctx, wHandle, gHandle, oHandle, igHandle, outputs, activation, count)) != 0; } bool UpdateWeightsMomentum(int wHandle, int gHandle, int iHandle, int dwHandle, int inputs, double learningRate, double momentumRate, int neurons) { return (m_tier == COMPUTE_TIER_CPU ? CPU_UpdateWeightsMomentum(m_ctx, wHandle, gHandle, iHandle, dwHandle, inputs, learningRate, momentumRate, neurons) : DML_UpdateWeightsMomentum(m_ctx, wHandle, gHandle, iHandle, dwHandle, inputs, learningRate, momentumRate, neurons)) != 0; } bool UpdateWeightsAdam(int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int neurons) { return (m_tier == COMPUTE_TIER_CPU ? CPU_UpdateWeightsAdam(m_ctx, wHandle, gHandle, iHandle, mHandle, vHandle, inputs, lt, b1v, b2v, neurons) : DML_UpdateWeightsAdam(m_ctx, wHandle, gHandle, iHandle, mHandle, vHandle, inputs, lt, b1v, b2v, neurons)) != 0; } bool FeedForwardConv(int wHandle, int iHandle, int oHandle, int inputs, int step, int windowIn, int windowOut, int activation, int positions) { return (m_tier == COMPUTE_TIER_CPU ? CPU_FeedForwardConv(m_ctx, wHandle, iHandle, oHandle, inputs, step, windowIn, windowOut, activation, positions) : DML_FeedForwardConv(m_ctx, wHandle, iHandle, oHandle, inputs, step, windowIn, windowOut, activation, positions)) != 0; } bool CalcHiddenGradientConv(int wHandle, int gHandle, int oHandle, int igHandle, int outputs, int step, int windowIn, int windowOut, int activation, int inputCount) { return (m_tier == COMPUTE_TIER_CPU ? CPU_CalcHiddenGradientConv(m_ctx, wHandle, gHandle, oHandle, igHandle, outputs, step, windowIn, windowOut, activation, inputCount) : DML_CalcHiddenGradientConv(m_ctx, wHandle, gHandle, oHandle, igHandle, outputs, step, windowIn, windowOut, activation, inputCount)) != 0; } bool UpdateWeightsConvMomentum(int wHandle, int gHandle, int iHandle, int dwHandle, int inputs, double learningRate, double momentumRate, int windowIn, int windowOut, int step) { return (m_tier == COMPUTE_TIER_CPU ? CPU_UpdateWeightsConvMomentum(m_ctx, wHandle, gHandle, iHandle, dwHandle, inputs, learningRate, momentumRate, windowIn, windowOut, step) : DML_UpdateWeightsConvMomentum(m_ctx, wHandle, gHandle, iHandle, dwHandle, inputs, learningRate, momentumRate, windowIn, windowOut, step)) != 0; } bool UpdateWeightsConvAdam(int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int windowIn, int windowOut, int step) { return (m_tier == COMPUTE_TIER_CPU ? CPU_UpdateWeightsConvAdam(m_ctx, wHandle, gHandle, iHandle, mHandle, vHandle, inputs, lt, b1v, b2v, windowIn, windowOut, step) : DML_UpdateWeightsConvAdam(m_ctx, wHandle, gHandle, iHandle, mHandle, vHandle, inputs, lt, b1v, b2v, windowIn, windowOut, step)) != 0; } bool LSTMGates(int wHandle, int hiddenPrevHandle, int inputsHandle, int concatenatedHandle, int hiddenSize, int inputSize) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMGates(m_ctx, wHandle, hiddenPrevHandle, inputsHandle, concatenatedHandle, hiddenSize, inputSize) : DML_LSTMGates(m_ctx, wHandle, hiddenPrevHandle, inputsHandle, concatenatedHandle, hiddenSize, inputSize)) != 0; } bool LSTMState(int concatenatedHandle, int memoryHandle, int hiddenPrevHandle, int hiddenCacheHandle, int outputHandle, int hiddenSize) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMState(m_ctx, concatenatedHandle, memoryHandle, hiddenPrevHandle, hiddenCacheHandle, outputHandle, hiddenSize) : DML_LSTMState(m_ctx, concatenatedHandle, memoryHandle, hiddenPrevHandle, hiddenCacheHandle, outputHandle, hiddenSize)) != 0; } bool LSTMGateGradient(int gradientHandle, int memoryHandle, int concatenatedHandle, int concatenatedGradientHandle, int hiddenSize) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMGateGradient(m_ctx, gradientHandle, memoryHandle, concatenatedHandle, concatenatedGradientHandle, hiddenSize) : DML_LSTMGateGradient(m_ctx, gradientHandle, memoryHandle, concatenatedHandle, concatenatedGradientHandle, hiddenSize)) != 0; } bool LSTMWeightsGradient(int concatenatedGradientHandle, int hiddenCacheHandle, int inputsHandle, int weightsGradientHandle, int hiddenSize, int inputSize) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMWeightsGradient(m_ctx, concatenatedGradientHandle, hiddenCacheHandle, inputsHandle, weightsGradientHandle, hiddenSize, inputSize) : DML_LSTMWeightsGradient(m_ctx, concatenatedGradientHandle, hiddenCacheHandle, inputsHandle, weightsGradientHandle, hiddenSize, inputSize)) != 0; } bool LSTMInputsGradient(int concatenatedGradientHandle, int wHandle, int inputsGradientHandle, int hiddenSize, int inputSize) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMInputsGradient(m_ctx, concatenatedGradientHandle, wHandle, inputsGradientHandle, hiddenSize, inputSize) : DML_LSTMInputsGradient(m_ctx, concatenatedGradientHandle, wHandle, inputsGradientHandle, hiddenSize, inputSize)) != 0; } bool LSTMUpdateWeightsAdam(int wHandle, int weightsGradientHandle, int mHandle, int vHandle, double l, double b1v, double b2v, int total) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMUpdateWeightsAdam(m_ctx, wHandle, weightsGradientHandle, mHandle, vHandle, l, b1v, b2v, total) : DML_LSTMUpdateWeightsAdam(m_ctx, wHandle, weightsGradientHandle, mHandle, vHandle, l, b1v, b2v, total)) != 0; } bool LSTMUpdateWeightsMomentum(int wHandle, int weightsGradientHandle, int dwHandle, double learningRate, double momentumRate, int total) { return (m_tier == COMPUTE_TIER_CPU ? CPU_LSTMUpdateWeightsMomentum(m_ctx, wHandle, weightsGradientHandle, dwHandle, learningRate, momentumRate, total) : DML_LSTMUpdateWeightsMomentum(m_ctx, wHandle, weightsGradientHandle, dwHandle, learningRate, momentumRate, total)) != 0; } bool FeedForwardProof(int iHandle, int oHandle, int inputs, int window, int step, int outputs) { return (m_tier == COMPUTE_TIER_CPU ? CPU_FeedForwardProof(m_ctx, iHandle, oHandle, inputs, window, step, outputs) : DML_FeedForwardProof(m_ctx, iHandle, oHandle, inputs, window, step, outputs)) != 0; } bool CalcInputGradientProof(int iHandle, int gHandle, int oHandle, int igHandle, int outputs, int window, int step, int inputs) { return (m_tier == COMPUTE_TIER_CPU ? CPU_CalcInputGradientProof(m_ctx, iHandle, gHandle, oHandle, igHandle, outputs, window, step, inputs) : DML_CalcInputGradientProof(m_ctx, iHandle, gHandle, oHandle, igHandle, outputs, window, step, inputs)) != 0; } }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+