2026-08-20 22:59:17 +03:00 | | | //+------------------------------------------------------------------+
|
| | | //| D2SkillBankStudy.mq5 |
|
| | | //| Standalone D2Skill bank lifecycle acceptance study. |
|
| | | //+------------------------------------------------------------------+
|
| | | #property strict
|
| | | #property version "1.00"
|
| | |
|
| | | #include "..\\NeuroNet_DNG\\NeuroNet.mqh"
|
| | |
|
| | | const uint D2_STUDY_DIM = 2;
|
| | | const uint D2_STUDY_SLOTS = 2;
|
| | | const float D2_STUDY_EPSILON = 0.000001f;
|
| | | const string D2_STUDY_FILE = "D2SkillBankStudy.bin";
|
| | |
|
| | | CNet g_net;
|
| | | CNeuronBaseOCL g_source;
|
| | | bool g_completed = false;
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool CreateHost(void)
|
| | | {
|
| | | CArrayObj *description = new CArrayObj();
|
| | | if(!description)
|
| | | ReturnFalse;
|
| | | description.FreeMode(true);
|
| | | for(int i = 0; i < 2; i++)
|
| | | {
|
| | | CLayerDescription *layer = new CLayerDescription();
|
| | | if(!layer)
|
| | | {
|
| | | DeleteObjAndFalse(description);
|
| | | }
|
| | | layer.type = defNeuronBaseOCL;
|
| | | layer.count = D2_STUDY_DIM;
|
| | | layer.activation = None;
|
| | | layer.optimization = ADAM;
|
| | | if(!description.Add(layer))
|
| | | {
|
| | | DeleteObj(layer);
|
| | | DeleteObjAndFalse(description);
|
| | | }
|
| | | }
|
| | | const bool created = g_net.Create(description);
|
| | | delete description;
|
| | | return(created && g_net.GetOpenCL() != NULL);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool Put(CBufferFloat *buffer, const float first, const float second)
|
| | | {
|
| | | float values[2] = {first, second};
|
| | | return(buffer && buffer.AssignArray(values) && buffer.BufferWrite());
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool ReadSelection(CD2SkillBank &bank)
|
| | | {
|
| | | return(bank.SelectedSlot().BufferRead() && bank.SelectedScore().BufferRead() &&
|
| | | bank.SelectedCorrection().BufferRead());
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Confirmation data use a stable account-side query. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool Train(CD2SkillBank &bank, const float gradient_x, const float gradient_y)
|
| | | {
|
| | | if(!Put(g_source.getOutput(), 1.0f, 0.0f) || !bank.FeedForward(g_source.AsObject()) ||
|
| | | !Put(g_source.getGradient(), gradient_x, gradient_y) ||
|
| | | !bank.UpdateFromGradient(g_source.getOutput(), g_source.getGradient()))
|
| | | ReturnFalse;
|
| | | return(true);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool ReadLifecycle(CD2SkillBank &bank)
|
| | | {
|
| | | return(bank.RefreshDiagnostics() && bank.Diagnostics().BufferRead() &&
|
| | | bank.Used().BufferRead() && bank.State().BufferRead());
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Two close keys intentionally own corrections with opposite sign.|
|
| | | //+------------------------------------------------------------------+
|
| | | bool SeedCloseOpposingCorrections(CD2SkillBank &bank)
|
| | | {
|
| | | float keys[4] = {1.00f, 0.00f, 0.98f, 0.20f};
|
| | | float corrections[4] = {0.40f, 0.00f, -0.40f, 0.00f};
|
| | | float directions[4] = {1.00f, 0.00f, -1.00f, 0.00f};
|
| | | float scales[2] = {0.40f, 0.40f};
|
| | | float utility[2] = {0.00f, 0.00f};
|
| | | float observations[2] = {2.00f, 2.00f};
|
| | | float uses[2] = {0.00f, 0.00f};
|
| | | float mass[2] = {1.00f, 1.00f};
|
| | | float used[2] = {1.00f, 1.00f};
|
| | | float states[2] = {(float)D2SKILL_ACTIVE, (float)D2SKILL_ACTIVE};
|
| | | float ages[2] = {0.00f, 0.00f};
|
| | | float protection[2] = {0.00f, 0.00f};
|
| | | float influence[2] = {0.00f, 0.00f};
|
| | | return(bank.Keys().AssignArray(keys) && bank.Keys().BufferWrite() &&
|
| | | bank.Corrections().AssignArray(corrections) && bank.Corrections().BufferWrite() &&
|
| | | bank.Directions().AssignArray(directions) && bank.Directions().BufferWrite() &&
|
| | | bank.Scales().AssignArray(scales) && bank.Scales().BufferWrite() &&
|
| | | bank.Utility().AssignArray(utility) && bank.Utility().BufferWrite() &&
|
| | | bank.ObservationsBank().AssignArray(observations) && bank.ObservationsBank().BufferWrite() &&
|
| | | bank.Uses().AssignArray(uses) && bank.Uses().BufferWrite() &&
|
| | | bank.MassBank().AssignArray(mass) && bank.MassBank().BufferWrite() &&
|
| | | bank.Used().AssignArray(used) && bank.Used().BufferWrite() &&
|
| | | bank.State().AssignArray(states) && bank.State().BufferWrite() &&
|
| | | bank.Ages().AssignArray(ages) && bank.Ages().BufferWrite() &&
|
| | | bank.Protection().AssignArray(protection) && bank.Protection().BufferWrite() &&
|
| | | bank.Influence().AssignArray(influence) && bank.Influence().BufferWrite());
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Nearby account states must select their matching signed residual.|
|
| | | //+------------------------------------------------------------------+
|
| | | bool RunCorrectionSelection(void)
|
| | | {
|
| | | CD2SkillBank bank;
|
| | | if(!bank.Init(D2_STUDY_DIM, 100, g_net.GetOpenCL(), D2_STUDY_SLOTS, ADAM, 1) ||
|
| | | !bank.SetThresholds(0.50f, 0.50f) || !bank.SetAlpha(1.00f) ||
|
| | | !bank.SetUtilityPolicy(false, -1.00f) || !bank.SetEnabled(true) ||
|
| | | !SeedCloseOpposingCorrections(bank))
|
| | | ReturnFalse;
|
| | | if(!Put(g_source.getOutput(), 1.00f, 0.00f) || !bank.FeedForward(g_source.AsObject()) ||
|
| | | !ReadSelection(bank))
|
| | | ReturnFalse;
|
| | | const float aligned_slot = bank.SelectedSlot()[0];
|
| | | const float aligned_correction = bank.SelectedCorrection()[0];
|
| | | if(!Put(g_source.getOutput(), 0.98f, 0.20f) || !bank.FeedForward(g_source.AsObject()) ||
|
| | | !ReadSelection(bank))
|
| | | ReturnFalse;
|
| | | const float opposing_slot = bank.SelectedSlot()[0];
|
| | | const float opposing_correction = bank.SelectedCorrection()[0];
|
| | | PrintFormat("D2SkillBankStudy: corrections aligned_slot=%.0f aligned=%.4f "
|
| | | "opposing_slot=%.0f opposing=%.4f",
|
| | | aligned_slot, aligned_correction, opposing_slot, opposing_correction);
|
| | | return(aligned_slot == 0.0f && aligned_correction > 0.0f &&
|
| | | opposing_slot == 1.0f && opposing_correction < 0.0f);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Candidate becomes an ordinary slot, then evicts only inactive. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool RunCandidateAndEviction(void)
|
| | | {
|
| | | CD2SkillBank bank;
|
| | | if(!bank.Init(D2_STUDY_DIM, 200, g_net.GetOpenCL(), D2_STUDY_SLOTS, ADAM, 1) ||
|
| | | !bank.SetThresholds(0.80f, 0.80f) || !bank.SetLifecycle(2, 0, 2) ||
|
| | | !bank.SetUtilityPolicy(false, -1.00f) || !bank.SetEnabled(true) ||
|
| | | !Train(bank, 1.00f, 0.00f) || !Train(bank, 1.00f, 0.00f) ||
|
| | | !Train(bank, -1.00f, 0.00f) || !Train(bank, -1.00f, 0.00f) ||
|
| | | !Train(bank, 0.00f, 1.00f) || !Train(bank, 0.00f, 1.00f) ||
|
| | | !ReadLifecycle(bank))
|
| | | ReturnFalse;
|
| | | const float candidate_before = bank.Diagnostics()[10];
|
| | | const float evicted_before = bank.Diagnostics()[7];
|
| | | if(candidate_before != 1.0f || evicted_before != 0.0f)
|
| | | ReturnFalse;
|
| | | if(!Train(bank, 0.00f, 1.00f) || !ReadLifecycle(bank))
|
| | | ReturnFalse;
|
| | | const float evicted_after = bank.Diagnostics()[7];
|
| | | const float inactive_after = bank.Diagnostics()[13];
|
| | | PrintFormat("D2SkillBankStudy: candidate candidate=%.0f evicted_before=%.0f "
|
| | | "evicted_after=%.0f inactive_after=%.0f",
|
| | | candidate_before, evicted_before, evicted_after, inactive_after);
|
| | | return(evicted_after >= 1.0f && inactive_after <= 1.0f);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-08-21 08:18:10 +03:00 | | | //| A promoted DirectionMagnitude candidate keeps scale as its norm. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool RunDirectionMagnitudePromotion(void)
|
| | | {
|
| | | CD2SkillBank bank;
|
| | | if(!bank.Init(D2_STUDY_DIM, 250, g_net.GetOpenCL(), D2_STUDY_SLOTS, ADAM, 1) ||
|
| | | !bank.SetThresholds(0.80f, -1.00f) || !bank.SetLifecycle(2, 0, 2) ||
|
| | | !bank.SetMaxCorrection(10.00f) || !bank.SetUtilityPolicy(false, -1.00f) ||
|
| | | !bank.SetEnabled(true) || !Train(bank, 1.00f, 0.00f) ||
|
| | | !Train(bank, 1.00f, 1.00f) || !Put(g_source.getOutput(), 1.00f, 0.00f) ||
|
| | | !bank.FeedForward(g_source.AsObject()) || !ReadSelection(bank) ||
|
| | | !bank.Directions().BufferRead() || !bank.Scales().BufferRead())
|
| | | ReturnFalse;
|
| | | const int slot = (int)bank.SelectedSlot()[0];
|
| | | if(slot < 0 || slot >= (int)D2_STUDY_SLOTS)
|
| | | ReturnFalse;
|
| | | const int offset = slot * (int)D2_STUDY_DIM;
|
| | | const float direction_norm = (float)MathSqrt(bank.Directions()[offset] *
|
| | | bank.Directions()[offset] + bank.Directions()[offset + 1] *
|
| | | bank.Directions()[offset + 1]);
|
| | | const float correction_norm = (float)MathSqrt(bank.SelectedCorrection()[0] *
|
| | | bank.SelectedCorrection()[0] + bank.SelectedCorrection()[1] *
|
| | | bank.SelectedCorrection()[1]);
|
| | | const float scale = bank.Scales()[slot];
|
| | | PrintFormat("D2SkillBankStudy: direction_magnitude slot=%d direction_norm=%.8f "
|
| | | "correction_norm=%.8f scale=%.8f", slot, direction_norm,
|
| | | correction_norm, scale);
|
| | | return(MathAbs(direction_norm - 1.00f) <= D2_STUDY_EPSILON &&
|
| | | MathAbs(correction_norm - scale) <= D2_STUDY_EPSILON);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-08-20 22:59:17 +03:00 | | | //| Protected slots reject an unrelated confirmed candidate. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool RunProtectedRejection(void)
|
| | | {
|
| | | CD2SkillBank bank;
|
| | | if(!bank.Init(D2_STUDY_DIM, 300, g_net.GetOpenCL(), D2_STUDY_SLOTS, ADAM, 1) ||
|
| | | !bank.SetThresholds(0.80f, 0.80f) || !bank.SetLifecycle(2, 100, 1000) ||
|
| | | !bank.SetUtilityPolicy(false, -1.00f) || !bank.SetEnabled(true) ||
|
| | | !Train(bank, 1.00f, 0.00f) || !Train(bank, 1.00f, 0.00f) ||
|
| | | !Train(bank, -1.00f, 0.00f) || !Train(bank, -1.00f, 0.00f) ||
|
| | | !Train(bank, 0.00f, 1.00f) || !Train(bank, 0.00f, 1.00f) ||
|
| | | !ReadLifecycle(bank))
|
| | | ReturnFalse;
|
| | | const float rejected = bank.Diagnostics()[5];
|
| | | const float evicted = bank.Diagnostics()[7];
|
| | | const float protected_slots = bank.Diagnostics()[11];
|
| | | PrintFormat("D2SkillBankStudy: protected rejected=%.0f evicted=%.0f protected=%.0f",
|
| | | rejected, evicted, protected_slots);
|
| | | return(rejected >= 1.0f && evicted == 0.0f && protected_slots == D2_STUDY_SLOTS);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| A query outside the acceptance gate must take the zero NoSkill path.|
|
| | | //+------------------------------------------------------------------+
|
| | | bool RunNoSkill(void)
|
| | | {
|
| | | CD2SkillBank bank;
|
| | | if(!bank.Init(D2_STUDY_DIM, 400, g_net.GetOpenCL(), D2_STUDY_SLOTS, ADAM, 1) ||
|
| | | !bank.SetThresholds(0.50f, 0.50f) || !bank.SetUtilityPolicy(false, -1.00f) ||
|
| | | !bank.SetEnabled(true) || !SeedCloseOpposingCorrections(bank) ||
|
| | | !Put(g_source.getOutput(), -1.00f, 0.00f) || !bank.FeedForward(g_source.AsObject()) ||
|
| | | !ReadSelection(bank))
|
| | | ReturnFalse;
|
| | | const float correction_norm = bank.SelectedCorrection()[0] * bank.SelectedCorrection()[0] +
|
| | | bank.SelectedCorrection()[1] * bank.SelectedCorrection()[1];
|
| | | PrintFormat("D2SkillBankStudy: noskill slot=%.0f correction_norm=%.8f",
|
| | | bank.SelectedSlot()[0], correction_norm);
|
| | | return(bank.SelectedSlot()[0] == -1.0f && correction_norm <= D2_STUDY_EPSILON);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Save/Load preserves durable slots and gives the same next winner.|
|
| | | //+------------------------------------------------------------------+
|
| | | bool RunPersistenceWinner(void)
|
| | | {
|
| | | CD2SkillBank original;
|
| | | if(!original.Init(D2_STUDY_DIM, 500, g_net.GetOpenCL(), D2_STUDY_SLOTS, ADAM, 1) ||
|
| | | !original.SetThresholds(0.50f, 0.50f) || !original.SetAlpha(1.00f) ||
|
| | | !original.SetUtilityPolicy(false, -1.00f) || !original.SetEnabled(true) ||
|
| | | !SeedCloseOpposingCorrections(original) || !Put(g_source.getOutput(), 0.98f, 0.20f) ||
|
| | | !original.FeedForward(g_source.AsObject()) || !ReadSelection(original))
|
| | | ReturnFalse;
|
| | | const float expected_slot = original.SelectedSlot()[0];
|
| | | const float expected_score = original.SelectedScore()[0];
|
| | | const float expected_correction = original.SelectedCorrection()[0];
|
| | | int handle = FileOpen(D2_STUDY_FILE, FILE_WRITE | FILE_BIN | FILE_COMMON);
|
| | | if(handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | if(!original.Save(handle))
|
| | | {
|
| | | FileClose(handle);
|
| | | ReturnFalse;
|
| | | }
|
| | | FileClose(handle);
|
| | | CD2SkillBank restored;
|
| | | handle = FileOpen(D2_STUDY_FILE, FILE_READ | FILE_BIN | FILE_COMMON | FILE_SHARE_READ);
|
| | | if(handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | if(!restored.Load(handle, g_net.GetOpenCL()))
|
| | | {
|
| | | FileClose(handle);
|
| | | ReturnFalse;
|
| | | }
|
| | | FileClose(handle);
|
| | | if(!Put(g_source.getOutput(), 0.98f, 0.20f) || !restored.FeedForward(g_source.AsObject()) ||
|
| | | !ReadSelection(restored))
|
| | | ReturnFalse;
|
| | | const float slot_delta = MathAbs(restored.SelectedSlot()[0] - expected_slot);
|
| | | const float score_delta = MathAbs(restored.SelectedScore()[0] - expected_score);
|
| | | const float correction_delta = MathAbs(restored.SelectedCorrection()[0] - expected_correction);
|
| | | PrintFormat("D2SkillBankStudy: persistence winner=%.0f slot_delta=%.8f "
|
| | | "score_delta=%.8f correction_delta=%.8f",
|
| | | restored.SelectedSlot()[0], slot_delta, score_delta, correction_delta);
|
| | | return(slot_delta <= D2_STUDY_EPSILON && score_delta <= D2_STUDY_EPSILON &&
|
| | | correction_delta <= D2_STUDY_EPSILON);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | int OnInit(void)
|
| | | {
|
| | | if(!CreateHost() ||
|
| | | !g_source.Init(0, 10, g_net.GetOpenCL(), D2_STUDY_DIM, ADAM, 1) ||
|
| | | !RunCorrectionSelection() || !RunCandidateAndEviction() ||
|
2026-08-21 08:18:10 +03:00 | | | !RunDirectionMagnitudePromotion() ||
|
2026-08-20 22:59:17 +03:00 | | | !RunProtectedRejection() || !RunNoSkill() || !RunPersistenceWinner())
|
| | | {
|
| | | PrintFormat("D2SkillBankStudy: FAIL error=%d", GetLastError());
|
| | | return(INIT_FAILED);
|
| | | }
|
2026-08-21 08:18:10 +03:00 | | | Print("D2SkillBankStudy: PASS corrections candidate direction protected eviction noskill persistence");
|
2026-08-20 22:59:17 +03:00 | | | g_completed = true;
|
| | | if(!EventSetMillisecondTimer(1))
|
| | | return(INIT_FAILED);
|
| | | return(INIT_SUCCEEDED);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | void OnTimer(void)
|
| | | {
|
| | | if(g_completed)
|
| | | ExpertRemove();
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | void OnDeinit(const int reason)
|
| | | {
|
| | | EventKillTimer();
|
| | | g_source.Clear();
|
| | | }
|
| | | //+------------------------------------------------------------------+
|