2026-08-01 11:27:28 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| NeuronOCLBase.mqh |
|
|
|
|
|
//| |
|
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
|
|
|
//| CNeuronBaseOCL - the accelerated dense neuron (OpenCL / CPU-DLL |
|
|
|
|
|
//| tiers). |
|
2026-08-01 11:27:28 -04:00
|
|
|
//| |
|
|
|
|
|
//| Included from AI\Network.mqh AFTER every class declaration - |
|
|
|
|
|
//| bodies only, no declarations. Relocation is behaviour-neutral by |
|
|
|
|
|
//| construction: nothing here is reachable until Network.mqh ends. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#ifndef WARRIOR_AI_IMPL_NEURONOCLBASE_MQH
|
|
|
|
|
#define WARRIOR_AI_IMPL_NEURONOCLBASE_MQH
|
feat(rng): ALGLIB's L'Ecuyer generator replaces MathRand, and a seed collision goes with it
MQL5's MathRand() is the 15-bit MSVC LCG - 32768 distinct values and
the lattice structure that shape of generator has. Two places here
actually lean on randomness and both were hurt by it:
WEIGHT INIT. Six He/LeCun-uniform sites drew
((MathRand()+1)/32768.0 - 0.5) * 2 * scale, so a first dense layer of
~250k weights had only 32768 possible values and thousands of
connections started byte-identical. Breaking that symmetry is the whole
job of random init.
SHUFFLING. ShuffleRandomIndex() already had to splice TWO MathRand()
draws to reach 30 bits, and its own comment documented the residual
modulo bias it still carried. HQRndUniformI() is rejection-sampled and
exactly uniform, so the splice and the bias note both go.
CHighQualityRand is L'Ecuyer's combined multiplicative congruential
generator - two differenced streams, 31-bit output, period ~2.3e18 -
and it ships with the terminal.
AND A BUG THE MIGRATION EXPOSED. The three MathSrand(GetTickCount())
calls sit immediately before "build a fresh topology", once per model.
GetTickCount() steps in ~15.6 ms on Windows and an ensemble builds every
member inside one OnInit, so members could be handed the SAME seed and
draw the SAME weights wherever their shapes coincide - and members that
start identical are not an ensemble. WarriorRandSeed() takes a salt (the
model id) plus a never-reset call counter, so a collision is impossible
rather than merely unlikely, while the tick keeps the run itself
genuinely unrepeatable the way those call sites asked for.
Seeds are masked positive rather than trusted: HQRndSeed computes
s % (M-1) + 1 and MQL5's % keeps the sign, so a negative seed leaves the
generator in a state its own assertions reject. GetTickCount() is a uint
and goes negative as an int after ~24 days of uptime - a fault that
would surface as "training is broken" on a long-running terminal and
nowhere else.
The indicator tuner's 52 draws move across too: its random search is
where sample quality earns its keep.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 00:29:12 -04:00
|
|
|
#include "..\..\System\Random.mqh"
|
2026-08-01 11:27:28 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
CNeuronBaseOCL::CNeuronBaseOCL(void) : alpha(SgdMomentum),
|
2026-08-01 11:27:28 -04:00
|
|
|
activation(TANH),
|
|
|
|
|
optimization(SGD),
|
|
|
|
|
t(1)
|
|
|
|
|
{
|
|
|
|
|
OpenCL = NULL;
|
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 = NULL;
|
2026-08-01 11:27:28 -04:00
|
|
|
Output = new CBufferDouble();
|
|
|
|
|
PrevOutput = new CBufferDouble();
|
|
|
|
|
Weights = new CBufferDouble();
|
|
|
|
|
DeltaWeights = new CBufferDouble();
|
|
|
|
|
Gradient = new CBufferDouble();
|
|
|
|
|
FirstMomentum = new CBufferDouble();
|
|
|
|
|
SecondMomentum = new CBufferDouble();
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
//--- Allocated lazily by EnsureGradAccum() only if a batched update ever runs - see its declaration.
|
|
|
|
|
GradAccum = NULL;
|
2026-08-01 11:27:28 -04:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
CNeuronBaseOCL::~CNeuronBaseOCL(void)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(Output) != POINTER_INVALID)
|
|
|
|
|
delete Output;
|
|
|
|
|
if(CheckPointer(PrevOutput) != POINTER_INVALID)
|
|
|
|
|
delete PrevOutput;
|
|
|
|
|
if(CheckPointer(Weights) != POINTER_INVALID)
|
|
|
|
|
delete Weights;
|
|
|
|
|
if(CheckPointer(DeltaWeights) != POINTER_INVALID)
|
|
|
|
|
delete DeltaWeights;
|
|
|
|
|
if(CheckPointer(Gradient) != POINTER_INVALID)
|
|
|
|
|
delete Gradient;
|
|
|
|
|
if(CheckPointer(FirstMomentum) != POINTER_INVALID)
|
|
|
|
|
delete FirstMomentum;
|
|
|
|
|
if(CheckPointer(SecondMomentum) != POINTER_INVALID)
|
|
|
|
|
delete SecondMomentum;
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
if(CheckPointer(GradAccum) != POINTER_INVALID)
|
|
|
|
|
delete GradAccum;
|
2026-08-01 11:27:28 -04:00
|
|
|
OpenCL = NULL;
|
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 = NULL;
|
2026-08-01 11:27:28 -04:00
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint numNeurons, ENUM_OPTIMIZATION optimization_type)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(open_cl) == POINTER_INVALID || numNeurons <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
OpenCL = open_cl;
|
|
|
|
|
optimization = optimization_type;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Output = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!Output.BufferInit(numNeurons, 1.0))
|
|
|
|
|
return false;
|
|
|
|
|
if(!Output.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
PrevOutput = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!PrevOutput.BufferInit(numNeurons, 1.0))
|
|
|
|
|
return false;
|
|
|
|
|
if(!PrevOutput.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Gradient = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!Gradient.BufferInit(numNeurons + 1, 0.0))
|
|
|
|
|
return false;
|
|
|
|
|
if(!Gradient.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(numOutputs > 0)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Weights = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int count = (int)((numNeurons + 1) * numOutputs);
|
|
|
|
|
if(!Weights.Reserve(count))
|
|
|
|
|
return false;
|
|
|
|
|
// He-scaled init: k=sqrt(2/fan_in), weight drawn uniform in [-k,k] (variance-matched to He
|
|
|
|
|
// et al.'s normal-distribution formulation, just uniform instead of Gaussian - same as the
|
|
|
|
|
// LeCun-uniform scheme this replaced, which used the same uniform-draw convention with a
|
|
|
|
|
// 1/sqrt(fan_in+1) scale). BuildFreshTopology() puts every hidden layer on PRELU (leaky
|
|
|
|
|
// ReLU family) - He is the variant actually derived for ReLU-family activations, accounting
|
|
|
|
|
// for the fact that they zero out roughly half their input distribution, whereas the
|
|
|
|
|
// previous LeCun-uniform scale was tuned for tanh/sigmoid-style saturating activations and
|
|
|
|
|
// was ~2x too conservative here. Applied to every layer through this one shared Init()
|
|
|
|
|
// (input/output included, not just hidden) rather than threading ENUM_ACTIVATION through -
|
|
|
|
|
// the output layer is only m_outputNeuronsCount (3) neurons wide, where fan-in barely
|
|
|
|
|
// differs from the old scale's, and MAX_WEIGHT/MAX_WEIGHT_DELTA already clip any resulting
|
|
|
|
|
// extremes on every backend, so the imprecision there is not worth the much larger, riskier
|
|
|
|
|
// change of threading activation awareness through every neuron subtype's Init() overload.
|
|
|
|
|
double weighScale = MathSqrt(2.0 / ((double)numNeurons + 1.0));
|
|
|
|
|
for(int i = 0; i < count; i++)
|
|
|
|
|
{
|
feat(rng): ALGLIB's L'Ecuyer generator replaces MathRand, and a seed collision goes with it
MQL5's MathRand() is the 15-bit MSVC LCG - 32768 distinct values and
the lattice structure that shape of generator has. Two places here
actually lean on randomness and both were hurt by it:
WEIGHT INIT. Six He/LeCun-uniform sites drew
((MathRand()+1)/32768.0 - 0.5) * 2 * scale, so a first dense layer of
~250k weights had only 32768 possible values and thousands of
connections started byte-identical. Breaking that symmetry is the whole
job of random init.
SHUFFLING. ShuffleRandomIndex() already had to splice TWO MathRand()
draws to reach 30 bits, and its own comment documented the residual
modulo bias it still carried. HQRndUniformI() is rejection-sampled and
exactly uniform, so the splice and the bias note both go.
CHighQualityRand is L'Ecuyer's combined multiplicative congruential
generator - two differenced streams, 31-bit output, period ~2.3e18 -
and it ships with the terminal.
AND A BUG THE MIGRATION EXPOSED. The three MathSrand(GetTickCount())
calls sit immediately before "build a fresh topology", once per model.
GetTickCount() steps in ~15.6 ms on Windows and an ensemble builds every
member inside one OnInit, so members could be handed the SAME seed and
draw the SAME weights wherever their shapes coincide - and members that
start identical are not an ensemble. WarriorRandSeed() takes a salt (the
model id) plus a never-reset call counter, so a collision is impossible
rather than merely unlikely, while the tick keeps the run itself
genuinely unrepeatable the way those call sites asked for.
Seeds are masked positive rather than trusted: HQRndSeed computes
s % (M-1) + 1 and MQL5's % keeps the sign, so a negative seed leaves the
generator in a state its own assertions reject. GetTickCount() is a uint
and goes negative as an int after ~24 days of uptime - a fault that
would surface as "training is broken" on a long-running terminal and
nowhere else.
The indicator tuner's 52 draws move across too: its random search is
where sample quality earns its keep.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 00:29:12 -04:00
|
|
|
double weigh = WarriorRandSymmetric() * weighScale;
|
2026-08-01 11:27:28 -04:00
|
|
|
if(weigh == 0)
|
|
|
|
|
weigh = 0.001;
|
|
|
|
|
if(!Weights.Add(weigh))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!Weights.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(optimization == SGD)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
DeltaWeights = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!DeltaWeights.BufferInit(count, 0))
|
|
|
|
|
return false;
|
|
|
|
|
if(!DeltaWeights.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
if(CheckPointer(FirstMomentum) != POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
delete FirstMomentum;
|
|
|
|
|
FirstMomentum = NULL;
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(SecondMomentum) != POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
delete SecondMomentum;
|
|
|
|
|
SecondMomentum = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(DeltaWeights) != POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
delete DeltaWeights;
|
|
|
|
|
DeltaWeights = NULL;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
FirstMomentum = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!FirstMomentum.BufferInit(count, 0))
|
|
|
|
|
return false;
|
|
|
|
|
if(!FirstMomentum.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
SecondMomentum = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!SecondMomentum.BufferInit(count, 0))
|
|
|
|
|
return false;
|
|
|
|
|
if(!SecondMomentum.BufferCreate(OpenCL))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(Weights) != POINTER_INVALID)
|
|
|
|
|
delete Weights;
|
|
|
|
|
if(CheckPointer(DeltaWeights) != POINTER_INVALID)
|
|
|
|
|
delete DeltaWeights;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
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
|
|
|
//| CPU-DLL tier equivalent of Init(COpenCLMy*) above - same |
|
|
|
|
|
//| buffer layout, buffers just get created on the CPU-DLL backend. |
|
2026-08-01 11:27:28 -04:00
|
|
|
//+------------------------------------------------------------------+
|
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
|
|
|
bool CNeuronBaseOCL::Init(uint numOutputs, uint myIndex, CComputeDll *compute_dll, uint numNeurons, ENUM_OPTIMIZATION optimization_type)
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
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
|
|
|
if(CheckPointer(compute_dll) == POINTER_INVALID || numNeurons <= 0)
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
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 = compute_dll;
|
2026-08-01 11:27:28 -04:00
|
|
|
optimization = optimization_type;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Output = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!Output.BufferInit(numNeurons, 1.0))
|
|
|
|
|
return false;
|
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
|
|
|
if(!Output.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
PrevOutput = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!PrevOutput.BufferInit(numNeurons, 1.0))
|
|
|
|
|
return false;
|
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
|
|
|
if(!PrevOutput.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Gradient = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!Gradient.BufferInit(numNeurons + 1, 0.0))
|
|
|
|
|
return false;
|
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
|
|
|
if(!Gradient.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(numOutputs > 0)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Weights = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int count = (int)((numNeurons + 1) * numOutputs);
|
|
|
|
|
if(!Weights.Reserve(count))
|
|
|
|
|
return false;
|
|
|
|
|
// He-scaled init - see the matching OpenCL Init() overload above for the full rationale.
|
|
|
|
|
double weighScale = MathSqrt(2.0 / ((double)numNeurons + 1.0));
|
|
|
|
|
for(int i = 0; i < count; i++)
|
|
|
|
|
{
|
feat(rng): ALGLIB's L'Ecuyer generator replaces MathRand, and a seed collision goes with it
MQL5's MathRand() is the 15-bit MSVC LCG - 32768 distinct values and
the lattice structure that shape of generator has. Two places here
actually lean on randomness and both were hurt by it:
WEIGHT INIT. Six He/LeCun-uniform sites drew
((MathRand()+1)/32768.0 - 0.5) * 2 * scale, so a first dense layer of
~250k weights had only 32768 possible values and thousands of
connections started byte-identical. Breaking that symmetry is the whole
job of random init.
SHUFFLING. ShuffleRandomIndex() already had to splice TWO MathRand()
draws to reach 30 bits, and its own comment documented the residual
modulo bias it still carried. HQRndUniformI() is rejection-sampled and
exactly uniform, so the splice and the bias note both go.
CHighQualityRand is L'Ecuyer's combined multiplicative congruential
generator - two differenced streams, 31-bit output, period ~2.3e18 -
and it ships with the terminal.
AND A BUG THE MIGRATION EXPOSED. The three MathSrand(GetTickCount())
calls sit immediately before "build a fresh topology", once per model.
GetTickCount() steps in ~15.6 ms on Windows and an ensemble builds every
member inside one OnInit, so members could be handed the SAME seed and
draw the SAME weights wherever their shapes coincide - and members that
start identical are not an ensemble. WarriorRandSeed() takes a salt (the
model id) plus a never-reset call counter, so a collision is impossible
rather than merely unlikely, while the tick keeps the run itself
genuinely unrepeatable the way those call sites asked for.
Seeds are masked positive rather than trusted: HQRndSeed computes
s % (M-1) + 1 and MQL5's % keeps the sign, so a negative seed leaves the
generator in a state its own assertions reject. GetTickCount() is a uint
and goes negative as an int after ~24 days of uptime - a fault that
would surface as "training is broken" on a long-running terminal and
nowhere else.
The indicator tuner's 52 draws move across too: its random search is
where sample quality earns its keep.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 00:29:12 -04:00
|
|
|
double weigh = WarriorRandSymmetric() * weighScale;
|
2026-08-01 11:27:28 -04:00
|
|
|
if(weigh == 0)
|
|
|
|
|
weigh = 0.001;
|
|
|
|
|
if(!Weights.Add(weigh))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
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
|
|
|
if(!Weights.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(optimization == SGD)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
DeltaWeights = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!DeltaWeights.BufferInit(count, 0))
|
|
|
|
|
return false;
|
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
|
|
|
if(!DeltaWeights.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
if(CheckPointer(FirstMomentum) != POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
delete FirstMomentum;
|
|
|
|
|
FirstMomentum = NULL;
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(SecondMomentum) != POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
delete SecondMomentum;
|
|
|
|
|
SecondMomentum = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(DeltaWeights) != POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
delete DeltaWeights;
|
|
|
|
|
DeltaWeights = NULL;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
FirstMomentum = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!FirstMomentum.BufferInit(count, 0))
|
|
|
|
|
return false;
|
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
|
|
|
if(!FirstMomentum.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
SecondMomentum = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!SecondMomentum.BufferInit(count, 0))
|
|
|
|
|
return false;
|
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
|
|
|
if(!SecondMomentum.BufferCreate(ComputeDll))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(Weights) != POINTER_INVALID)
|
|
|
|
|
delete Weights;
|
|
|
|
|
if(CheckPointer(DeltaWeights) != POINTER_INVALID)
|
|
|
|
|
delete DeltaWeights;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::feedForward(CObject *SourceObject)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(SourceObject) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
CNeuronBaseOCL *temp = NULL;
|
|
|
|
|
switch(SourceObject.Type())
|
|
|
|
|
{
|
|
|
|
|
case defNeuronBaseOCL:
|
|
|
|
|
case defNeuronConvOCL:
|
|
|
|
|
case defNeuronLSTMOCL:
|
|
|
|
|
case defNeuronPoolOCL:
|
|
|
|
|
case defNeuronBatchNormOCL:
|
|
|
|
|
temp = SourceObject;
|
|
|
|
|
return feedForward(temp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::feedForward(CNeuronBaseOCL *NeuronOCL)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(NeuronOCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
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
|
|
|
if(CheckPointer(ComputeDll) != POINTER_INVALID)
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
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
|
|
|
if(!ComputeDll.FeedForward(NeuronOCL.getWeightsIndex(), NeuronOCL.getOutputIndex(), Output.GetIndex(),
|
2026-08-01 11:27:28 -04:00
|
|
|
NeuronOCL.Neurons(), NativeActivationCode(activation)))
|
|
|
|
|
{
|
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
|
|
|
Print(__FUNCTION__ + ": " + ComputeDll.BackendName() + " FeedForward failed, error " + IntegerToString(ComputeDll.LastError()));
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return Output.BufferRead();
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(OpenCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
uint global_work_offset[1] = {0};
|
|
|
|
|
uint global_work_size[1];
|
|
|
|
|
global_work_size[0] = Output.Total();
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_FeedForward, def_k_ff_matrix_w, NeuronOCL.getWeightsIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_FeedForward, def_k_ff_matrix_i, NeuronOCL.getOutputIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_FeedForward, def_k_ff_matrix_o, Output.GetIndex());
|
|
|
|
|
OpenCL.SetArgument(def_k_FeedForward, def_k_ff_inputs, NeuronOCL.Neurons());
|
|
|
|
|
OpenCL.SetArgument(def_k_FeedForward, def_k_ff_activation, NativeActivationCode(activation));
|
|
|
|
|
if(!OpenCL.Execute(def_k_FeedForward, 1, global_work_offset, global_work_size))
|
|
|
|
|
{
|
|
|
|
|
printf("Error of execution kernel FeedForward: %d", GetLastError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//--- Output stays GPU-resident; the next layer's feedForward reads it via getOutputIndex()
|
|
|
|
|
//--- (a device buffer handle), never through this CPU mirror. Any caller that does need the
|
|
|
|
|
//--- host-side array (getResults(), backPropOCL()'s error-metric read) goes through
|
|
|
|
|
//--- getOutputVal()/GetData(), which calls BufferRead() itself - see CBufferDouble::GetData().
|
|
|
|
|
//--- Eagerly reading here on every layer, every sample was pure host<->device sync overhead.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Pure-MQL5 dense forward pass - exact double-precision mirror of |
|
|
|
|
|
//| Network.cl's FeedForward kernel. NeuronOCL is the PREVIOUS layer, |
|
|
|
|
|
//| which (Gizlyk convention) owns both the inputs (its Output) and |
|
|
|
|
|
//| the weights connecting them to THIS layer (its Weights), laid out |
|
|
|
|
|
//| [thisNeuron][prevNeurons+1] with the +1 bias last. Reads host |
|
|
|
|
|
//| buffers only; used when no compute backend exists (CPU inference).|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::feedForwardCPU(CNeuronBaseOCL *NeuronOCL)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(NeuronOCL) == POINTER_INVALID || CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
int inputs = NeuronOCL.Neurons();
|
|
|
|
|
int outCount = Output.Total();
|
|
|
|
|
int wTotal = NeuronOCL.WeightsCount();
|
|
|
|
|
if(wTotal < (inputs + 1) * outCount)
|
|
|
|
|
return false; // weight buffer smaller than the dense layout requires - refuse rather than misread
|
|
|
|
|
for(int i = 0; i < outCount; i++)
|
|
|
|
|
{
|
|
|
|
|
int shift = (inputs + 1) * i;
|
|
|
|
|
double sum = 0.0;
|
|
|
|
|
for(int k = 0; k < inputs; k++)
|
|
|
|
|
sum += NeuronOCL.OutputHost(k) * NeuronOCL.WeightHost(shift + k);
|
|
|
|
|
sum += NeuronOCL.WeightHost(shift + inputs); // bias
|
|
|
|
|
switch(activation)
|
|
|
|
|
{
|
|
|
|
|
case TANH:
|
|
|
|
|
sum = tanh(sum);
|
|
|
|
|
break;
|
|
|
|
|
case SIGMOID:
|
|
|
|
|
sum = 1.0 / (1.0 + exp(-MathMax(-50.0, MathMin(50.0, sum))));
|
|
|
|
|
break;
|
|
|
|
|
case PRELU:
|
|
|
|
|
if(sum < 0.0)
|
|
|
|
|
sum *= 0.01;
|
|
|
|
|
break;
|
|
|
|
|
// NONE (raw logits): identity - matches NativeActivationCode()'s -1/default kernel case.
|
|
|
|
|
}
|
|
|
|
|
if(!Output.Update(i, sum))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Host-side input write for the CPU inference path's layer 0 (there |
|
|
|
|
|
//| is no device buffer to write into, unlike CNet::feedForward's |
|
|
|
|
|
//| BufferWrite branch). Copies inputVals into the Output host array. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::SetInputsCPU(CArrayDouble *inputVals)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(inputVals) == POINTER_INVALID || CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
int total = MathMin(Output.Total(), inputVals.Total());
|
|
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
if(!Output.Update(i, inputVals.At(i)))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Host-side output read for getResults() on the CPU inference path |
|
|
|
|
|
//| (no device BufferRead available). Returns the count copied. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
int CNeuronBaseOCL::GetOutputsCPU(CArrayDouble *values)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(values) == POINTER_INVALID || CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
return 0;
|
|
|
|
|
values.Clear();
|
|
|
|
|
int n = Output.Total();
|
|
|
|
|
for(int i = 0; i < n; i++)
|
|
|
|
|
if(!values.Add(Output.At(i)))
|
|
|
|
|
return i;
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::calcHiddenGradients(CNeuronBaseOCL *NeuronOCL)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(NeuronOCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
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
|
|
|
if(CheckPointer(ComputeDll) != POINTER_INVALID)
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
fix: dense backprop read the weight matrix transposed - on every backend
CaclHiddenGradient computed this layer's gradient as matrix_w[(outputs+1)*i + k]
against a buffer whose actual layout (one row per NEXT-layer neuron, stride
inputs+1) makes the correct read matrix_w[k*(inputs+1) + i]: the transpose for
square layers, and for the non-square boundaries this EA actually builds
(tapered stacks, the 3-neuron head) a mis-strided walk that ran past the buffer
end - garbage on OpenCL, zeroed reads on the CPU DLL, so the tiers did not even
agree with each other. Every gradient crossing a dense boundary on its way down
- the entire learning signal reaching the BN/conv/LSTM front ends - passed
through a fixed wrong matrix: feedback-alignment dynamics, not backprop, which
is why nets still "learned something" and this survived. The book reference
(NeuroNet_DNG) fixed this in a later article version; our kernel descended from
the earlier one. Confounds every model-based negative verdict to date.
Also in this commit, same root cause family:
- per-sample UpdateWeightsAdam (OpenCL): input for slot group j was read at
matrix_i[j] instead of matrix_i[j*4] (corrupted outer product past group 0),
and dispatch dim 1 sized on ceil(inputs/4) left the bias column unreachable
whenever inputs%4==0 - dense biases never trained on OpenCL. Rewritten as a
lane-guarded scalar loop keeping our Adam conventions (sqrt-stored v,
decoupled decay, both clamps, no sign gate). The batched accum path never had
either bug; this kernel is what SetBatchSize(1) runs - including online
continual learning on client machines, where OpenCL is the only tier.
- conv backward passed raw (int)Activation() where the kernels expect
NativeActivationCode(): NONE took the tanh branch (clamping a BN layer's
unbounded z-scores), TANH took sigmoid, PRELU took none. Dormant only because
the conv sits at layer 1 today.
- hidden-gradient dispatch over Neurons()+1 dropped to Neurons(): biases get no
backprop gradient and the extra work-item only ever read past matrix_o.
All three backends (Network.cl, WarriorCPU.cpp, WarriorDML.cpp HLSL) changed in
lockstep; DML gained an `inputs` constant to derive the row stride. New
dense_backprop_check.cpp proves the CPU kernel is central-finite-difference
consistent with the real forward kernel on 8x8, 64x3, 33x64, 5x3 (max diff
3e-9) and that all three activation branches match transcription. All 16 checks
pass. Offline math check only - the in-situ proof remains the per-layer dW/W
report on a real era.
FORCES FULL RETRAIN. Both DLLs rebuilt and redeployed to MQL5\Libraries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:06:09 -04:00
|
|
|
//--- count is exactly Neurons() - biases receive no backprop gradient, and the DLL needs the
|
|
|
|
|
//--- true neuron count to derive the weight-row stride (inputs + 1). The old Neurons() + 1
|
|
|
|
|
//--- "bias row" only ever produced out-of-bounds reads (see the 2026-08-11 transpose fix in
|
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
|
|
|
//--- the kernels: Network.cl CaclHiddenGradient, WarriorCPU.cpp CalcHiddenGradient).
|
|
|
|
|
if(!ComputeDll.CalcHiddenGradient(getWeightsIndex(), NeuronOCL.getGradientIndex(), getOutputIndex(), getGradientIndex(),
|
fix: dense backprop read the weight matrix transposed - on every backend
CaclHiddenGradient computed this layer's gradient as matrix_w[(outputs+1)*i + k]
against a buffer whose actual layout (one row per NEXT-layer neuron, stride
inputs+1) makes the correct read matrix_w[k*(inputs+1) + i]: the transpose for
square layers, and for the non-square boundaries this EA actually builds
(tapered stacks, the 3-neuron head) a mis-strided walk that ran past the buffer
end - garbage on OpenCL, zeroed reads on the CPU DLL, so the tiers did not even
agree with each other. Every gradient crossing a dense boundary on its way down
- the entire learning signal reaching the BN/conv/LSTM front ends - passed
through a fixed wrong matrix: feedback-alignment dynamics, not backprop, which
is why nets still "learned something" and this survived. The book reference
(NeuroNet_DNG) fixed this in a later article version; our kernel descended from
the earlier one. Confounds every model-based negative verdict to date.
Also in this commit, same root cause family:
- per-sample UpdateWeightsAdam (OpenCL): input for slot group j was read at
matrix_i[j] instead of matrix_i[j*4] (corrupted outer product past group 0),
and dispatch dim 1 sized on ceil(inputs/4) left the bias column unreachable
whenever inputs%4==0 - dense biases never trained on OpenCL. Rewritten as a
lane-guarded scalar loop keeping our Adam conventions (sqrt-stored v,
decoupled decay, both clamps, no sign gate). The batched accum path never had
either bug; this kernel is what SetBatchSize(1) runs - including online
continual learning on client machines, where OpenCL is the only tier.
- conv backward passed raw (int)Activation() where the kernels expect
NativeActivationCode(): NONE took the tanh branch (clamping a BN layer's
unbounded z-scores), TANH took sigmoid, PRELU took none. Dormant only because
the conv sits at layer 1 today.
- hidden-gradient dispatch over Neurons()+1 dropped to Neurons(): biases get no
backprop gradient and the extra work-item only ever read past matrix_o.
All three backends (Network.cl, WarriorCPU.cpp, WarriorDML.cpp HLSL) changed in
lockstep; DML gained an `inputs` constant to derive the row stride. New
dense_backprop_check.cpp proves the CPU kernel is central-finite-difference
consistent with the real forward kernel on 8x8, 64x3, 33x64, 5x3 (max diff
3e-9) and that all three activation branches match transcription. All 16 checks
pass. Offline math check only - the in-situ proof remains the per-layer dW/W
report on a real era.
FORCES FULL RETRAIN. Both DLLs rebuilt and redeployed to MQL5\Libraries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:06:09 -04:00
|
|
|
NeuronOCL.Neurons(), NativeActivationCode(activation), Neurons()))
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
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
|
|
|
Print(__FUNCTION__ + ": " + ComputeDll.BackendName() + " CalcHiddenGradient failed, error " + IntegerToString(ComputeDll.LastError()));
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return Gradient.BufferRead();
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(OpenCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
uint global_work_offset[1] = {0};
|
|
|
|
|
uint global_work_size[1];
|
fix: dense backprop read the weight matrix transposed - on every backend
CaclHiddenGradient computed this layer's gradient as matrix_w[(outputs+1)*i + k]
against a buffer whose actual layout (one row per NEXT-layer neuron, stride
inputs+1) makes the correct read matrix_w[k*(inputs+1) + i]: the transpose for
square layers, and for the non-square boundaries this EA actually builds
(tapered stacks, the 3-neuron head) a mis-strided walk that ran past the buffer
end - garbage on OpenCL, zeroed reads on the CPU DLL, so the tiers did not even
agree with each other. Every gradient crossing a dense boundary on its way down
- the entire learning signal reaching the BN/conv/LSTM front ends - passed
through a fixed wrong matrix: feedback-alignment dynamics, not backprop, which
is why nets still "learned something" and this survived. The book reference
(NeuroNet_DNG) fixed this in a later article version; our kernel descended from
the earlier one. Confounds every model-based negative verdict to date.
Also in this commit, same root cause family:
- per-sample UpdateWeightsAdam (OpenCL): input for slot group j was read at
matrix_i[j] instead of matrix_i[j*4] (corrupted outer product past group 0),
and dispatch dim 1 sized on ceil(inputs/4) left the bias column unreachable
whenever inputs%4==0 - dense biases never trained on OpenCL. Rewritten as a
lane-guarded scalar loop keeping our Adam conventions (sqrt-stored v,
decoupled decay, both clamps, no sign gate). The batched accum path never had
either bug; this kernel is what SetBatchSize(1) runs - including online
continual learning on client machines, where OpenCL is the only tier.
- conv backward passed raw (int)Activation() where the kernels expect
NativeActivationCode(): NONE took the tanh branch (clamping a BN layer's
unbounded z-scores), TANH took sigmoid, PRELU took none. Dormant only because
the conv sits at layer 1 today.
- hidden-gradient dispatch over Neurons()+1 dropped to Neurons(): biases get no
backprop gradient and the extra work-item only ever read past matrix_o.
All three backends (Network.cl, WarriorCPU.cpp, WarriorDML.cpp HLSL) changed in
lockstep; DML gained an `inputs` constant to derive the row stride. New
dense_backprop_check.cpp proves the CPU kernel is central-finite-difference
consistent with the real forward kernel on 8x8, 64x3, 33x64, 5x3 (max diff
3e-9) and that all three activation branches match transcription. All 16 checks
pass. Offline math check only - the in-situ proof remains the per-layer dW/W
report on a real era.
FORCES FULL RETRAIN. Both DLLs rebuilt and redeployed to MQL5\Libraries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:06:09 -04:00
|
|
|
//--- Exactly Neurons(): the kernel derives the weight-row stride from get_global_size(0), and
|
|
|
|
|
//--- biases receive no backprop gradient (the old +1 work-item only ever read past matrix_o).
|
|
|
|
|
global_work_size[0] = Neurons();
|
2026-08-01 11:27:28 -04:00
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclHiddenGradient, def_k_chg_matrix_w, getWeightsIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclHiddenGradient, def_k_chg_matrix_g, NeuronOCL.getGradientIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclHiddenGradient, def_k_chg_matrix_o, getOutputIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclHiddenGradient, def_k_chg_matrix_ig, getGradientIndex());
|
|
|
|
|
OpenCL.SetArgument(def_k_CaclHiddenGradient, def_k_chg_outputs, NeuronOCL.Neurons());
|
|
|
|
|
OpenCL.SetArgument(def_k_CaclHiddenGradient, def_k_chg_activation, NativeActivationCode(activation));
|
|
|
|
|
if(!OpenCL.Execute(def_k_CaclHiddenGradient, 1, global_work_offset, global_work_size))
|
|
|
|
|
{
|
|
|
|
|
printf("Error of execution kernel CaclHiddenGradient: %d", GetLastError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//--- Gradient stays GPU-resident (consumed by the previous layer via getGradientIndex()); see
|
|
|
|
|
//--- the note in feedForward() above - self-syncing GetData() covers any real host consumer.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::calcOutputGradients(CArrayDouble *Target)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(Target) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
int count = Target.Total();
|
|
|
|
|
for(int i = 0; i < count; i++)
|
|
|
|
|
if(!Gradient.Update(i, Target.At(i)))
|
|
|
|
|
return false;
|
|
|
|
|
Gradient.BufferWrite();
|
|
|
|
|
//--- note: Gradient is reused here as matrix_t (target) below, exactly as the OpenCL path does
|
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
|
|
|
if(CheckPointer(ComputeDll) != POINTER_INVALID)
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
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
|
|
|
if(!ComputeDll.CalcOutputGradient(getGradientIndex(), getOutputIndex(), getGradientIndex(), NativeActivationCode(activation), count))
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
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
|
|
|
Print(__FUNCTION__ + ": " + ComputeDll.BackendName() + " CalcOutputGradient failed, error " + IntegerToString(ComputeDll.LastError()));
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return Gradient.BufferRead();
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(OpenCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
uint global_work_offset[1] = {0};
|
|
|
|
|
uint global_work_size[1];
|
|
|
|
|
global_work_size[0] = count;
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclOutputGradient, def_k_cog_matrix_t, getGradientIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclOutputGradient, def_k_cog_matrix_o, getOutputIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_CaclOutputGradient, def_k_cog_matrix_ig, getGradientIndex());
|
|
|
|
|
OpenCL.SetArgument(def_k_CaclOutputGradient, def_k_cog_activation, NativeActivationCode(activation));
|
|
|
|
|
ResetLastError();
|
|
|
|
|
if(!OpenCL.Execute(def_k_CaclOutputGradient, 1, global_work_offset, global_work_size))
|
|
|
|
|
{
|
|
|
|
|
printf("Error of execution kernel CaclOutputGradient: %d", GetLastError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//--- backPropOCL()'s sampleWeight scaling reads this via getGradient()/GetData(), which
|
|
|
|
|
//--- BufferRead()s itself - see the note in feedForward() above.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(NeuronOCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
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
|
|
|
if(CheckPointer(ComputeDll) != POINTER_INVALID)
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
|
|
|
|
int inputs = NeuronOCL.Neurons();
|
|
|
|
|
int neurons = Neurons();
|
|
|
|
|
if(optimization == SGD)
|
|
|
|
|
{
|
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
|
|
|
if(!ComputeDll.UpdateWeightsMomentum(NeuronOCL.getWeightsIndex(), getGradientIndex(), NeuronOCL.getOutputIndex(),
|
2026-08-20 07:00:09 -04:00
|
|
|
NeuronOCL.getDeltaWeightsIndex(), inputs, g_eta, alpha, neurons,
|
2026-08-01 11:27:28 -04:00
|
|
|
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
|
|
|
Print(__FUNCTION__ + ": " + ComputeDll.BackendName() + " UpdateWeightsMomentum failed, error " + IntegerToString(ComputeDll.LastError()));
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-08-20 07:00:09 -04:00
|
|
|
double lt = g_eta * sqrt(1 - pow(AdamBeta2, t)) / (1 - pow(AdamBeta1, t));
|
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
|
|
|
if(!ComputeDll.UpdateWeightsAdam(NeuronOCL.getWeightsIndex(), getGradientIndex(), NeuronOCL.getOutputIndex(),
|
2026-08-01 11:27:28 -04:00
|
|
|
NeuronOCL.getFirstMomentumIndex(), NeuronOCL.getSecondMomentumIndex(),
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
inputs, lt, AdamBeta1, AdamBeta2, neurons))
|
2026-08-01 11:27:28 -04:00
|
|
|
{
|
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
|
|
|
Print(__FUNCTION__ + ": " + ComputeDll.BackendName() + " UpdateWeightsAdam failed, error " + IntegerToString(ComputeDll.LastError()));
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
t++;
|
|
|
|
|
}
|
|
|
|
|
//--- Weights stay DLL-resident; the next feedForward/backProp reads them via getWeightsIndex()
|
|
|
|
|
//--- (same as the OpenCL branch below). Save()/BlendWeightsFrom() BufferRead() on demand.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(OpenCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
uint global_work_offset[2] = {0, 0};
|
|
|
|
|
uint global_work_size[2];
|
|
|
|
|
global_work_size[0] = Neurons();
|
|
|
|
|
global_work_size[1] = NeuronOCL.Neurons();
|
|
|
|
|
if(optimization == SGD)
|
|
|
|
|
{
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_UpdateWeightsMomentum, def_k_uwm_matrix_w, NeuronOCL.getWeightsIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_UpdateWeightsMomentum, def_k_uwm_matrix_g, getGradientIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_UpdateWeightsMomentum, def_k_uwm_matrix_i, NeuronOCL.getOutputIndex());
|
|
|
|
|
OpenCL.SetArgumentBuffer(def_k_UpdateWeightsMomentum, def_k_uwm_matrix_dw, NeuronOCL.getDeltaWeightsIndex());
|
|
|
|
|
OpenCL.SetArgument(def_k_UpdateWeightsMomentum, def_k_uwm_inputs, NeuronOCL.Neurons());
|
2026-08-20 07:00:09 -04:00
|
|
|
OpenCL.SetArgument(def_k_UpdateWeightsMomentum, def_k_uwm_learning_rates, (float)g_eta);
|
2026-08-01 11:27:28 -04:00
|
|
|
OpenCL.SetArgument(def_k_UpdateWeightsMomentum, def_k_uwm_momentum, (float)alpha);
|
|
|
|
|
OpenCL.SetArgument(def_k_UpdateWeightsMomentum, def_k_uwm_optimizer, 0);
|
|
|
|
|
ResetLastError();
|
|
|
|
|
if(!OpenCL.Execute(def_k_UpdateWeightsMomentum, 2, global_work_offset, global_work_size))
|
|
|
|
|
{
|
|
|
|
|
printf("Error of execution kernel UpdateWeightsMomentum: %d", GetLastError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_UpdateWeightsAdam, def_k_uwa_matrix_w, NeuronOCL.getWeightsIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_UpdateWeightsAdam, def_k_uwa_matrix_g, getGradientIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_UpdateWeightsAdam, def_k_uwa_matrix_i, NeuronOCL.getOutputIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_UpdateWeightsAdam, def_k_uwa_matrix_m, NeuronOCL.getFirstMomentumIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_UpdateWeightsAdam, def_k_uwa_matrix_v, NeuronOCL.getSecondMomentumIndex()))
|
|
|
|
|
return false;
|
2026-08-20 07:00:09 -04:00
|
|
|
double lt = g_eta * sqrt(1 - pow(AdamBeta2, t)) / (1 - pow(AdamBeta1, t));
|
2026-08-01 11:27:28 -04:00
|
|
|
if(!OpenCL.SetArgument(def_k_UpdateWeightsAdam, def_k_uwa_inputs, NeuronOCL.Neurons()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgument(def_k_UpdateWeightsAdam, def_k_uwa_l, (float)lt))
|
|
|
|
|
return false;
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
if(!OpenCL.SetArgument(def_k_UpdateWeightsAdam, def_k_uwa_b1, (float)AdamBeta1))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
if(!OpenCL.SetArgument(def_k_UpdateWeightsAdam, def_k_uwa_b2, (float)AdamBeta2))
|
2026-08-01 11:27:28 -04:00
|
|
|
return false;
|
fix: dense backprop read the weight matrix transposed - on every backend
CaclHiddenGradient computed this layer's gradient as matrix_w[(outputs+1)*i + k]
against a buffer whose actual layout (one row per NEXT-layer neuron, stride
inputs+1) makes the correct read matrix_w[k*(inputs+1) + i]: the transpose for
square layers, and for the non-square boundaries this EA actually builds
(tapered stacks, the 3-neuron head) a mis-strided walk that ran past the buffer
end - garbage on OpenCL, zeroed reads on the CPU DLL, so the tiers did not even
agree with each other. Every gradient crossing a dense boundary on its way down
- the entire learning signal reaching the BN/conv/LSTM front ends - passed
through a fixed wrong matrix: feedback-alignment dynamics, not backprop, which
is why nets still "learned something" and this survived. The book reference
(NeuroNet_DNG) fixed this in a later article version; our kernel descended from
the earlier one. Confounds every model-based negative verdict to date.
Also in this commit, same root cause family:
- per-sample UpdateWeightsAdam (OpenCL): input for slot group j was read at
matrix_i[j] instead of matrix_i[j*4] (corrupted outer product past group 0),
and dispatch dim 1 sized on ceil(inputs/4) left the bias column unreachable
whenever inputs%4==0 - dense biases never trained on OpenCL. Rewritten as a
lane-guarded scalar loop keeping our Adam conventions (sqrt-stored v,
decoupled decay, both clamps, no sign gate). The batched accum path never had
either bug; this kernel is what SetBatchSize(1) runs - including online
continual learning on client machines, where OpenCL is the only tier.
- conv backward passed raw (int)Activation() where the kernels expect
NativeActivationCode(): NONE took the tanh branch (clamping a BN layer's
unbounded z-scores), TANH took sigmoid, PRELU took none. Dormant only because
the conv sits at layer 1 today.
- hidden-gradient dispatch over Neurons()+1 dropped to Neurons(): biases get no
backprop gradient and the extra work-item only ever read past matrix_o.
All three backends (Network.cl, WarriorCPU.cpp, WarriorDML.cpp HLSL) changed in
lockstep; DML gained an `inputs` constant to derive the row stride. New
dense_backprop_check.cpp proves the CPU kernel is central-finite-difference
consistent with the real forward kernel on 8x8, 64x3, 33x64, 5x3 (max diff
3e-9) and that all three activation branches match transcription. All 16 checks
pass. Offline math check only - the in-situ proof remains the per-layer dW/W
report on a real era.
FORCES FULL RETRAIN. Both DLLs rebuilt and redeployed to MQL5\Libraries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:06:09 -04:00
|
|
|
//--- Dim 1 covers the full (inputs + 1)-wide weight row in groups of 4, so the bias column is
|
|
|
|
|
//--- dispatched too. Sizing this on Neurons() (without the +1) left the bias group unreachable
|
|
|
|
|
//--- whenever inputs % 4 == 0 - see the kernel's 2026-08-11 comment.
|
|
|
|
|
global_work_size[1] = NeuronOCL.Neurons() + 1;
|
2026-08-01 11:27:28 -04:00
|
|
|
uint rest = global_work_size[1] % 4;
|
|
|
|
|
global_work_size[1] = (global_work_size[1] - rest) / 4 + (rest > 0 ? 1 : 0);
|
|
|
|
|
ResetLastError();
|
|
|
|
|
if(!OpenCL.Execute(def_k_UpdateWeightsAdam, 2, global_work_offset, global_work_size))
|
|
|
|
|
{
|
|
|
|
|
printf("Error of execution kernel UpdateWeightsAdam: %d", GetLastError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
t++;
|
|
|
|
|
}
|
|
|
|
|
//--- Weights stays GPU-resident; the next feedForward reads it via getWeightsIndex(). Save()
|
|
|
|
|
//--- and BlendWeightsFrom()'s getWeights() both call GetData(), which BufferRead()s itself.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
//| MINI-BATCH ACCUMULATE (dense) - see the declaration comment. |
|
|
|
|
|
//| Same dispatch shape as updateInputWeights above, but it only ADDS |
|
|
|
|
|
//| this sample's outer product into GradAccum; no weight is touched |
|
|
|
|
|
//| and no optimizer state advances until ApplyAccumulatedGradients. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::accumulateInputWeightGrads(CNeuronBaseOCL *NeuronOCL)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(NeuronOCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
//--- The weight block being accumulated for lives on the SOURCE neuron, exactly as
|
|
|
|
|
//--- updateInputWeights' matrix_w does - so the accumulator is sized and owned there too.
|
|
|
|
|
if(!NeuronOCL.EnsureGradAccum(NeuronOCL.Weights))
|
|
|
|
|
return false;
|
|
|
|
|
int inputs = NeuronOCL.Neurons();
|
|
|
|
|
int neurons = Neurons();
|
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
|
|
|
if(CheckPointer(ComputeDll) != POINTER_INVALID)
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
{
|
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
|
|
|
if(!ComputeDll.AccumulateWeightGrad(NeuronOCL.GradAccum.GetIndex(), getGradientIndex(),
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
NeuronOCL.getOutputIndex(), inputs, neurons))
|
|
|
|
|
{
|
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
|
|
|
Print(__FUNCTION__ + ": " + ComputeDll.BackendName() + " AccumulateWeightGrad failed, error " + IntegerToString(ComputeDll.LastError()));
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(OpenCL) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
uint global_work_offset[2] = {0, 0};
|
|
|
|
|
uint global_work_size[2];
|
|
|
|
|
global_work_size[0] = neurons;
|
|
|
|
|
global_work_size[1] = inputs + 1; // the last slot is the bias, exactly as the Adam kernel treats it
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_AccumulateWeightGrad, def_k_awg_matrix_acc, NeuronOCL.GradAccum.GetIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_AccumulateWeightGrad, def_k_awg_matrix_g, getGradientIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_AccumulateWeightGrad, def_k_awg_matrix_i, NeuronOCL.getOutputIndex()))
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgument(def_k_AccumulateWeightGrad, def_k_awg_inputs, inputs))
|
|
|
|
|
return false;
|
|
|
|
|
ResetLastError();
|
|
|
|
|
if(!OpenCL.Execute(def_k_AccumulateWeightGrad, 2, global_work_offset, global_work_size))
|
|
|
|
|
{
|
|
|
|
|
printf("Error of execution kernel AccumulateWeightGrad: %d", GetLastError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Type-dispatching wrapper, mirroring updateInputWeights(CObject*). |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::accumulateInputWeightGrads(CObject *SourceObject)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(SourceObject) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
//--- Every *OCL neuron type derives from CNeuronBaseOCL and stores its incoming dense matrix in
|
|
|
|
|
//--- Weights, so one branch covers them all. Conv and LSTM own EXTRA weight blocks and override
|
|
|
|
|
//--- this method; the legacy scalar hierarchy never reaches here (CNet refuses to batch without a
|
|
|
|
|
//--- backend - see CNet::BatchSize).
|
|
|
|
|
switch(SourceObject.Type())
|
|
|
|
|
{
|
|
|
|
|
case defNeuronBaseOCL:
|
|
|
|
|
case defNeuronBatchNormOCL:
|
|
|
|
|
case defNeuronConvOCL:
|
|
|
|
|
case defNeuronPoolOCL:
|
|
|
|
|
case defNeuronLSTMOCL:
|
|
|
|
|
{
|
|
|
|
|
CNeuronBaseOCL *temp = SourceObject;
|
|
|
|
|
return accumulateInputWeightGrads(temp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Zero this neuron's accumulator - see the declaration comment. |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::BeginGradAccum(void)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(GradAccum) == POINTER_INVALID || GradAccum.Total() <= 0)
|
|
|
|
|
return true; // nothing accumulated here yet; the first accumulate allocates it zeroed
|
|
|
|
|
return ZeroOptimizerBuffer(GradAccum);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-22 00:30:14 -04:00
|
|
|
//| DEVICE-SIDE mini-batch apply (OpenCL only). One dispatch, |
|
|
|
|
|
//| nothing crosses the bus. |
|
perf: mini-batch apply becomes a kernel - 8 weight-matrix transfers per batch become 0
Market builds cannot import a DLL, so OpenCL is the tier paying clients run.
It was several times slower than the CPU DLL, and the dominant reason was a
host-side optimizer step I shipped with the mini-batch work in 274630f.
ApplyAccumToBlock read the weights, the accumulator and both Adam moments back
over the bus, stepped them in MQL5, and wrote four buffers out - eight full
weight-matrix transfers per batch PER WEIGHT BLOCK, each a blocking sync. At
TRAIN_BATCH_SIZE 8 that is roughly one entire weight matrix crossing the bus
per training sample. It was host-side for a good reason (one optimizer
implementation shared by all four tiers instead of four that can drift), and
that reason turned out to cost the product's own compute tier.
- ApplyAccumAdam / ApplyAccumMomentum in Network.cl: flat, elementwise, and
they zero the accumulator themselves so there is no separate clear dispatch
and no way to leave it dirty via an early return
- ApplyAccumOnDevice dispatches them; the host step stays as the reference and
as the implementation for DirectML, the CPU DLL and pure-MQL5
- failure latches OFF process-wide with one warning rather than a failed
Execute per batch, since a kernel that did not build will not build later
- m_applyKernelsOk is tracked SEPARATELY from m_batchKernelsOk: without the
accumulation kernels a device cannot batch and must drop to per-sample
updates, whereas without these it batches normally and merely pays the
transfers. Conflating them would turn a missing optimisation into a changed
optimizer
The kernel runs fp32 where the host step ran fp64. That is the OpenCL tier
becoming self-consistent, not a regression: its device buffers are already
fp32 (CBufferDouble::m_data_f) and its unbatched optimizer already ran in
fp32, so the batched path was the odd one out. DLL tiers keep fp64 throughout.
Validated: no OpenCL platform exists on this box, so the kernel source is
syntax/type checked as C against a shim and driven for 4000 steps. It clears
the accumulator, and it is scale-invariant - displacement 1.199336 at |g|=1
versus 1.199333 at |g|=1e-4, matching the figure DirectML/batch_accum_check
produced for the fixed CPU_UpdateWeightsAdam. It reproduces the corrected Adam,
not the pre-371f8aa one.
Both build variants compile 0 errors, 0 warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:09:52 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::ApplyAccumOnDevice(CBufferDouble *w, CBufferDouble *acc, CBufferDouble *m,
|
|
|
|
|
CBufferDouble *v, CBufferDouble *dw, double scale, int total)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(OpenCL) == POINTER_INVALID || total <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
uint offset[1] = {0};
|
|
|
|
|
uint size[1];
|
|
|
|
|
size[0] = (uint)total;
|
|
|
|
|
if(optimization == ADAM)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(m) == POINTER_INVALID || CheckPointer(v) == POINTER_INVALID ||
|
|
|
|
|
m.Total() < total || v.Total() < total || m.GetIndex() < 0 || v.GetIndex() < 0)
|
|
|
|
|
return false;
|
|
|
|
|
//--- Same bias-corrected step the host path and the unbatched kernel compute. t is NOT advanced
|
|
|
|
|
//--- here - ApplyAccumulatedGradients owns that, once per batch across every block it holds.
|
2026-08-20 07:00:09 -04:00
|
|
|
double lt = g_eta * sqrt(1 - pow(AdamBeta2, t)) / (1 - pow(AdamBeta1, t));
|
perf: mini-batch apply becomes a kernel - 8 weight-matrix transfers per batch become 0
Market builds cannot import a DLL, so OpenCL is the tier paying clients run.
It was several times slower than the CPU DLL, and the dominant reason was a
host-side optimizer step I shipped with the mini-batch work in 274630f.
ApplyAccumToBlock read the weights, the accumulator and both Adam moments back
over the bus, stepped them in MQL5, and wrote four buffers out - eight full
weight-matrix transfers per batch PER WEIGHT BLOCK, each a blocking sync. At
TRAIN_BATCH_SIZE 8 that is roughly one entire weight matrix crossing the bus
per training sample. It was host-side for a good reason (one optimizer
implementation shared by all four tiers instead of four that can drift), and
that reason turned out to cost the product's own compute tier.
- ApplyAccumAdam / ApplyAccumMomentum in Network.cl: flat, elementwise, and
they zero the accumulator themselves so there is no separate clear dispatch
and no way to leave it dirty via an early return
- ApplyAccumOnDevice dispatches them; the host step stays as the reference and
as the implementation for DirectML, the CPU DLL and pure-MQL5
- failure latches OFF process-wide with one warning rather than a failed
Execute per batch, since a kernel that did not build will not build later
- m_applyKernelsOk is tracked SEPARATELY from m_batchKernelsOk: without the
accumulation kernels a device cannot batch and must drop to per-sample
updates, whereas without these it batches normally and merely pays the
transfers. Conflating them would turn a missing optimisation into a changed
optimizer
The kernel runs fp32 where the host step ran fp64. That is the OpenCL tier
becoming self-consistent, not a regression: its device buffers are already
fp32 (CBufferDouble::m_data_f) and its unbatched optimizer already ran in
fp32, so the batched path was the odd one out. DLL tiers keep fp64 throughout.
Validated: no OpenCL platform exists on this box, so the kernel source is
syntax/type checked as C against a shim and driven for 4000 steps. It clears
the accumulator, and it is scale-invariant - displacement 1.199336 at |g|=1
versus 1.199333 at |g|=1e-4, matching the figure DirectML/batch_accum_check
produced for the fixed CPU_UpdateWeightsAdam. It reproduces the corrected Adam,
not the pre-371f8aa one.
Both build variants compile 0 errors, 0 warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:09:52 -04:00
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_ApplyAccumAdam, def_k_aaa_matrix_w, w.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgumentBuffer(def_k_ApplyAccumAdam, def_k_aaa_matrix_acc, acc.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgumentBuffer(def_k_ApplyAccumAdam, def_k_aaa_matrix_m, m.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgumentBuffer(def_k_ApplyAccumAdam, def_k_aaa_matrix_v, v.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumAdam, def_k_aaa_scale, (float)scale) ||
|
|
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumAdam, def_k_aaa_l, (float)lt) ||
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumAdam, def_k_aaa_b1, (float)AdamBeta1) ||
|
|
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumAdam, def_k_aaa_b2, (float)AdamBeta2))
|
perf: mini-batch apply becomes a kernel - 8 weight-matrix transfers per batch become 0
Market builds cannot import a DLL, so OpenCL is the tier paying clients run.
It was several times slower than the CPU DLL, and the dominant reason was a
host-side optimizer step I shipped with the mini-batch work in 274630f.
ApplyAccumToBlock read the weights, the accumulator and both Adam moments back
over the bus, stepped them in MQL5, and wrote four buffers out - eight full
weight-matrix transfers per batch PER WEIGHT BLOCK, each a blocking sync. At
TRAIN_BATCH_SIZE 8 that is roughly one entire weight matrix crossing the bus
per training sample. It was host-side for a good reason (one optimizer
implementation shared by all four tiers instead of four that can drift), and
that reason turned out to cost the product's own compute tier.
- ApplyAccumAdam / ApplyAccumMomentum in Network.cl: flat, elementwise, and
they zero the accumulator themselves so there is no separate clear dispatch
and no way to leave it dirty via an early return
- ApplyAccumOnDevice dispatches them; the host step stays as the reference and
as the implementation for DirectML, the CPU DLL and pure-MQL5
- failure latches OFF process-wide with one warning rather than a failed
Execute per batch, since a kernel that did not build will not build later
- m_applyKernelsOk is tracked SEPARATELY from m_batchKernelsOk: without the
accumulation kernels a device cannot batch and must drop to per-sample
updates, whereas without these it batches normally and merely pays the
transfers. Conflating them would turn a missing optimisation into a changed
optimizer
The kernel runs fp32 where the host step ran fp64. That is the OpenCL tier
becoming self-consistent, not a regression: its device buffers are already
fp32 (CBufferDouble::m_data_f) and its unbatched optimizer already ran in
fp32, so the batched path was the odd one out. DLL tiers keep fp64 throughout.
Validated: no OpenCL platform exists on this box, so the kernel source is
syntax/type checked as C against a shim and driven for 4000 steps. It clears
the accumulator, and it is scale-invariant - displacement 1.199336 at |g|=1
versus 1.199333 at |g|=1e-4, matching the figure DirectML/batch_accum_check
produced for the fixed CPU_UpdateWeightsAdam. It reproduces the corrected Adam,
not the pre-371f8aa one.
Both build variants compile 0 errors, 0 warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:09:52 -04:00
|
|
|
return false;
|
|
|
|
|
ResetLastError();
|
|
|
|
|
return OpenCL.Execute(def_k_ApplyAccumAdam, 1, offset, size);
|
|
|
|
|
}
|
|
|
|
|
if(CheckPointer(dw) == POINTER_INVALID || dw.Total() < total || dw.GetIndex() < 0)
|
|
|
|
|
return false;
|
|
|
|
|
if(!OpenCL.SetArgumentBuffer(def_k_ApplyAccumMomentum, def_k_aam_matrix_w, w.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgumentBuffer(def_k_ApplyAccumMomentum, def_k_aam_matrix_acc, acc.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgumentBuffer(def_k_ApplyAccumMomentum, def_k_aam_matrix_dw, dw.GetIndex()) ||
|
|
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumMomentum, def_k_aam_scale, (float)scale) ||
|
2026-08-20 07:00:09 -04:00
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumMomentum, def_k_aam_lr, (float)g_eta) ||
|
perf: mini-batch apply becomes a kernel - 8 weight-matrix transfers per batch become 0
Market builds cannot import a DLL, so OpenCL is the tier paying clients run.
It was several times slower than the CPU DLL, and the dominant reason was a
host-side optimizer step I shipped with the mini-batch work in 274630f.
ApplyAccumToBlock read the weights, the accumulator and both Adam moments back
over the bus, stepped them in MQL5, and wrote four buffers out - eight full
weight-matrix transfers per batch PER WEIGHT BLOCK, each a blocking sync. At
TRAIN_BATCH_SIZE 8 that is roughly one entire weight matrix crossing the bus
per training sample. It was host-side for a good reason (one optimizer
implementation shared by all four tiers instead of four that can drift), and
that reason turned out to cost the product's own compute tier.
- ApplyAccumAdam / ApplyAccumMomentum in Network.cl: flat, elementwise, and
they zero the accumulator themselves so there is no separate clear dispatch
and no way to leave it dirty via an early return
- ApplyAccumOnDevice dispatches them; the host step stays as the reference and
as the implementation for DirectML, the CPU DLL and pure-MQL5
- failure latches OFF process-wide with one warning rather than a failed
Execute per batch, since a kernel that did not build will not build later
- m_applyKernelsOk is tracked SEPARATELY from m_batchKernelsOk: without the
accumulation kernels a device cannot batch and must drop to per-sample
updates, whereas without these it batches normally and merely pays the
transfers. Conflating them would turn a missing optimisation into a changed
optimizer
The kernel runs fp32 where the host step ran fp64. That is the OpenCL tier
becoming self-consistent, not a regression: its device buffers are already
fp32 (CBufferDouble::m_data_f) and its unbatched optimizer already ran in
fp32, so the batched path was the odd one out. DLL tiers keep fp64 throughout.
Validated: no OpenCL platform exists on this box, so the kernel source is
syntax/type checked as C against a shim and driven for 4000 steps. It clears
the accumulator, and it is scale-invariant - displacement 1.199336 at |g|=1
versus 1.199333 at |g|=1e-4, matching the figure DirectML/batch_accum_check
produced for the fixed CPU_UpdateWeightsAdam. It reproduces the corrected Adam,
not the pre-371f8aa one.
Both build variants compile 0 errors, 0 warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:09:52 -04:00
|
|
|
!OpenCL.SetArgument(def_k_ApplyAccumMomentum, def_k_aam_momentum, (float)alpha))
|
|
|
|
|
return false;
|
|
|
|
|
ResetLastError();
|
|
|
|
|
return OpenCL.Execute(def_k_ApplyAccumMomentum, 1, offset, size);
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-22 00:30:14 -04:00
|
|
|
//| MINI-BATCH APPLY (dense) - ONE optimizer step on the batch MEAN. |
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::ApplyAccumToBlock(CBufferDouble *w, CBufferDouble *acc, CBufferDouble *m,
|
|
|
|
|
CBufferDouble *v, CBufferDouble *dw, double scale)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(acc) == POINTER_INVALID || acc.Total() <= 0 ||
|
|
|
|
|
CheckPointer(w) == POINTER_INVALID || w.Total() <= 0)
|
|
|
|
|
return true; // no weight block here (e.g. an output-layer neuron) - nothing to apply
|
|
|
|
|
int total = MathMin(acc.Total(), w.Total());
|
perf: mini-batch apply becomes a kernel - 8 weight-matrix transfers per batch become 0
Market builds cannot import a DLL, so OpenCL is the tier paying clients run.
It was several times slower than the CPU DLL, and the dominant reason was a
host-side optimizer step I shipped with the mini-batch work in 274630f.
ApplyAccumToBlock read the weights, the accumulator and both Adam moments back
over the bus, stepped them in MQL5, and wrote four buffers out - eight full
weight-matrix transfers per batch PER WEIGHT BLOCK, each a blocking sync. At
TRAIN_BATCH_SIZE 8 that is roughly one entire weight matrix crossing the bus
per training sample. It was host-side for a good reason (one optimizer
implementation shared by all four tiers instead of four that can drift), and
that reason turned out to cost the product's own compute tier.
- ApplyAccumAdam / ApplyAccumMomentum in Network.cl: flat, elementwise, and
they zero the accumulator themselves so there is no separate clear dispatch
and no way to leave it dirty via an early return
- ApplyAccumOnDevice dispatches them; the host step stays as the reference and
as the implementation for DirectML, the CPU DLL and pure-MQL5
- failure latches OFF process-wide with one warning rather than a failed
Execute per batch, since a kernel that did not build will not build later
- m_applyKernelsOk is tracked SEPARATELY from m_batchKernelsOk: without the
accumulation kernels a device cannot batch and must drop to per-sample
updates, whereas without these it batches normally and merely pays the
transfers. Conflating them would turn a missing optimisation into a changed
optimizer
The kernel runs fp32 where the host step ran fp64. That is the OpenCL tier
becoming self-consistent, not a regression: its device buffers are already
fp32 (CBufferDouble::m_data_f) and its unbatched optimizer already ran in
fp32, so the batched path was the odd one out. DLL tiers keep fp64 throughout.
Validated: no OpenCL platform exists on this box, so the kernel source is
syntax/type checked as C against a shim and driven for 4000 steps. It clears
the accumulator, and it is scale-invariant - displacement 1.199336 at |g|=1
versus 1.199333 at |g|=1e-4, matching the figure DirectML/batch_accum_check
produced for the fixed CPU_UpdateWeightsAdam. It reproduces the corrected Adam,
not the pre-371f8aa one.
Both build variants compile 0 errors, 0 warnings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:09:52 -04:00
|
|
|
//--- DEVICE FAST PATH. Latched off on first failure rather than retried every batch: a kernel that
|
|
|
|
|
//--- did not build will not build later, and the host path below is a correct answer, so the right
|
|
|
|
|
//--- behaviour is one warning and full speed on the fallback - not a failed dispatch per batch.
|
|
|
|
|
if(g_applyAccumKernelUsable && CheckPointer(OpenCL) != POINTER_INVALID && w.GetIndex() >= 0 &&
|
|
|
|
|
acc.GetIndex() >= 0)
|
|
|
|
|
{
|
|
|
|
|
if(ApplyAccumOnDevice(w, acc, m, v, dw, scale, total))
|
|
|
|
|
return true;
|
|
|
|
|
g_applyAccumKernelUsable = false;
|
|
|
|
|
PrintFormat("%s: device-side mini-batch apply failed (error %d) - falling back to the host step "
|
|
|
|
|
"for the rest of this run. Training stays correct; each batch now costs a full "
|
|
|
|
|
"weight-matrix round trip, so expect it to be substantially slower.",
|
|
|
|
|
__FUNCTION__, GetLastError());
|
|
|
|
|
}
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
//--- Pull the device-side copies into the host mirrors. On a host-only net (no backend) there is no
|
|
|
|
|
//--- device to read FROM and the host mirror is already the truth, so the round-trip is skipped
|
|
|
|
|
//--- rather than treated as a failure.
|
|
|
|
|
bool haveDevice = (w.GetIndex() >= 0);
|
|
|
|
|
if(haveDevice && (!w.BufferRead() || !acc.BufferRead()))
|
|
|
|
|
return false;
|
|
|
|
|
if(optimization == ADAM)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(m) == POINTER_INVALID || CheckPointer(v) == POINTER_INVALID ||
|
|
|
|
|
m.Total() < total || v.Total() < total)
|
|
|
|
|
return false;
|
|
|
|
|
if(haveDevice && (!m.BufferRead() || !v.BufferRead()))
|
|
|
|
|
return false;
|
|
|
|
|
//--- Bias correction reads t but does NOT advance it here - the caller advances once per batch
|
|
|
|
|
//--- after every block it owns has been stepped, because t counts optimizer STEPS and a batch is
|
|
|
|
|
//--- one step no matter how many weight blocks the neuron carries.
|
2026-08-20 07:00:09 -04:00
|
|
|
double lt = g_eta * sqrt(1 - pow(AdamBeta2, t)) / (1 - pow(AdamBeta1, t));
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
double grad = acc.At(i) * scale;
|
|
|
|
|
double wv = w.At(i);
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
double mt = AdamBeta1 * m.At(i) + (1 - AdamBeta1) * grad;
|
fix: the Adam second moment was never Adam - all four tiers
Root cause of the B=32 regression, and it predates F4 entirely. Every Adam
kernel stored v already square-rooted and then fed that stored value back in
as if it were the variance:
v_new = sqrt(b2 * v_old + (1 - b2) * g^2)
That recursion has a fixed point at v ~= b2 = 0.999 for ANY gradient below
unit scale, so the denominator stops tracking the gradient and Adam degrades
into plain SGD with lr = lt. Measured against the shipped WarriorCPU.dll
(batch_accum_check.cpp, TestOptimizerScaleInvariance), 4000 steps of a
constant gradient: 3285x less displacement at |g|=1e-5 than at |g|=1, where
a scale-invariant optimizer gives the same distance for both. After the fix
all six magnitudes read 1.199 and v tracks |g| exactly.
It hit conv/LSTM specifically because they sit behind a batch-norm with
running variance ~2.6e+05, so their gradients arrive divided by ~500 - deep
in the degraded regime - while the dense stack near the loss stayed in the
working one. In situ on SP500 H1: lstm1 dW/W 2.62/10.0/7.14% -> 0.024/0.022/
0.003%, conv1 decaying to 0.000% by era 30. NeuronBatchNorm.mqh already
squared v back for gamma/beta and its comment named the kernels as wrong,
which is exactly why gamma/beta kept training while the stages behind froze.
Persisted .nnw needs no migration - v keeps its std-dev meaning.
Also, the two ways F4 exposed it, both mine:
- No LR compensation for B fewer steps per era. sqrt(B) for adaptive methods
(Krizhevsky 2014; Granziol et al. 2022), applied once in
InitialEtaForOptimizer(). Linear scaling (Goyal et al. 2017) is for SGD.
- Plateau patience denominated in eras, so raising B made the ladder 32x more
impatient in its only unit. PAI converged at era 41 on ~49k updates where
the same config had been finding new bests at era 1028.
TrainPlateauPatienceEras() stretches it by the same sqrt(B).
TRAIN_BATCH_SIZE 32 -> 8 so the patience stretch stays affordable (8 -> 23
eras per stage, not 8 -> 45). Both helpers are identities at B=1.
Deploy gate: DEPLOY_MIN_SIDE_RECALL_PCT (10%) folded into tradeableOK. The
perceptron reported Sell:0% recall in all 41 eras, cleared the floor on Buy
alone at 36.6% vs 34% chance, deployed, and sprayed buy arrows. Folded into
the ranking key rather than checked at deploy time so a one-sided era cannot
become best-so-far in the first place.
Deinit: the arrow purge now runs BEFORE ExtPanel.Destroy(), an unbounded
CAppDialog teardown that sat ahead of it - the same ordering inversion the
rule there exists to prevent. CONV was force-terminated 4.8 s into OnDeinit
(vs ~1.1 s for the three that finished) having reached none of its cleanup,
so its arrows stayed on the chart. Steps are now timed in the log.
PurgeChart's verification rescan filtered on OBJ_ARROW, the same blind spot
as the bulk delete, so "persisted 10 ... cleared 0" passed silently. It now
walks every object type and reports the object counts when both are zero.
Both build variants compile 0 errors / 0 warnings; both DLLs rebuilt.
FORCES A RETRAIN (already forced by N1) and both DLLs must ship with the .ex5.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 14:02:35 -04:00
|
|
|
//--- v is STORED square-rooted, so square it back before the recursion. See Network.cl's
|
|
|
|
|
//--- UpdateWeightsAdam for why feeding the stored sqrt straight in is not Adam at all.
|
refactor(stdlib): adopt Math\Stat for the deploy gate's normal tail; retire the b1/b2/lr/momentum macros
The gate's NormalUpperTail was a hand-rolled Abramowitz & Stegun 26.2.17
approximation. Its own comment gave the reason - "drags a chain of headers
behind it" - and that turned out to be one file: Math\Stat\Normal.mqh
includes only Math.mqh, which includes nothing. Swapped for Cody's rational
approximation in the library (~18 significant digits vs |error| < 7.5e-8).
No past verdict changes: at the z the gate operates on, the difference is
orders of magnitude below DEPLOY_FAMILY_WISE_ALPHA.
Adopting it needed the four bare macros in AI\Network.mqh gone first.
"#define b1 AdamBeta1" collides with an identifier in Math.mqh, so the
include would have macro-expanded the library's own local and failed to
compile - the same landmine that made the original author rename the
approximation's coefficients to ntB1..ntB5 rather than use the reference's
b1..b5. lr, b2 and momentum are the same class of hazard: single-token
global macros in a 52k-line codebase. All four now resolve to the input
names they always aliased, which is a pure textual identity - verified zero
bare occurrences remain.
Also:
- SelectionSort over the buffered signals was O(n^2) with an O(n^2) count of
StructToTime calls, because the comparison rebuilt both datetimes from the
six int date fields every time. Now materialises the keys once and does an
insertion sort; ArraySort cannot permute a struct array. IsEarlier goes
with it, MakeDateTime becomes SignalTime.
- Seven FileOpen sites lacked FILE_SHARE_READ|FILE_SHARE_WRITE, including
AtomicWriteBegin, which stages every model save. All 43 sites now carry
them - an exclusive open fails outright when another process holds the
path, which here has meant a silently skipped save.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:31:36 -04:00
|
|
|
double vt = sqrt(AdamBeta2 * v.At(i) * v.At(i) + (1 - AdamBeta2) * grad * grad);
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
double delta = lt * mt / (vt > 0 ? vt : lt * 10) - lt * WEIGHT_DECAY * wv;
|
|
|
|
|
delta = MathMax(-MAX_WEIGHT_DELTA, MathMin(MAX_WEIGHT_DELTA, delta));
|
|
|
|
|
if(!w.Update(i, MathMax(-MAX_WEIGHT, MathMin(MAX_WEIGHT, wv + delta))) ||
|
|
|
|
|
!m.Update(i, mt) || !v.Update(i, vt))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(haveDevice && (!m.BufferWrite() || !v.BufferWrite()))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(dw) == POINTER_INVALID || dw.Total() < total)
|
|
|
|
|
return false;
|
|
|
|
|
if(haveDevice && !dw.BufferRead())
|
|
|
|
|
return false;
|
|
|
|
|
for(int i = 0; i < total; i++)
|
|
|
|
|
{
|
|
|
|
|
double grad = acc.At(i) * scale;
|
2026-08-20 07:00:09 -04:00
|
|
|
double delta = g_eta * grad + alpha * dw.At(i);
|
feat: mini-batch gradient accumulation (F4), front-end-aware capacity budget (F6), split Wyckoff categoricals (N1)
Completes the 2026-08-09 training audit. FORCES A RETRAIN of every
Wyckoff-enabled config (N1 re-keys the fingerprint), and BOTH DLLs must be
redeployed alongside the .ex5 - they carry new exports.
F4 - mini-batch accumulation, TRAIN_BATCH_SIZE=32. Training was pure online
SGD (one weight update per bar), which is the mechanical source of the
era-to-era whipsaw every downstream guard was built to cope with. The O(n^2)
outer product is native - AccumulateWeightGrad / AccumulateWeightGradConv /
AccumulateBufferInto in Network.cl, WarriorCPU and WarriorDML - while the
optimizer step is host-side MQL5 shared by all tiers (ApplyAccumToBlock), so
there is one Adam/SGD implementation instead of four that can drift.
- the LSTM needs no outer-product kernel (WeightsGradient already holds the
sample's full dW) but could NOT simply be left un-zeroed between samples:
CPU_LSTMSeqBackward/DML_LSTMSeqBackward memset it on entry. Hence a
separate accumulator plus an elementwise add.
- batch-norm gamma/beta accumulate in host arrays, not new BatchOptions
slots - BN_OPT_STRIDE is baked into every persisted .nnw.
- scoped to pass 2; online learning keeps immediate updates. Every save /
checkpoint / scoring boundary flushes, scaling by the real sample count.
- degrades to per-sample updates (one log line) on a tier that cannot
accumulate, so old devices and DLL-free builds are unaffected.
- verified offline: DirectML/batch_accum_check.cpp drives the real exports
against an independent reference; at B=1 the accumulator matches the
shipped unbatched kernel's own gradient to 1.1e-16. Math only - the
in-situ check remains the per-layer dW/W report on a real era.
F6 - ComputeFirstLayerWidth budgeted against the RAW input width even where a
conv/LSTM front end had already reduced it, so an LSTM's dense stack was
charged for 1,280 inputs when it receives 64. Confirmed from the deployed
.cfg files: CONV, LSTM and HYBRID were all pinned at the 16-unit floor. Now
budgeted against the front-end output and capped at it (never fan out), with
the derivation reordered so both stages settle first.
N1 - EventCode/EventPhase/StructuralPhase are signed categoricals packing
direction and Wyckoff stage into one scalar across a sign discontinuity. Split
into direction + [0,1] magnitude, the same convention the base OHLC block uses.
Information-preserving; 13 readings now occupy 16 inputs.
Compiled clean (0 errors, 0 warnings); both DLLs rebuilt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 11:48:03 -04:00
|
|
|
if(!dw.Update(i, delta) ||
|
|
|
|
|
!w.Update(i, MathMax(-MAX_WEIGHT, MathMin(MAX_WEIGHT, w.At(i) + delta))))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(haveDevice && !dw.BufferWrite())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//--- Clear the accumulator for the next batch and push the new weights back to the device.
|
|
|
|
|
for(int i = 0; i < acc.Total(); i++)
|
|
|
|
|
if(!acc.Update(i, 0.0))
|
|
|
|
|
return false;
|
|
|
|
|
if(haveDevice && (!w.BufferWrite() || !acc.BufferWrite()))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::ApplyAccumulatedGradients(double scale)
|
|
|
|
|
{
|
|
|
|
|
if(!ApplyAccumToBlock(Weights, GradAccum, FirstMomentum, SecondMomentum, DeltaWeights, scale))
|
|
|
|
|
return false;
|
|
|
|
|
if(optimization == ADAM)
|
|
|
|
|
t++;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-08-01 11:27:28 -04:00
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::calcHiddenGradients(CObject *TargetObject)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(TargetObject) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
CNeuronBaseOCL *temp = NULL;
|
|
|
|
|
CNeuronConvOCL *tempConv = NULL;
|
|
|
|
|
CNeuronLSTMOCL *tempLstm = NULL;
|
|
|
|
|
CNeuronPoolOCL *tempPool = NULL;
|
|
|
|
|
CNeuronBatchNormOCL *tempBN = NULL;
|
|
|
|
|
switch(TargetObject.Type())
|
|
|
|
|
{
|
|
|
|
|
case defNeuronBaseOCL:
|
|
|
|
|
temp = TargetObject;
|
|
|
|
|
return calcHiddenGradients(temp);
|
|
|
|
|
break;
|
|
|
|
|
case defNeuronBatchNormOCL:
|
|
|
|
|
//--- Same inverted-call convention as conv/pool/LSTM: batch norm owns its own backward step
|
|
|
|
|
//--- and writes into this->Gradient. Routing it through the dense branch instead would run
|
|
|
|
|
//--- CaclHiddenGradient against a weight matrix batch norm does not have.
|
|
|
|
|
tempBN = TargetObject;
|
|
|
|
|
return tempBN.calcInputGradients(GetPointer(this));
|
|
|
|
|
break;
|
|
|
|
|
case defNeuronConvOCL:
|
|
|
|
|
//--- Conv owns the backward step (calcInputGradients), called on itself with
|
|
|
|
|
//--- "this" (the earlier layer) passed in so it writes into this->Gradient.
|
|
|
|
|
tempConv = TargetObject;
|
|
|
|
|
return tempConv.calcInputGradients(GetPointer(this));
|
|
|
|
|
break;
|
|
|
|
|
case defNeuronLSTMOCL:
|
|
|
|
|
tempLstm = TargetObject;
|
|
|
|
|
return tempLstm.calcInputGradients(GetPointer(this));
|
|
|
|
|
break;
|
|
|
|
|
case defNeuronPoolOCL:
|
|
|
|
|
tempPool = TargetObject;
|
|
|
|
|
return tempPool.calcInputGradients(GetPointer(this));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::updateInputWeights(CObject *SourceObject)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(SourceObject) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
CNeuronBaseOCL *temp = NULL;
|
|
|
|
|
switch(SourceObject.Type())
|
|
|
|
|
{
|
|
|
|
|
case defNeuronBaseOCL:
|
|
|
|
|
case defNeuronConvOCL:
|
|
|
|
|
case defNeuronLSTMOCL:
|
|
|
|
|
case defNeuronPoolOCL:
|
|
|
|
|
case defNeuronBatchNormOCL:
|
|
|
|
|
temp = SourceObject;
|
|
|
|
|
return updateInputWeights(temp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::Save(const int file_handle)
|
|
|
|
|
{
|
|
|
|
|
if(file_handle == INVALID_HANDLE)
|
|
|
|
|
return false;
|
|
|
|
|
if(FileWriteInteger(file_handle, Type()) < INT_VALUE)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(FileWriteInteger(file_handle, (int)activation, INT_VALUE) < INT_VALUE)
|
|
|
|
|
return false;
|
|
|
|
|
if(FileWriteInteger(file_handle, (int)optimization, INT_VALUE) < INT_VALUE)
|
|
|
|
|
return false;
|
|
|
|
|
if(FileWriteInteger(file_handle, (int)t, INT_VALUE) < INT_VALUE)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID || !Output.BufferRead() || !Output.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID || !PrevOutput.BufferRead() || !PrevOutput.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID || !Gradient.BufferRead() || !Gradient.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
FileWriteInteger(file_handle, 0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
FileWriteInteger(file_handle, 1);
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID || !Weights.BufferRead() || !Weights.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(optimization == SGD)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID || !DeltaWeights.BufferRead() || !DeltaWeights.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID || !FirstMomentum.BufferRead() || !FirstMomentum.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID || !SecondMomentum.BufferRead() || !SecondMomentum.Save(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CNeuronBaseOCL::Load(const int file_handle)
|
|
|
|
|
{
|
|
|
|
|
if(file_handle == INVALID_HANDLE)
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
activation = (ENUM_ACTIVATION)FileReadInteger(file_handle, INT_VALUE);
|
|
|
|
|
optimization = (ENUM_OPTIMIZATION)FileReadInteger(file_handle, INT_VALUE);
|
|
|
|
|
t = FileReadInteger(file_handle, INT_VALUE);
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Output = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Output) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(Output.GetIndex() >= 0)
|
|
|
|
|
Output.BufferFree();
|
|
|
|
|
if(!Output.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(Output))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
PrevOutput = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(PrevOutput) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(PrevOutput.GetIndex() >= 0)
|
|
|
|
|
PrevOutput.BufferFree();
|
|
|
|
|
if(!PrevOutput.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(PrevOutput))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Gradient = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Gradient) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(Gradient.GetIndex() >= 0)
|
|
|
|
|
Gradient.BufferFree();
|
|
|
|
|
if(!Gradient.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(Gradient))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(FileReadInteger(file_handle) == 0)
|
|
|
|
|
return true;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
Weights = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(Weights) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(Weights.GetIndex() >= 0)
|
|
|
|
|
Weights.BufferFree();
|
|
|
|
|
if(!Weights.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(Weights))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(optimization == SGD)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
DeltaWeights = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(DeltaWeights) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(DeltaWeights.GetIndex() >= 0)
|
|
|
|
|
DeltaWeights.BufferFree();
|
|
|
|
|
if(!DeltaWeights.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(DeltaWeights))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
FirstMomentum = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(FirstMomentum) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(FirstMomentum.GetIndex() >= 0)
|
|
|
|
|
FirstMomentum.BufferFree();
|
|
|
|
|
if(!FirstMomentum.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(FirstMomentum))
|
|
|
|
|
return false;
|
|
|
|
|
//---
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID)
|
|
|
|
|
{
|
|
|
|
|
SecondMomentum = new CBufferDouble();
|
|
|
|
|
if(CheckPointer(SecondMomentum) == POINTER_INVALID)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(SecondMomentum.GetIndex() >= 0)
|
|
|
|
|
SecondMomentum.BufferFree();
|
|
|
|
|
if(!SecondMomentum.Load(file_handle))
|
|
|
|
|
return false;
|
|
|
|
|
if(!BackendBufferCreate(SecondMomentum))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
#endif
|