Warrior_EA/AI/NeuronDirectML.mqh

259 lines
22 KiB
MQL5
Raw Permalink Normal View History

refactor(AI): split 8 self-contained classes out of the Network.mqh god-file AI/Network.mqh was 5,805 lines / 18 classes in one file. Investigation found method implementations for several classes (CNeuronBase/Pool/Conv, CNeuronBaseOCL) hand-interleaved across thousands of lines - not safe to split without risky manual reassembly. But 8 classes turned out to be genuinely self-contained (declaration + every method body physically contiguous, and only ever depended upon, never depending on anything declared later): CConnection/CArrayCon, CNeuron, CDirectMLMy (+ its WarriorDML.dll/WarriorCPU.dll #import blocks), CArrayLayer, CLayerDescription, CBufferDouble, and CNeuronConvOCL/CNeuronPoolOCL. Extracted each verbatim, via exact line-range extraction (not manual retyping) to eliminate transcription risk, into its own AI/*.mqh file, included from Network.mqh at the exact point each class used to sit - preserving original declaration order exactly. Mathematically verified byte-for-byte: reconstructing the original file from the 7 new files' bodies + Network.mqh's remaining segments is line-for-line identical to the pre-split git history. Compiled clean (MetaEditor, 0 errors/0 warnings) both before and after. The remaining tangled classes (CNeuronBase, CNeuronPool, CNeuronConv, CNet, CNeuronLSTM, CNeuronBaseOCL, CNeuronConvOCL/PoolOCL's shared base, CNeuronLSTMOCL, COpenCLMy) stay in Network.mqh (now ~4,450 lines) - splitting those safely needs deliberate per-method surgery, deferred to a future dedicated pass rather than rushed into this one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 16:13:03 -04:00
//+------------------------------------------------------------------+
//| 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 <terminal data folder>\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; }
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+