Warrior_EA/AI/ComputeDll.mqh

221 lines
20 KiB
MQL5
Raw Permalink Normal View History

refactor(ai): remove the DirectML/D3D12 GPU compute tier (S1.5) Three backends left, as the operator specified: OpenCL, the CPU DLL, and pure MQL5. CDirectMLMy was a two-tier wrapper (GPU via WarriorDML.dll, CPU via WarriorCPU.dll) whose name only ever named the tier being removed here; the CPU DLL tier - the one actually used on the training machine (no OpenCL, no DirectML) - is untouched. AI/NeuronDirectML.mqh -> AI/ComputeDll.mqh: dropped the DML_* #import block and COMPUTE_TIER_GPU (checked first that nothing persists the enum value and only one external site reads .Tier() - safe), collapsed every tier==CPU?CPU_x():DML_x() ternary to a straight CPU_x() call. Renamed CDirectMLMy->CComputeDll, InitDirectML()->InitComputeDll(), member directml/DirectML->computeDll/ComputeDll across every AI/ file that touched a neuron/net backend plus Topology.mqh/OnlineLearning.mqh. NetBuild.mqh's InitComputeDll also lost the dead D3D12 error-code switch and the now-impossible GPU-tier log branch. Verified via per-file brace-balance diff against HEAD and a whole-repo grep for every removed symbol (CDirectMLMy/InitDirectML/ COMPUTE_TIER_GPU/DML_*) - the only surviving hit is an intentional historical-note comment in the new file's header. DirectML\WarriorDML.cpp/.h and its build scripts are now orphaned C++ source, left in place pending an operator decision. Architecture docs (AI_NETWORK.md, Warrior_EA_System_Overview.md, etc.) still describe the 4-backend/GPU-tier shape and are not updated in this pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:32:09 -04:00
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| ComputeDll.mqh |
//| AnimateDread |
//| https://www.mql5.com |
//| CComputeDll + the WarriorCPU.dll #import block it wraps - the |
//| multithreaded-CPU compute tier used when OpenCL is unavailable. |
//| Self-contained - nothing outside this class touches CPU_* import |
//| functions directly. |
//| Formerly NeuronDirectML.mqh / CDirectMLMy: the D3D12/DirectML GPU |
//| tier (WarriorDML.dll) it also used to front was removed entirely |
//| (operator directive 2026-08-23) - three backends left: OpenCL, |
//| this CPU DLL, pure MQL5. See project_solid_campaign_plan S1.5. |
//+------------------------------------------------------------------+
//| CPU thread-pool fallback tier - used when OpenCL is unavailable |
//| (e.g. a VM with no GPU passthrough). 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). |
//+------------------------------------------------------------------+
// Every function below (besides CPU_Init/CPU_GetHardwareConcurrency) takes `ctx`, the opaque
// per-instance handle CPU_Init() returns - see DirectML\WarriorCPU.h's comment for why: the DLL
// keeps no global state of its own, so each CComputeDll instance owns an independent thread pool
// and the DLL can be loaded/used by any number of instances or threads in parallel with zero
// cross-talk between them.
#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 optimizer);
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 optimizer);
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_LSTMSeqForward(long ctx, int wHandle, int inputsHandle, int cacheGatesHandle, int cacheCellHandle, int cacheHiddenHandle, int outputHandle, int hiddenSize, int stepInputs, int steps);
int CPU_LSTMSeqBackward(long ctx, int wHandle, int inputsHandle, int cacheGatesHandle, int cacheCellHandle, int cacheHiddenHandle, int outGradientHandle, int weightsGradientHandle, int inputsGradientHandle, int hiddenSize, int stepInputs, int steps);
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 optimizer);
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);
int CPU_AccumulateWeightGrad(long ctx, int accHandle, int gHandle, int iHandle, int inputs, int neurons);
int CPU_AccumulateWeightGradConv(long ctx, int accHandle, int gHandle, int iHandle, int inputs, int windowIn, int windowOut, int step);
int CPU_AccumulateBufferInto(long ctx, int dstHandle, int srcHandle, int count);
perf(train): DLL-side mini-batch apply + 300ms slice - the era bottleneck "Hundreds of times slower than a regular EA" decomposed into two multiplied factors, both measured: 1. THE OPTIMIZER STEP RAN IN INTERPRETED MQL5. The CPU tier shipped the F4 accumulate exports with deliberately no matching apply (WarriorCPU.h said so), so on the DLL backend - this box - every TRAIN_BATCH_SIZE=8 batch fell to the host loop in ApplyAccumToBlock: a per-weight MQL5 pass through CBufferDouble.At()/Update() plus four full weight-matrix BufferRead/Write round trips. The 2026-07-26 profile had already shown the per-sample Adam step at 81% of ALL runtime (feedForward: 8%; feature building: 0.35%) - sqrt+divide per weight vs one multiply-add; moving it into MQL5 made it worse. New CPU_ApplyAccumAdam / CPU_ApplyAccumMomentum: one element-wise ParallelFor takes the batch-mean step and zeroes the accumulator DLL-side, generic over any flat block (dense/conv/LSTM/batch-norm - all apply paths funnel through ApplyAccumToBlock, which now tries the DLL first, with the same one-warning failure latch as the OpenCL fast path). Math is the shipped step to the last clamp: sqrt-stored v, ClampDelta, AdamW decay, ClampWeight. batch_accum_check extended (check 6) and ALL PASS: apply == host reference at B=8/B=4, accumulator zeroed, and B=1 accumulate+apply == the unbatched Adam kernel BIT-EXACTLY (kernel-vs-kernel, no transcription). DLL rebuilt with the shipped /fp:fast recipe. 2. A 24% DUTY CYCLE. Train sliced 120ms per 500ms timer period (30ms/member x4), leaving the chart thread idle 76% of the time. Now 300ms total (75ms/member): ~60% duty, ~2.5x, click latency bounded at ~300ms while training runs - between the fully-reactive 120 and the documented "sticky drag" 480. DEPLOYMENT COUPLING: the new .ex5 #imports the new exports, so it will NOT LOAD against the old WarriorCPU.dll ("cannot find function"). Copy DirectML\WarriorCPU.dll into MQL5\Libraries (terminal closed) in the same step as deploying the new .ex5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 19:32:15 -04:00
int CPU_ApplyAccumAdam(long ctx, int wHandle, int accHandle, int mHandle, int vHandle, int total, double scale, double lt, double b1v, double b2v);
int CPU_ApplyAccumMomentum(long ctx, int wHandle, int accHandle, int dwHandle, int total, double scale, double learningRate, double momentumRate);
perf(tester,bn): no sub-second timer in the tester + BN kernels on the DLL tier THE OPTIMIZER ("0.1% an hour per agent", 0 of 39 passes in 78 min, 12 agents): the tester fires OnTimer on SIMULATED time, so the live chart's 500ms EventSetMillisecondTimer over a 2016-2026 pass is ~600 MILLION OnTimer calls - each walking 4x PollTraining, the vote readout's string build, the overlay advance and the deployed census. None of it serves an inference-only pass: training never runs, per-bar inference is driven by OnTickHandler off the tick stream, the risk budget re-checks in OnTick, and there is no chart to keep fresh. StepSetTimer now arms EventSetTimer(3600) in tester/optimizer/forward (~2,600 calls per pass) and keeps the 500ms timer for live charts. Plus a TESTER PASS SELF-PROFILE: per-tick buckets (pre / Expert.OnTick / journal) and the timer total, printed once at the pass's OnDeinit - so if a pass is still slow it names its own consumer instead of being diagnosed from outside. OFFLOAD (operator: "as much calculation as possible to DLL/OpenCL"): batch norm was the ONE stage still host-side on the DLL tier - the device path was OpenCL-only, so every sample crossed the bus twice per BN layer and normalized in interpreted MQL5 (and every model runs batchnorm ON). Four new exports mirror AI\Network.cl's BatchNorm* kernels 1:1 in DOUBLE precision (closer to the host reference than the float OpenCL kernels): forward with running stats + frozen flag, hidden gradient with the clamp derivative, gamma/beta accumulate, and the batch-mean apply (no weight decay, moments-before-skip ordering, sqrt-stored v). BnDeviceEligible/EnsureBnDeviceBuffers/all four Dispatch* now route by backend; the EXISTING in-situ self-checks (host-vs-device on the first real sample, latch-off + host fallback on mismatch) verify the DLL kernels exactly as they verified OpenCL ones. batch_accum_check regression: ALL CHECKS PASSED on the rebuilt DLL. Same deployment coupling as bd46374: the .ex5 imports the new exports - copy DirectML\WarriorCPU.dll into MQL5\Libraries (terminal closed) together with the new .ex5, and re-copy it to the tester agents (or just run DirectML\build_cpu.bat once with everything closed - it deploys to every discovered Libraries folder). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 20:44:18 -04:00
int CPU_BatchNormForward(long ctx, int iHandle, int oHandle, int optHandle, double w, int frozen, int count);
int CPU_BatchNormHiddenGrad(long ctx, int gHandle, int prevOHandle, int prevGHandle, int optHandle, int activation, int count);
int CPU_BatchNormAccumGammaBeta(long ctx, int gHandle, int optHandle, int accHandle, int count);
int CPU_BatchNormApplyGammaBeta(long ctx, int optHandle, int accHandle, double scale, double lt, double b1v, double b2v, double lr, double momentumRate, int optimizer, int count);
refactor(ai): remove the DirectML/D3D12 GPU compute tier (S1.5) Three backends left, as the operator specified: OpenCL, the CPU DLL, and pure MQL5. CDirectMLMy was a two-tier wrapper (GPU via WarriorDML.dll, CPU via WarriorCPU.dll) whose name only ever named the tier being removed here; the CPU DLL tier - the one actually used on the training machine (no OpenCL, no DirectML) - is untouched. AI/NeuronDirectML.mqh -> AI/ComputeDll.mqh: dropped the DML_* #import block and COMPUTE_TIER_GPU (checked first that nothing persists the enum value and only one external site reads .Tier() - safe), collapsed every tier==CPU?CPU_x():DML_x() ternary to a straight CPU_x() call. Renamed CDirectMLMy->CComputeDll, InitDirectML()->InitComputeDll(), member directml/DirectML->computeDll/ComputeDll across every AI/ file that touched a neuron/net backend plus Topology.mqh/OnlineLearning.mqh. NetBuild.mqh's InitComputeDll also lost the dead D3D12 error-code switch and the now-impossible GPU-tier log branch. Verified via per-file brace-balance diff against HEAD and a whole-repo grep for every removed symbol (CDirectMLMy/InitDirectML/ COMPUTE_TIER_GPU/DML_*) - the only surviving hit is an intentional historical-note comment in the new file's header. DirectML\WarriorDML.cpp/.h and its build scripts are now orphaned C++ source, left in place pending an operator decision. Architecture docs (AI_NETWORK.md, Warrior_EA_System_Overview.md, etc.) still describe the 4-backend/GPU-tier shape and are not updated in this pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:32:09 -04:00
#import
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
enum ENUM_COMPUTE_TIER
{
COMPUTE_TIER_NONE = 0,
COMPUTE_TIER_CPU = 2 // multithreaded fallback - WarriorCPU.dll
};
//+------------------------------------------------------------------+
//| CPU DLL fallback wrapper. Used when OpenCL is unavailable (no HW |
//| accel, e.g. a VM), so callers just get "multithreaded acceleration,|
//| or NULL" without caring about the backend's internals. |
//+------------------------------------------------------------------+
class CComputeDll
{
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 the CPU DLL tier (0 when m_tier==NONE). WarriorCPU.dll
//--- keeps no global state of its own - each CPU_Init() call heap-allocates an independent context
//--- (own thread 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 DLL 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;
//--- CPU_Init()'s failure reason, captured immediately since a failed Init() hands back no context
//--- to read it from afterward - LastError() falls back to this when m_tier is NONE.
int m_initError;
public:
CComputeDll(void) : m_tier(COMPUTE_TIER_NONE), m_cpuLoadPercent(100), m_ctx(0), m_initError(0) {};
~CComputeDll(void)
{
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.
void SetCpuLoadPercent(int pct) { m_cpuLoadPercent = pct; }
ENUM_COMPUTE_TIER Tier(void) { return m_tier; }
//--- Which DLL is actually behind this object, for diagnostics.
string BackendName(void)
{
switch(m_tier)
{
case COMPUTE_TIER_CPU:
return "CPU-DLL";
default:
return "no-backend";
}
}
int CpuThreadsUsed(void) { return (m_tier == COMPUTE_TIER_CPU) ? CPU_GetThreadCount(m_ctx) : 0; }
bool Initialize(void)
{
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;
}
// CPU-DLL init failed - every layer now falls through to the slow plain-MQL5 CPU path with no
// diagnostic trail explaining the resulting behavior/performance change unless this is logged here.
m_initError = -1;
Print(__FUNCTION__ + ": CPU-DLL init failed (CPU_Init returned no context) - falling back to the plain-MQL5 CPU tier.");
m_tier = COMPUTE_TIER_NONE;
return false;
}
int LastError(void) { return (m_tier == COMPUTE_TIER_NONE) ? m_initError : CPU_GetLastError(m_ctx); }
int BufferCreate(int count)
{ return CPU_BufferCreate(m_ctx, count); }
bool BufferWrite(int handle, double &data[], int count)
{ return CPU_BufferWrite(m_ctx, handle, data, count) != 0; }
bool BufferRead(int handle, double &data[], int count)
{ return CPU_BufferRead(m_ctx, handle, data, count) != 0; }
void BufferFree(int handle)
{ CPU_BufferFree(m_ctx, handle); }
bool FeedForward(int wHandle, int iHandle, int oHandle, int inputs, int activation)
{ return CPU_FeedForward(m_ctx, wHandle, iHandle, oHandle, inputs, activation) != 0; }
bool CalcOutputGradient(int tHandle, int oHandle, int igHandle, int activation, int count)
{ return CPU_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 CPU_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, int optimizer)
{ return CPU_UpdateWeightsMomentum(m_ctx, wHandle, gHandle, iHandle, dwHandle, inputs, learningRate, momentumRate, neurons, optimizer) != 0; }
bool UpdateWeightsAdam(int wHandle, int gHandle, int iHandle, int mHandle, int vHandle, int inputs, double lt, double b1v, double b2v, int neurons)
{ return CPU_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 CPU_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 CPU_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, int optimizer)
{ return CPU_UpdateWeightsConvMomentum(m_ctx, wHandle, gHandle, iHandle, dwHandle, inputs, learningRate, momentumRate, windowIn, windowOut, step, optimizer) != 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 CPU_UpdateWeightsConvAdam(m_ctx, wHandle, gHandle, iHandle, mHandle, vHandle, inputs, lt, b1v, b2v, windowIn, windowOut, step) != 0; }
//--- Mini-batch gradient accumulation (2026-08-09 audit, F4). Accumulation only - the optimizer step
//--- is host-side MQL5, so there is no matching Apply* here; see CNeuronBaseOCL::ApplyAccumulatedGradients.
bool AccumulateWeightGrad(int accHandle, int gHandle, int iHandle, int inputs, int neurons)
{ return CPU_AccumulateWeightGrad(m_ctx, accHandle, gHandle, iHandle, inputs, neurons) != 0; }
bool AccumulateWeightGradConv(int accHandle, int gHandle, int iHandle, int inputs, int windowIn, int windowOut, int step)
{ return CPU_AccumulateWeightGradConv(m_ctx, accHandle, gHandle, iHandle, inputs, windowIn, windowOut, step) != 0; }
bool AccumulateBufferInto(int dstHandle, int srcHandle, int count)
{ return CPU_AccumulateBufferInto(m_ctx, dstHandle, srcHandle, count) != 0; }
perf(train): DLL-side mini-batch apply + 300ms slice - the era bottleneck "Hundreds of times slower than a regular EA" decomposed into two multiplied factors, both measured: 1. THE OPTIMIZER STEP RAN IN INTERPRETED MQL5. The CPU tier shipped the F4 accumulate exports with deliberately no matching apply (WarriorCPU.h said so), so on the DLL backend - this box - every TRAIN_BATCH_SIZE=8 batch fell to the host loop in ApplyAccumToBlock: a per-weight MQL5 pass through CBufferDouble.At()/Update() plus four full weight-matrix BufferRead/Write round trips. The 2026-07-26 profile had already shown the per-sample Adam step at 81% of ALL runtime (feedForward: 8%; feature building: 0.35%) - sqrt+divide per weight vs one multiply-add; moving it into MQL5 made it worse. New CPU_ApplyAccumAdam / CPU_ApplyAccumMomentum: one element-wise ParallelFor takes the batch-mean step and zeroes the accumulator DLL-side, generic over any flat block (dense/conv/LSTM/batch-norm - all apply paths funnel through ApplyAccumToBlock, which now tries the DLL first, with the same one-warning failure latch as the OpenCL fast path). Math is the shipped step to the last clamp: sqrt-stored v, ClampDelta, AdamW decay, ClampWeight. batch_accum_check extended (check 6) and ALL PASS: apply == host reference at B=8/B=4, accumulator zeroed, and B=1 accumulate+apply == the unbatched Adam kernel BIT-EXACTLY (kernel-vs-kernel, no transcription). DLL rebuilt with the shipped /fp:fast recipe. 2. A 24% DUTY CYCLE. Train sliced 120ms per 500ms timer period (30ms/member x4), leaving the chart thread idle 76% of the time. Now 300ms total (75ms/member): ~60% duty, ~2.5x, click latency bounded at ~300ms while training runs - between the fully-reactive 120 and the documented "sticky drag" 480. DEPLOYMENT COUPLING: the new .ex5 #imports the new exports, so it will NOT LOAD against the old WarriorCPU.dll ("cannot find function"). Copy DirectML\WarriorCPU.dll into MQL5\Libraries (terminal closed) in the same step as deploying the new .ex5. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 19:32:15 -04:00
//--- Mini-batch apply (2026-08-25): the optimizer step on the batch mean, device-side. See
//--- CPU_ApplyAccumAdam in WarriorCPU.h - without these the step ran as a per-weight MQL5 loop.
bool ApplyAccumAdam(int wHandle, int accHandle, int mHandle, int vHandle, int total, double scale, double lt, double b1v, double b2v)
{ return CPU_ApplyAccumAdam(m_ctx, wHandle, accHandle, mHandle, vHandle, total, scale, lt, b1v, b2v) != 0; }
bool ApplyAccumMomentum(int wHandle, int accHandle, int dwHandle, int total, double scale, double learningRate, double momentumRate)
{ return CPU_ApplyAccumMomentum(m_ctx, wHandle, accHandle, dwHandle, total, scale, learningRate, momentumRate) != 0; }
perf(tester,bn): no sub-second timer in the tester + BN kernels on the DLL tier THE OPTIMIZER ("0.1% an hour per agent", 0 of 39 passes in 78 min, 12 agents): the tester fires OnTimer on SIMULATED time, so the live chart's 500ms EventSetMillisecondTimer over a 2016-2026 pass is ~600 MILLION OnTimer calls - each walking 4x PollTraining, the vote readout's string build, the overlay advance and the deployed census. None of it serves an inference-only pass: training never runs, per-bar inference is driven by OnTickHandler off the tick stream, the risk budget re-checks in OnTick, and there is no chart to keep fresh. StepSetTimer now arms EventSetTimer(3600) in tester/optimizer/forward (~2,600 calls per pass) and keeps the 500ms timer for live charts. Plus a TESTER PASS SELF-PROFILE: per-tick buckets (pre / Expert.OnTick / journal) and the timer total, printed once at the pass's OnDeinit - so if a pass is still slow it names its own consumer instead of being diagnosed from outside. OFFLOAD (operator: "as much calculation as possible to DLL/OpenCL"): batch norm was the ONE stage still host-side on the DLL tier - the device path was OpenCL-only, so every sample crossed the bus twice per BN layer and normalized in interpreted MQL5 (and every model runs batchnorm ON). Four new exports mirror AI\Network.cl's BatchNorm* kernels 1:1 in DOUBLE precision (closer to the host reference than the float OpenCL kernels): forward with running stats + frozen flag, hidden gradient with the clamp derivative, gamma/beta accumulate, and the batch-mean apply (no weight decay, moments-before-skip ordering, sqrt-stored v). BnDeviceEligible/EnsureBnDeviceBuffers/all four Dispatch* now route by backend; the EXISTING in-situ self-checks (host-vs-device on the first real sample, latch-off + host fallback on mismatch) verify the DLL kernels exactly as they verified OpenCL ones. batch_accum_check regression: ALL CHECKS PASSED on the rebuilt DLL. Same deployment coupling as bd46374: the .ex5 imports the new exports - copy DirectML\WarriorCPU.dll into MQL5\Libraries (terminal closed) together with the new .ex5, and re-copy it to the tester agents (or just run DirectML\build_cpu.bat once with everything closed - it deploys to every discovered Libraries folder). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 20:44:18 -04:00
//--- Batch norm (2026-08-25) - see CPU_BatchNormForward in WarriorCPU.h. These are what let a BN
//--- layer run device-side on the DLL tier; the OpenCL kernels remain the device path there.
bool BatchNormForward(int iHandle, int oHandle, int optHandle, double w, int frozen, int count)
{ return CPU_BatchNormForward(m_ctx, iHandle, oHandle, optHandle, w, frozen, count) != 0; }
bool BatchNormHiddenGrad(int gHandle, int prevOHandle, int prevGHandle, int optHandle, int activation, int count)
{ return CPU_BatchNormHiddenGrad(m_ctx, gHandle, prevOHandle, prevGHandle, optHandle, activation, count) != 0; }
bool BatchNormAccumGammaBeta(int gHandle, int optHandle, int accHandle, int count)
{ return CPU_BatchNormAccumGammaBeta(m_ctx, gHandle, optHandle, accHandle, count) != 0; }
bool BatchNormApplyGammaBeta(int optHandle, int accHandle, double scale, double lt, double b1v, double b2v, double lr, double momentumRate, int optimizer, int count)
{ return CPU_BatchNormApplyGammaBeta(m_ctx, optHandle, accHandle, scale, lt, b1v, b2v, lr, momentumRate, optimizer, count) != 0; }
refactor(ai): remove the DirectML/D3D12 GPU compute tier (S1.5) Three backends left, as the operator specified: OpenCL, the CPU DLL, and pure MQL5. CDirectMLMy was a two-tier wrapper (GPU via WarriorDML.dll, CPU via WarriorCPU.dll) whose name only ever named the tier being removed here; the CPU DLL tier - the one actually used on the training machine (no OpenCL, no DirectML) - is untouched. AI/NeuronDirectML.mqh -> AI/ComputeDll.mqh: dropped the DML_* #import block and COMPUTE_TIER_GPU (checked first that nothing persists the enum value and only one external site reads .Tier() - safe), collapsed every tier==CPU?CPU_x():DML_x() ternary to a straight CPU_x() call. Renamed CDirectMLMy->CComputeDll, InitDirectML()->InitComputeDll(), member directml/DirectML->computeDll/ComputeDll across every AI/ file that touched a neuron/net backend plus Topology.mqh/OnlineLearning.mqh. NetBuild.mqh's InitComputeDll also lost the dead D3D12 error-code switch and the now-impossible GPU-tier log branch. Verified via per-file brace-balance diff against HEAD and a whole-repo grep for every removed symbol (CDirectMLMy/InitDirectML/ COMPUTE_TIER_GPU/DML_*) - the only surviving hit is an intentional historical-note comment in the new file's header. DirectML\WarriorDML.cpp/.h and its build scripts are now orphaned C++ source, left in place pending an operator decision. Architecture docs (AI_NETWORK.md, Warrior_EA_System_Overview.md, etc.) still describe the 4-backend/GPU-tier shape and are not updated in this pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:32:09 -04:00
//--- Fused unrolled sequence LSTM. These are what a sequence model uses; the per-step LSTMGates/
//--- LSTMState/LSTM*Gradient below remain only for the legacy single-timestep layer.
bool LSTMSeqForward(int wHandle, int inputsHandle, int cacheGatesHandle, int cacheCellHandle, int cacheHiddenHandle, int outputHandle, int hiddenSize, int stepInputs, int steps)
{ return CPU_LSTMSeqForward(m_ctx, wHandle, inputsHandle, cacheGatesHandle, cacheCellHandle, cacheHiddenHandle, outputHandle, hiddenSize, stepInputs, steps) != 0; }
bool LSTMSeqBackward(int wHandle, int inputsHandle, int cacheGatesHandle, int cacheCellHandle, int cacheHiddenHandle, int outGradientHandle, int weightsGradientHandle, int inputsGradientHandle, int hiddenSize, int stepInputs, int steps)
{ return CPU_LSTMSeqBackward(m_ctx, wHandle, inputsHandle, cacheGatesHandle, cacheCellHandle, cacheHiddenHandle, outGradientHandle, weightsGradientHandle, inputsGradientHandle, hiddenSize, stepInputs, steps) != 0; }
bool LSTMGates(int wHandle, int hiddenPrevHandle, int inputsHandle, int concatenatedHandle, int hiddenSize, int inputSize)
{ return CPU_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 CPU_LSTMState(m_ctx, concatenatedHandle, memoryHandle, hiddenPrevHandle, hiddenCacheHandle, outputHandle, hiddenSize) != 0; }
bool LSTMGateGradient(int gradientHandle, int memoryHandle, int concatenatedHandle, int concatenatedGradientHandle, int hiddenSize)
{ return CPU_LSTMGateGradient(m_ctx, gradientHandle, memoryHandle, concatenatedHandle, concatenatedGradientHandle, hiddenSize) != 0; }
bool LSTMWeightsGradient(int concatenatedGradientHandle, int hiddenCacheHandle, int inputsHandle, int weightsGradientHandle, int hiddenSize, int inputSize)
{ return CPU_LSTMWeightsGradient(m_ctx, concatenatedGradientHandle, hiddenCacheHandle, inputsHandle, weightsGradientHandle, hiddenSize, inputSize) != 0; }
bool LSTMInputsGradient(int concatenatedGradientHandle, int wHandle, int inputsGradientHandle, int hiddenSize, int inputSize)
{ return CPU_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 CPU_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, int optimizer)
{ return CPU_LSTMUpdateWeightsMomentum(m_ctx, wHandle, weightsGradientHandle, dwHandle, learningRate, momentumRate, total, optimizer) != 0; }
bool FeedForwardProof(int iHandle, int oHandle, int inputs, int window, int step, int outputs)
{ return CPU_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 CPU_CalcInputGradientProof(m_ctx, iHandle, gHandle, oHandle, igHandle, outputs, window, step, inputs) != 0; }
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+