2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | //| NeuroNet_RAGMemory.mqh |
|
| | | //| RAG memory inference layer implementation (library DNG) |
|
| | | //| Copyright DNG® |
|
| | | //| https://www.mql5.com/ru/users/dng |
|
| | | //+------------------------------------------------------------------+
|
| | | #ifndef NEURONET_RAG_MEMORY_MQH
|
| | | #define NEURONET_RAG_MEMORY_MQH
|
| | | /// \file
|
| | | /// \brief NeuroNet_RAGMemory.mqh
|
| | | /// Immutable RAG memory layer implementation: GPU TopK retrieval
|
| | | /// and copy-on-write snapshot publication.
|
| | | /// \author <A HREF="https://www.mql5.com/en/users/dng"> DNG </A>
|
| | | /// \copyright Copyright 2026, DNG
|
| | | //+------------------------------------------------------------------+
|
| | | //| RAG memory local constants |
|
| | | //+------------------------------------------------------------------+
|
| | | #define RAG_MAX_FLOAT 3.402823466e+38f
|
| | | #define RAG_TERMINAL_EPSILON 1.0e-6f
|
| | | #define RAG_NORM_MIN 0.999
|
| | | #define RAG_NORM_MAX 1.001
|
| | | #define RAG_SNAPSHOT_MAGIC 0x52414731
|
| | | #define RAG_FNV_PRIME 1099511628211
|
| | | #define RAG_FNV_OFFSET 1469598103934665603
|
| | | #define RAG_MAX_CANDIDATES 65536
|
| | | #define RAG_FORMAT_SAVE 2
|
| | | #define RAG_FORMAT_PUBLISHED 3
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Constructor: initializes the CRAGInferenceSet layer defaults. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | CRAGInferenceSet::CRAGInferenceSet(void) : opencl(NULL), centroids(NULL), used(NULL),
|
| | | scores(NULL), partial(NULL), merge(NULL), top(NULL), actions(NULL), actionUsed(NULL),
|
| | | actionMeanReward(NULL), actionHitCount(NULL), absoluteRelevance(NULL), tokens(NULL),
|
| | | fingerprint(0)
|
| | | {
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Destructor: releases the CRAGInferenceSet GPU resources. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | CRAGInferenceSet::~CRAGInferenceSet(void)
|
| | | {
|
| | | if(centroids != NULL)
|
| | | delete centroids;
|
| | | if(used != NULL)
|
| | | delete used;
|
| | | if(scores != NULL)
|
| | | delete scores;
|
| | | if(partial != NULL)
|
| | | delete partial;
|
| | | if(merge != NULL)
|
| | | delete merge;
|
| | | if(top != NULL)
|
| | | delete top;
|
| | | if(actions != NULL)
|
| | | delete actions;
|
| | | if(actionUsed != NULL)
|
| | | delete actionUsed;
|
| | | if(actionMeanReward != NULL)
|
| | | delete actionMeanReward;
|
| | | if(actionHitCount != NULL)
|
| | | delete actionHitCount;
|
| | | if(absoluteRelevance != NULL)
|
| | | delete absoluteRelevance;
|
| | | if(tokens != NULL)
|
| | | delete tokens;
|
| | | opencl = NULL;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | // CLayerDescription mapping for defNeuronRAGMemory:
|
| | | // window=scenario embedding, window_out=action dimension, count=token width,
|
| | | // layers=TopK, units[0]=scenario capacity, heads[0]=action capacity.
|
| | | // This is structural model metadata. OnlineMemorySize is runtime-only.
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Constructor: initializes the CNeuronRAGMemory layer defaults. |
|
| | | //+------------------------------------------------------------------+
|
2026-08-18 02:41:45 +03:00 | | | CNeuronRAGMemory::CNeuronRAGMemory(void) : iScenarioEmbedding(0),
|
| | | iActionDimension(0), iTokenWidth(0), iScenarioCapacity(0),
|
| | | iActionCapacity(0), iTopK(0), iOnlineMemorySize(0), iScenarioCount(0),
|
| | | iFormatVersion(RAG_FORMAT_SAVE), m_inference(NULL), iPublicationGeneration(0), iNextPendingId(1)
|
| | | {
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Constructor: initializes the CNeuronRAGMemory layer defaults. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | CNeuronRAGMemory::CNeuronRAGMemory(CLayerDescription &description) : iScenarioEmbedding(0),
|
| | | iActionDimension(0), iTokenWidth(0), iScenarioCapacity(0),
|
| | | iActionCapacity(0), iTopK(0), iOnlineMemorySize(0), iScenarioCount(0),
|
| | | iFormatVersion(RAG_FORMAT_SAVE), m_inference(NULL), iPublicationGeneration(0), iNextPendingId(1)
|
| | | {
|
| | | Configure(description);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Destructor: releases the CNeuronRAGMemory GPU resources. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | CNeuronRAGMemory::~CNeuronRAGMemory(void)
|
| | | {
|
| | | if(m_inference != NULL)
|
| | | delete m_inference;
|
| | | m_inference = NULL;
|
| | | ArrayResize(pending_records, 0);
|
| | | ArrayResize(pending_facts, 0);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory configure operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Configure(CLayerDescription &description)
|
| | | {
|
| | | if(description.type != defNeuronRAGMemory || description.window == 0 ||
|
| | | description.window_out == 0 || description.count == 0 || description.layers == 0 ||
|
| | | ArraySize(description.units) < 1 || ArraySize(description.heads) < 1 ||
|
| | | description.units[0] == 0 || description.heads[0] == 0 ||
|
| | | description.count != description.window_out + 2 ||
|
| | | description.layers > RAG_TOPK_MAX || description.units[0] > RAG_FLOAT_INDEX_LIMIT ||
|
| | | description.units[0] >= (uint)INT_MAX || description.heads[0] >= (uint)INT_MAX)
|
| | | ReturnFalse;
|
| | | iScenarioEmbedding = description.window;
|
| | | iActionDimension = description.window_out;
|
| | | iTokenWidth = description.count;
|
| | | iTopK = description.layers;
|
| | | iScenarioCapacity = description.units[0];
|
| | | iActionCapacity = description.heads[0];
|
| | | return true;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory clear terminal operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | void CNeuronRAGMemory::ClearTerminal(SRAGTerminalOutcome &terminal) const
|
| | | {
|
| | | terminal.relative_return = 0.0f;
|
| | | terminal.maxDrawdown = 0.0f;
|
| | | terminal.costs = 0.0f;
|
| | | terminal.riskUsage = 0.0f;
|
| | | terminal.duration = 0.0f;
|
| | | terminal.reward = 0.0f;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory normalize action descriptor |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::NormalizeActionDescriptor(const float &source[], const double equity,
|
| | | const double riskBudget, const double pointCost,
|
| | | const double point, const double volatility,
|
| | | const double volatilityRange,
|
| | | const double maximumTakeProfit,
|
| | | const double maximumStopLoss,
|
| | | float &destination[]) const
|
| | | {
|
| | | ArrayInitialize(destination, 0.0f);
|
| | | // The generic RAG layer owns the numerical canonical descriptor, but its
|
| | | // caller owns the interpretation and supplies every scale explicitly.
|
| | | if(iActionDimension != 6 || ArraySize(source) != (int)iActionDimension ||
|
| | | ArraySize(destination) != (int)iActionDimension || equity <= 0 || riskBudget <= 0 ||
|
| | | pointCost <= 0 || point <= 0 || volatility <= 0 || volatilityRange <= 0 ||
|
| | | maximumTakeProfit < 0 || maximumStopLoss < 0 ||
|
| | | !MathIsValidNumber(equity) || !MathIsValidNumber(riskBudget) ||
|
| | | !MathIsValidNumber(pointCost) || !MathIsValidNumber(point) ||
|
| | | !MathIsValidNumber(volatility) || !MathIsValidNumber(volatilityRange) ||
|
| | | !MathIsValidNumber(maximumTakeProfit) || !MathIsValidNumber(maximumStopLoss))
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | if(!MathIsValidNumber(source[element]))
|
| | | ReturnFalse;
|
| | | const double scale = MathMax(volatility * volatilityRange, point);
|
| | | const double buySL = MathMax(double(source[2]), 0.0) * maximumStopLoss * point;
|
| | | const double sellSL = MathMax(double(source[5]), 0.0) * maximumStopLoss * point;
|
| | | const double buyTP = MathMax(double(source[1]), 0.0) * maximumTakeProfit * point;
|
| | | const double sellTP = MathMax(double(source[4]), 0.0) * maximumTakeProfit * point;
|
| | | destination[0] = float(MathMax(MathMin(double(source[0]) * buySL * pointCost / riskBudget, 1.0), 0.0));
|
| | | destination[1] = float(MathMax(MathMin(buyTP / scale, 1.0), 0.0));
|
| | | destination[2] = float(MathMax(MathMin(buySL / scale, 1.0), 0.0));
|
| | | destination[3] = float(MathMax(MathMin(double(source[3]) * sellSL * pointCost / riskBudget, 1.0), 0.0));
|
| | | destination[4] = float(MathMax(MathMin(sellTP / scale, 1.0), 0.0));
|
| | | destination[5] = float(MathMax(MathMin(sellSL / scale, 1.0), 0.0));
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory normalize terminal outcome |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | void CNeuronRAGMemory::NormalizeTerminalOutcome(const double reward, const double maxDrawdown,
|
| | | const double costs, const double riskUsage,
|
| | | const double duration, const double startingEquity,
|
| | | const double riskBudget, const double durationScale,
|
| | | SRAGTerminalOutcome &result) const
|
| | | {
|
| | | ClearTerminal(result);
|
| | | const bool equityValid = (MathIsValidNumber(startingEquity) && startingEquity > 0);
|
| | | const bool riskBudgetValid = (MathIsValidNumber(riskBudget) && riskBudget > 0);
|
| | | const bool durationScaleValid = (MathIsValidNumber(durationScale) && durationScale > 0);
|
| | | if(equityValid)
|
| | | {
|
| | | if(MathIsValidNumber(reward))
|
| | | {
|
| | | result.relative_return = float(reward / startingEquity);
|
| | | result.reward = result.relative_return;
|
| | | }
|
| | | if(MathIsValidNumber(maxDrawdown))
|
| | | result.maxDrawdown = float(MathMax(maxDrawdown, 0.0) / startingEquity);
|
| | | if(MathIsValidNumber(costs))
|
| | | result.costs = float(MathMax(costs, 0.0) / startingEquity);
|
| | | }
|
| | | if(riskBudgetValid && MathIsValidNumber(riskUsage))
|
| | | result.riskUsage = float(MathMax(MathMin(MathMax(riskUsage, 0.0) / riskBudget, 1.0), 0.0));
|
| | | if(durationScaleValid && MathIsValidNumber(duration))
|
| | | result.duration = float(MathMax(MathMin(MathMax(duration, 0.0) / durationScale, 1.0), 0.0));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Initializes the CNeuronRAGMemory layer and allocates its GPU |
|
| | | //| buffers. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::InitializeAction(SRAGActionCentroid &action, const uint scenarioId) const
|
| | | {
|
| | | action.used = false;
|
| | | action.scenarioId = scenarioId;
|
| | | action.hit_count = 0;
|
| | | action.mean_reward = 0.0f;
|
| | | ClearTerminal(action.mean_terminal);
|
| | | if(ArrayResize(action.centroid, (int)iActionDimension) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | ArrayInitialize(action.centroid, 0.0f);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Initializes the CNeuronRAGMemory layer and allocates its GPU |
|
| | | //| buffers. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::InitializeScenario(SRAGScenarioCentroid &scenario, const uint scenarioId) const
|
| | | {
|
| | | scenario.used = false;
|
| | | scenario.scenarioId = scenarioId;
|
| | | scenario.hit_count = 0;
|
| | | if(ArrayResize(scenario.centroid, (int)iScenarioEmbedding) != (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | ArrayInitialize(scenario.centroid, 0.0f);
|
| | | if(ArrayResize(scenario.actions, (int)iActionCapacity) != (int)iActionCapacity)
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | if(!InitializeAction(scenario.actions[action], scenarioId))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy action operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopyAction(const SRAGActionCentroid &source,
|
| | | SRAGActionCentroid &destination) const
|
| | | {
|
| | | if(!ValidAction(source.centroid) ||
|
| | | ArrayResize(destination.centroid, (int)iActionDimension) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | destination.used = source.used;
|
| | | destination.scenarioId = source.scenarioId;
|
| | | destination.hit_count = source.hit_count;
|
| | | destination.mean_reward = source.mean_reward;
|
| | | destination.mean_terminal = source.mean_terminal;
|
| | | ArrayCopy(destination.centroid, source.centroid);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy scenario operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopyScenario(const SRAGScenarioCentroid &source,
|
| | | SRAGScenarioCentroid &destination) const
|
| | | {
|
| | | if(ArraySize(source.centroid) != (int)iScenarioEmbedding ||
|
| | | ArraySize(source.actions) != (int)iActionCapacity ||
|
| | | ArrayResize(destination.centroid, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(destination.actions, (int)iActionCapacity) != (int)iActionCapacity)
|
| | | ReturnFalse;
|
| | | destination.used = source.used;
|
| | | destination.scenarioId = source.scenarioId;
|
| | | destination.hit_count = source.hit_count;
|
| | | ArrayCopy(destination.centroid, source.centroid);
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | if(!CopyAction(source.actions[action], destination.actions[action]))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy scenario flexible operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopyScenarioFlexible(const SRAGScenarioCentroid &source,
|
| | | SRAGScenarioCentroid &destination) const
|
| | | {
|
| | | const int actionTotal = ArraySize(source.actions);
|
| | | if(ArraySize(source.centroid) != (int)iScenarioEmbedding ||
|
| | | actionTotal < (int)iActionCapacity ||
|
| | | ArrayResize(destination.centroid, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(destination.actions, actionTotal) != actionTotal)
|
| | | ReturnFalse;
|
| | | destination.used = source.used;
|
| | | destination.scenarioId = source.scenarioId;
|
| | | destination.hit_count = source.hit_count;
|
| | | ArrayCopy(destination.centroid, source.centroid);
|
| | | for(int action = 0; action < actionTotal; action++)
|
| | | if(!CopyAction(source.actions[action], destination.actions[action]))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory append action candidate operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::AppendActionCandidate(const uint scenarioId,
|
| | | SRAGScenarioCentroid &scenario,
|
| | | const SRAGActionCentroid &incoming) const
|
| | | {
|
| | | if(scenarioId >= iScenarioCapacity || !scenario.used || !incoming.used ||
|
| | | ArraySize(incoming.centroid) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | const int current = ArraySize(scenario.actions);
|
| | | for(int action = 0; action < current; action++)
|
| | | if(!scenario.actions[action].used)
|
| | | {
|
| | | if(!CopyAction(incoming, scenario.actions[action]))
|
| | | ReturnFalse;
|
| | | scenario.actions[action].scenarioId = scenarioId;
|
| | | return true;
|
| | | }
|
| | | if(current < 0 || current >= RAG_MAX_CANDIDATES || ArrayResize(scenario.actions, current + 1) != current + 1 ||
|
| | | !InitializeAction(scenario.actions[current], scenarioId) ||
|
| | | !CopyAction(incoming, scenario.actions[current]))
|
| | | ReturnFalse;
|
| | | scenario.actions[current].scenarioId = scenarioId;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory compact action candidates operation.|
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CompactActionCandidates(SRAGScenarioCentroid &scenario) const
|
| | | {
|
| | | SRAGActionCentroid compacted[];
|
| | | if(ArrayResize(compacted, (int)iActionCapacity) != (int)iActionCapacity)
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | if(!InitializeAction(compacted[action], scenario.scenarioId))
|
| | | ReturnFalse;
|
| | | uint destination = 0;
|
| | | for(int action = 0; action < ArraySize(scenario.actions); action++)
|
| | | if(scenario.actions[action].used)
|
| | | {
|
| | | if(destination >= iActionCapacity ||
|
| | | !CopyAction(scenario.actions[action], compacted[destination]))
|
| | | ReturnFalse;
|
| | | compacted[destination].scenarioId = scenario.scenarioId;
|
| | | destination++;
|
| | | }
|
| | | ArraySwap(scenario.actions, compacted);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory normalize l2 operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::NormalizeL2(const float &source[], float &destination[]) const
|
| | | {
|
| | | const int total = ArraySize(source);
|
| | | if(total <= 0 || ArrayResize(destination, total) != total)
|
| | | ReturnFalse;
|
| | | double squareSum = 0.0;
|
| | | for(int i = 0; i < total; i++)
|
| | | {
|
| | | if(!MathIsValidNumber(source[i]))
|
| | | ReturnFalse;
|
| | | squareSum += (double)source[i] * source[i];
|
| | | }
|
| | | if(!MathIsValidNumber(squareSum) || squareSum <= DBL_EPSILON)
|
| | | ReturnFalse;
|
| | | const double length = MathSqrt(squareSum);
|
| | | for(int i = 0; i < total; i++)
|
| | | destination[i] = (float)(source[i] / length);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory squared distance operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | float CNeuronRAGMemory::SquaredDistance(const float &left[], const float &right[]) const
|
| | | {
|
| | | const float maxFloat = RAG_MAX_FLOAT;
|
| | | if(ArraySize(left) != ArraySize(right) || ArraySize(left) <= 0)
|
| | | return maxFloat;
|
| | | float distance = 0.0f;
|
| | | for(int i = 0; i < ArraySize(left); i++)
|
| | | {
|
| | | if(!MathIsValidNumber(left[i]) || !MathIsValidNumber(right[i]))
|
| | | return maxFloat;
|
| | | const float delta = (float)(left[i] - right[i]);
|
| | | const float squared = (float)(delta * delta);
|
| | | distance = (float)(distance + squared);
|
| | | }
|
| | | return (MathIsValidNumber(distance) ? distance : maxFloat);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory valid action operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ValidAction(const float &action[]) const
|
| | | {
|
| | | if(ArraySize(action) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | if(!MathIsValidNumber(action[element]) || action[element] < 0.0f || action[element] > 1.0f)
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory valid terminal operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ValidTerminal(const SRAGTerminalOutcome &terminal) const
|
| | | {
|
| | | return (MathIsValidNumber(terminal.relative_return) &&
|
| | | MathIsValidNumber(terminal.maxDrawdown) &&
|
| | | MathIsValidNumber(terminal.costs) &&
|
| | | MathIsValidNumber(terminal.riskUsage) &&
|
| | | MathIsValidNumber(terminal.duration) &&
|
| | | MathIsValidNumber(terminal.reward) &&
|
| | | terminal.maxDrawdown >= 0.0f && terminal.costs >= 0.0f &&
|
| | | terminal.riskUsage >= 0.0f && terminal.riskUsage <= 1.0f &&
|
| | | terminal.duration >= 0.0f && terminal.duration <= 1.0f &&
|
| | | MathAbs(terminal.reward - terminal.relative_return) <= RAG_TERMINAL_EPSILON);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory merge terminal operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | void CNeuronRAGMemory::MergeTerminal(SRAGTerminalOutcome &destination,
|
| | | const ulong destinationHits,
|
| | | const SRAGTerminalOutcome &source,
|
| | | const ulong sourceHits) const
|
| | | {
|
| | | const double total = (double)destinationHits + (double)sourceHits;
|
| | | destination.relative_return = (float)((destination.relative_return * destinationHits +
|
| | | source.relative_return * sourceHits) / total);
|
| | | destination.maxDrawdown = (float)((destination.maxDrawdown * destinationHits +
|
| | | source.maxDrawdown * sourceHits) / total);
|
| | | destination.costs = (float)((destination.costs * destinationHits +
|
| | | source.costs * sourceHits) / total);
|
| | | destination.riskUsage = (float)((destination.riskUsage * destinationHits +
|
| | | source.riskUsage * sourceHits) / total);
|
| | | destination.duration = (float)((destination.duration * destinationHits +
|
| | | source.duration * sourceHits) / total);
|
| | | destination.reward = (float)((destination.reward * destinationHits +
|
| | | source.reward * sourceHits) / total);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory first free scenario operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | int CNeuronRAGMemory::FirstFreeScenario() const
|
| | | {
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!scenarios[scenario].used)
|
| | | return (int)scenario;
|
| | | return -1;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory first free action operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | int CNeuronRAGMemory::FirstFreeAction(const uint scenarioId) const
|
| | | {
|
| | | if(scenarioId >= iScenarioCapacity || !scenarios[scenarioId].used)
|
| | | return -1;
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | if(!scenarios[scenarioId].actions[action].used)
|
| | | return (int)action;
|
| | | return -1;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory rebind scenario actions operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::RebindScenarioActions(SRAGScenarioCentroid &scenario,
|
| | | const uint destinationScenarioId) const
|
| | | {
|
| | | scenario.scenarioId = destinationScenarioId;
|
| | | for(int action = 0; action < ArraySize(scenario.actions); action++)
|
| | | scenario.actions[action].scenarioId = destinationScenarioId;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory merge action candidates operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::MergeActionCandidates(const uint scenarioId,
|
| | | SRAGActionCentroid &destination,
|
| | | const SRAGActionCentroid &source) const
|
| | | {
|
| | | if(!destination.used || !source.used || destination.hit_count == 0 || source.hit_count == 0 ||
|
| | | !ValidAction(destination.centroid) || !ValidAction(source.centroid) ||
|
| | | !ValidTerminal(destination.mean_terminal) || !ValidTerminal(source.mean_terminal) ||
|
| | | !MathIsValidNumber(destination.mean_reward) || !MathIsValidNumber(source.mean_reward) ||
|
| | | destination.hit_count > ulong(-1) - source.hit_count)
|
| | | ReturnFalse;
|
| | | const ulong destinationHits = destination.hit_count;
|
| | | const ulong sourceHits = source.hit_count;
|
| | | const double total = (double)destinationHits + (double)sourceHits;
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | destination.centroid[element] = (float)((destination.centroid[element] * destinationHits +
|
| | | source.centroid[element] * sourceHits) / total);
|
| | | destination.mean_reward = (float)((destination.mean_reward * destinationHits +
|
| | | source.mean_reward * sourceHits) / total);
|
| | | MergeTerminal(destination.mean_terminal, destinationHits, source.mean_terminal, sourceHits);
|
| | | destination.hit_count = destinationHits + sourceHits;
|
| | | destination.used = true;
|
| | | destination.scenarioId = scenarioId;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory pair count safe operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::PairCountSafe(const uint candidateCount, int &pairCount) const
|
| | | {
|
| | | pairCount = 0;
|
| | | if(candidateCount < 2 || candidateCount > RAG_MAX_CANDIDATES)
|
| | | ReturnFalse;
|
| | | const ulong pairs = ((candidateCount & 1) == 0 ?
|
| | | (ulong)(candidateCount / 2) * (ulong)(candidateCount - 1) :
|
| | | (ulong)candidateCount * (ulong)((candidateCount - 1) / 2));
|
| | | if(pairs > (ulong)INT_MAX)
|
| | | ReturnFalse;
|
| | | pairCount = (int)pairs;
|
| | | return true;
|
| | | }
|
| | |
|
| | | // A launch reduces every independent candidate row in parallel. The device
|
| | | // returns only one float4 per row; the immutable snapshot itself stays on the
|
| | | // host until the count-weighted COW transaction has succeeded.
|
2026-09-07 23:58:52 +03:00 | | | //+------------------------------------------------------------------+
|
| | | //| Handles the CNeuronRAGMemory find nearest pairs gpu operation. |
|
| | | //+------------------------------------------------------------------+
|
2026-08-18 02:41:45 +03:00 | | | bool CNeuronRAGMemory::FindNearestPairsGPU(const float &candidateCentroids[],
|
| | | const float &candidateUsed[],
|
| | | const uint candidateCount, const uint dimension,
|
| | | const uint batchCount, int &destinations[], int &sources[])
|
| | | {
|
| | | ArrayResize(destinations, 0);
|
| | | ArrayResize(sources, 0);
|
| | | int pairCount = 0;
|
| | | if(OpenCL == NULL || candidateCount < 2 || dimension == 0 || batchCount == 0 ||
|
| | | candidateCount > RAG_MAX_CANDIDATES || dimension > (uint)INT_MAX || batchCount > (uint)INT_MAX ||
|
| | | batchCount > UINT_MAX / 64 || batchCount > (uint)INT_MAX / 4 ||
|
| | | !PairCountSafe(candidateCount, pairCount) || pairCount <= 0 ||
|
| | | (ulong)candidateCount * (ulong)batchCount > ULONG_MAX / (ulong)dimension)
|
| | | ReturnFalse;
|
| | | const ulong rows = (ulong)candidateCount * (ulong)batchCount;
|
| | | const ulong values = rows * (ulong)dimension;
|
| | | if(values > (ulong)INT_MAX || rows > (ulong)INT_MAX)
|
| | | ReturnFalse;
|
| | | if(ArraySize(candidateCentroids) != (int)values || ArraySize(candidateUsed) != (int)rows)
|
| | | ReturnFalse;
|
| | | if(ArrayResize(destinations, (int)batchCount) != (int)batchCount ||
|
| | | ArrayResize(sources, (int)batchCount) != (int)batchCount)
|
| | | ReturnFalse;
|
| | | ArrayInitialize(destinations, -1);
|
| | | ArrayInitialize(sources, -1);
|
| | | // BufferCreate reuses the allocation and performs BufferWrite when OpenCL
|
| | | // and the shape are unchanged. BufferInit frees only on a shape change.
|
| | | if(!cNearestCentroids.BufferInit((uint)values, 0.0f) ||
|
| | | !cNearestCentroids.AssignArray(candidateCentroids) ||
|
| | | !cNearestCentroids.BufferCreate(OpenCL) ||
|
| | | !cNearestUsed.BufferInit((uint)rows, 0.0f) ||
|
| | | !cNearestUsed.AssignArray(candidateUsed) || !cNearestUsed.BufferCreate(OpenCL) ||
|
| | | !cNearestResult.BufferInit(batchCount * 4, 0.0f) || !cNearestResult.BufferCreate(OpenCL))
|
| | | ReturnFalse;
|
| | | uint offset[] = {0};
|
| | | uint global[] = {batchCount * RAG_TOPK_LOCAL_WIDTH};
|
| | | uint local[] = {RAG_TOPK_LOCAL_WIDTH};
|
| | | if(!OpenCL.SetArgumentBuffer(def_k_RAGNearestPair, def_k_ragn_centroids, cNearestCentroids.GetIndex()) ||
|
| | | !OpenCL.SetArgumentBuffer(def_k_RAGNearestPair, def_k_ragn_used, cNearestUsed.GetIndex()) ||
|
| | | !OpenCL.SetArgumentBuffer(def_k_RAGNearestPair, def_k_ragn_result, cNearestResult.GetIndex()) ||
|
| | | !OpenCL.SetArgument(def_k_RAGNearestPair, def_k_ragn_candidate_count, candidateCount) ||
|
| | | !OpenCL.SetArgument(def_k_RAGNearestPair, def_k_ragn_dimension, dimension) ||
|
| | | !OpenCL.SetArgument(def_k_RAGNearestPair, def_k_ragn_batch_count, batchCount) ||
|
| | | !OpenCL.Execute(def_k_RAGNearestPair, 1, offset, global, local))
|
| | | ReturnFalse;
|
| | | float valuesOut[];
|
| | | if(ArrayResize(valuesOut, (int)(batchCount * 4)) != (int)(batchCount * 4) ||
|
| | | cNearestResult.GetData(valuesOut) != (int)(batchCount * 4))
|
| | | ReturnFalse;
|
| | | for(uint batch = 0; batch < batchCount; batch++)
|
| | | {
|
| | | const uint rowOffset = batch * 4;
|
| | | if(valuesOut[rowOffset + 3] < 0.5f || !MathIsValidNumber(valuesOut[rowOffset]))
|
| | | ReturnFalse;
|
| | | const int left = (int)(valuesOut[rowOffset + 1] + 0.5f);
|
| | | const int right = (int)(valuesOut[rowOffset + 2] + 0.5f);
|
| | | if(left < 0 || right <= left || right >= (int)candidateCount)
|
| | | ReturnFalse;
|
| | | destinations[batch] = left;
|
| | | sources[batch] = right;
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory find nearest pair gpu operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::FindNearestPairGPU(const float &candidateCentroids[],
|
| | | const float &candidateUsed[],
|
| | | const uint candidateCount, const uint dimension,
|
| | | const uint batchCount, int &destination, int &source)
|
| | | {
|
| | | destination = -1;
|
| | | source = -1;
|
| | | int destinations[], sources[];
|
| | | if(!FindNearestPairsGPU(candidateCentroids, candidateUsed, candidateCount, dimension,
|
| | | batchCount, destinations, sources) || ArraySize(destinations) < 1 ||
|
| | | ArraySize(sources) < 1)
|
| | | ReturnFalse;
|
| | | destination = destinations[0];
|
| | | source = sources[0];
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory find nearest action pair operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::FindNearestActionPair(const uint scenarioId,
|
| | | const SRAGActionCentroid &actionCandidates[],
|
| | | int &destination, int &source)
|
| | | {
|
| | | destination = -1;
|
| | | source = -1;
|
| | | if(OpenCL != NULL)
|
| | | {
|
| | | const int candidates = ArraySize(actionCandidates);
|
| | | float flat[], used[];
|
| | | if(candidates < 2 || ArrayResize(flat, candidates * (int)iActionDimension) != candidates * (int)iActionDimension ||
|
| | | ArrayResize(used, candidates) != candidates)
|
| | | ReturnFalse;
|
| | | for(int action = 0; action < candidates; action++)
|
| | | {
|
| | | used[action] = (actionCandidates[action].used ? 1.0f : 0.0f);
|
| | | ArrayCopy(flat, actionCandidates[action].centroid, action * (int)iActionDimension, 0, (int)iActionDimension);
|
| | | }
|
| | | return (scenarioId < iScenarioCapacity && FindNearestPairGPU(flat, used, (uint)candidates,
|
| | | iActionDimension, 1, destination, source));
|
| | | }
|
| | | const float maxFloat = RAG_MAX_FLOAT;
|
| | | float nearest = maxFloat;
|
| | | bool found = false;
|
| | | for(int left = 0; left < ArraySize(actionCandidates); left++)
|
| | | {
|
| | | if(!actionCandidates[left].used)
|
| | | continue;
|
| | | for(int right = left + 1; right < ArraySize(actionCandidates); right++)
|
| | | {
|
| | | if(!actionCandidates[right].used)
|
| | | continue;
|
| | | const float distance = SquaredDistance(actionCandidates[left].centroid,
|
| | | actionCandidates[right].centroid);
|
| | | if(!found || distance < nearest)
|
| | | {
|
| | | nearest = distance;
|
| | | destination = left;
|
| | | source = right;
|
| | | found = true;
|
| | | }
|
| | | }
|
| | | }
|
| | | return (scenarioId < iScenarioCapacity && destination >= 0 && source >= 0);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory consolidate action candidates |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ConsolidateActionCandidates(SRAGScenarioCentroid &candidateSnapshot[])
|
| | | {
|
| | | if(ArraySize(candidateSnapshot) != (int)iScenarioCapacity)
|
| | | ReturnFalse;
|
| | | while(true)
|
| | | {
|
| | | int activeScenarios[];
|
| | | int maxCandidates = 0;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | {
|
| | | if(!candidateSnapshot[scenario].used)
|
| | | continue;
|
| | | int usedActions = 0;
|
| | | const int candidates = ArraySize(candidateSnapshot[scenario].actions);
|
| | | if(candidates < 0 || candidates > 65536)
|
| | | ReturnFalse;
|
| | | for(int action = 0; action < candidates; action++)
|
| | | if(candidateSnapshot[scenario].actions[action].used)
|
| | | usedActions++;
|
| | | if(usedActions > (int)iActionCapacity)
|
| | | {
|
| | | const int active = ArraySize(activeScenarios);
|
| | | if(ArrayResize(activeScenarios, active + 1) != active + 1)
|
| | | ReturnFalse;
|
| | | activeScenarios[active] = (int)scenario;
|
| | | if(candidates > maxCandidates)
|
| | | maxCandidates = candidates;
|
| | | }
|
| | | }
|
| | | const uint batchCount = (uint)ArraySize(activeScenarios);
|
| | | if(batchCount == 0)
|
| | | break;
|
| | | if(OpenCL == NULL)
|
| | | {
|
| | | for(uint batch = 0; batch < batchCount; batch++)
|
| | | {
|
| | | const int scenario = activeScenarios[batch];
|
| | | int destination = -1, source = -1;
|
| | | if(!FindNearestActionPair((uint)scenario, candidateSnapshot[scenario].actions,
|
| | | destination, source) ||
|
| | | !MergeActionCandidates((uint)scenario,
|
| | | candidateSnapshot[scenario].actions[destination],
|
| | | candidateSnapshot[scenario].actions[source]) ||
|
| | | !InitializeAction(candidateSnapshot[scenario].actions[source], (uint)scenario))
|
| | | ReturnFalse;
|
| | | }
|
| | | continue;
|
| | | }
|
| | | const ulong rows = (ulong)batchCount * (ulong)maxCandidates;
|
| | | if(maxCandidates < 2 || rows > (ulong)INT_MAX ||
|
| | | rows > ULONG_MAX / (ulong)iActionDimension ||
|
| | | rows * (ulong)iActionDimension > (ulong)INT_MAX)
|
| | | ReturnFalse;
|
| | | const int values = (int)(rows * (ulong)iActionDimension);
|
| | | float flat[], used[];
|
| | | if(ArrayResize(flat, values) != values || ArrayResize(used, (int)rows) != (int)rows)
|
| | | ReturnFalse;
|
| | | ArrayInitialize(flat, 0.0f);
|
| | | ArrayInitialize(used, 0.0f);
|
| | | for(uint batch = 0; batch < batchCount; batch++)
|
| | | {
|
| | | const int scenario = activeScenarios[batch];
|
| | | for(int action = 0; action < ArraySize(candidateSnapshot[scenario].actions); action++)
|
| | | {
|
| | | if(!candidateSnapshot[scenario].actions[action].used)
|
| | | continue;
|
| | | const int slot = (int)batch * maxCandidates + action;
|
| | | used[slot] = 1.0f;
|
| | | ArrayCopy(flat, candidateSnapshot[scenario].actions[action].centroid,
|
| | | slot * (int)iActionDimension, 0, (int)iActionDimension);
|
| | | }
|
| | | }
|
| | | int destinations[], sources[];
|
| | | if(!FindNearestPairsGPU(flat, used, (uint)maxCandidates, iActionDimension, batchCount,
|
| | | destinations, sources) ||
|
| | | ArraySize(destinations) != (int)batchCount || ArraySize(sources) != (int)batchCount)
|
| | | ReturnFalse;
|
| | | for(uint batch = 0; batch < batchCount; batch++)
|
| | | {
|
| | | const int scenario = activeScenarios[batch];
|
| | | const int destination = destinations[batch];
|
| | | const int source = sources[batch];
|
| | | if(destination < 0 || source <= destination ||
|
| | | source >= ArraySize(candidateSnapshot[scenario].actions) ||
|
| | | !candidateSnapshot[scenario].actions[destination].used ||
|
| | | !candidateSnapshot[scenario].actions[source].used ||
|
| | | !MergeActionCandidates((uint)scenario,
|
| | | candidateSnapshot[scenario].actions[destination],
|
| | | candidateSnapshot[scenario].actions[source]) ||
|
| | | !InitializeAction(candidateSnapshot[scenario].actions[source], (uint)scenario))
|
| | | ReturnFalse;
|
| | | }
|
| | | }
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!CompactActionCandidates(candidateSnapshot[scenario]) ||
|
| | | !RebindScenarioActions(candidateSnapshot[scenario], scenario))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory merge scenario candidates operation.|
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::MergeScenarioCandidates(SRAGScenarioCentroid &destination,
|
| | | const SRAGScenarioCentroid &source)
|
| | | {
|
| | | if(!destination.used || !source.used || destination.hit_count == 0 || source.hit_count == 0 ||
|
| | | ArraySize(destination.centroid) != (int)iScenarioEmbedding ||
|
| | | ArraySize(source.centroid) != (int)iScenarioEmbedding ||
|
| | | destination.hit_count > ulong(-1) - source.hit_count)
|
| | | ReturnFalse;
|
| | | const ulong destinationHits = destination.hit_count;
|
| | | const ulong sourceHits = source.hit_count;
|
| | | float previousCentroid[];
|
| | | if(ArrayResize(previousCentroid, (int)iScenarioEmbedding) != (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | ArrayCopy(previousCentroid, destination.centroid);
|
| | | float weighted[];
|
| | | if(ArrayResize(weighted, (int)iScenarioEmbedding) != (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | weighted[element] = (float)(destination.centroid[element] * destinationHits +
|
| | | source.centroid[element] * sourceHits);
|
| | | // Equal and opposite normalized observations have no unique normalized
|
| | | // mean. Retain the prior valid centroid while still merging their facts.
|
| | | if(!NormalizeL2(weighted, destination.centroid))
|
| | | ArrayCopy(destination.centroid, previousCentroid);
|
| | | destination.hit_count = destinationHits + sourceHits;
|
| | | for(int action = 0; action < ArraySize(source.actions); action++)
|
| | | if(source.actions[action].used &&
|
| | | !AppendActionCandidate(destination.scenarioId, destination, source.actions[action]))
|
| | | ReturnFalse;
|
| | | return RebindScenarioActions(destination, destination.scenarioId);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory find nearest scenario pair |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::FindNearestScenarioPair(const SRAGScenarioCentroid &scenarioCandidates[],
|
| | | int &destination, int &source)
|
| | | {
|
| | | destination = -1;
|
| | | source = -1;
|
| | | if(OpenCL != NULL)
|
| | | {
|
| | | const int candidates = ArraySize(scenarioCandidates);
|
| | | float flat[], used[];
|
| | | if(candidates < 2 || ArrayResize(flat, candidates * (int)iScenarioEmbedding) != candidates * (int)iScenarioEmbedding ||
|
| | | ArrayResize(used, candidates) != candidates)
|
| | | ReturnFalse;
|
| | | for(int scenario = 0; scenario < candidates; scenario++)
|
| | | {
|
| | | used[scenario] = (scenarioCandidates[scenario].used ? 1.0f : 0.0f);
|
| | | ArrayCopy(flat, scenarioCandidates[scenario].centroid, scenario * (int)iScenarioEmbedding, 0,
|
| | | (int)iScenarioEmbedding);
|
| | | }
|
| | | return FindNearestPairGPU(flat, used, (uint)candidates, iScenarioEmbedding, 1, destination, source);
|
| | | }
|
| | | const float maxFloat = RAG_MAX_FLOAT;
|
| | | float nearest = maxFloat;
|
| | | bool found = false;
|
| | | for(int left = 0; left < ArraySize(scenarioCandidates); left++)
|
| | | {
|
| | | if(!scenarioCandidates[left].used)
|
| | | continue;
|
| | | for(int right = left + 1; right < ArraySize(scenarioCandidates); right++)
|
| | | {
|
| | | if(!scenarioCandidates[right].used)
|
| | | continue;
|
| | | const float distance = SquaredDistance(scenarioCandidates[left].centroid,
|
| | | scenarioCandidates[right].centroid);
|
| | | if(!found || distance < nearest)
|
| | | {
|
| | | nearest = distance;
|
| | | destination = left;
|
| | | source = right;
|
| | | found = true;
|
| | | }
|
| | | }
|
| | | }
|
| | | return (destination >= 0 && source >= 0);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory stage scenario candidates operation.|
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::StageScenarioCandidates(const uint sourceScenarioId,
|
| | | const float &scenarioEmbedding[],
|
| | | const float &action[],
|
| | | const SRAGTerminalOutcome &terminal,
|
| | | SRAGScenarioCentroid &scenarioCandidates[])
|
| | | {
|
| | | if(ArraySize(scenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | !ValidAction(action) || !ValidTerminal(terminal) ||
|
| | | ArrayResize(scenarioCandidates, iScenarioCapacity + 1) != (int)(iScenarioCapacity + 1))
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!CopyScenario(scenarios[scenario], scenarioCandidates[scenario]))
|
| | | ReturnFalse;
|
| | | if(!InitializeScenario(scenarioCandidates[iScenarioCapacity], sourceScenarioId) ||
|
| | | !NormalizeL2(scenarioEmbedding, scenarioCandidates[iScenarioCapacity].centroid))
|
| | | ReturnFalse;
|
| | | scenarioCandidates[iScenarioCapacity].used = true;
|
| | | scenarioCandidates[iScenarioCapacity].hit_count = 1;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].used = true;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].scenarioId = sourceScenarioId;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].hit_count = 1;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].mean_reward = terminal.reward;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].mean_terminal = terminal;
|
| | | ArrayCopy(scenarioCandidates[iScenarioCapacity].actions[0].centroid, action);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory add completed scenario operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::AddCompletedScenario(const uint sourceScenarioId,
|
| | | const float &scenarioEmbedding[],
|
| | | const float &action[],
|
| | | const SRAGTerminalOutcome &terminal)
|
| | | {
|
| | | SRAGScenarioCentroid candidateSnapshot[];
|
| | | uint candidateCount = iScenarioCount;
|
| | | if(!CopySnapshot(scenarios, candidateSnapshot) ||
|
| | | !ApplyCompletedRecordToSnapshot(candidateSnapshot, candidateCount, sourceScenarioId,
|
| | | scenarioEmbedding, action, terminal) ||
|
| | | !ConsolidateActionCandidates(candidateSnapshot))
|
| | | ReturnFalse;
|
| | | ArraySwap(scenarios, candidateSnapshot);
|
| | | iScenarioCount = candidateCount;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory apply completed record to snapshot |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ApplyCompletedRecordToSnapshot(SRAGScenarioCentroid &candidateSnapshot[],
|
| | | uint &candidateCount,
|
| | | const uint sourceScenarioId,
|
| | | const float &scenarioEmbedding[],
|
| | | const float &action[],
|
| | | const SRAGTerminalOutcome &terminal)
|
| | | {
|
| | | if(ArraySize(candidateSnapshot) != (int)iScenarioCapacity || candidateCount > iScenarioCapacity ||
|
| | | ArraySize(scenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | !ValidAction(action) || !ValidTerminal(terminal))
|
| | | ReturnFalse;
|
| | | float normalized[];
|
| | | if(!NormalizeL2(scenarioEmbedding, normalized))
|
| | | ReturnFalse;
|
| | | int freeScenario = -1;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!candidateSnapshot[scenario].used)
|
| | | {
|
| | | freeScenario = (int)scenario;
|
| | | break;
|
| | | }
|
| | | if(freeScenario >= 0)
|
| | | {
|
| | | SRAGScenarioCentroid incoming;
|
| | | if(!InitializeScenario(incoming, sourceScenarioId))
|
| | | ReturnFalse;
|
| | | incoming.used = true;
|
| | | incoming.hit_count = 1;
|
| | | ArrayCopy(incoming.centroid, normalized);
|
| | | incoming.actions[0].used = true;
|
| | | incoming.actions[0].scenarioId = sourceScenarioId;
|
| | | incoming.actions[0].hit_count = 1;
|
| | | incoming.actions[0].mean_reward = terminal.reward;
|
| | | incoming.actions[0].mean_terminal = terminal;
|
| | | ArrayCopy(incoming.actions[0].centroid, action);
|
| | | RebindScenarioActions(incoming, (uint)freeScenario);
|
| | | if(!CopyScenario(incoming, candidateSnapshot[freeScenario]))
|
| | | ReturnFalse;
|
| | | candidateCount++;
|
| | | return true;
|
| | | }
|
| | | SRAGScenarioCentroid scenarioCandidates[];
|
| | | if(ArrayResize(scenarioCandidates, (int)(iScenarioCapacity + 1)) != (int)(iScenarioCapacity + 1))
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!CopyScenarioFlexible(candidateSnapshot[scenario], scenarioCandidates[scenario]))
|
| | | ReturnFalse;
|
| | | if(!InitializeScenario(scenarioCandidates[iScenarioCapacity], sourceScenarioId))
|
| | | ReturnFalse;
|
| | | scenarioCandidates[iScenarioCapacity].used = true;
|
| | | scenarioCandidates[iScenarioCapacity].hit_count = 1;
|
| | | ArrayCopy(scenarioCandidates[iScenarioCapacity].centroid, normalized);
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].used = true;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].scenarioId = sourceScenarioId;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].hit_count = 1;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].mean_reward = terminal.reward;
|
| | | scenarioCandidates[iScenarioCapacity].actions[0].mean_terminal = terminal;
|
| | | ArrayCopy(scenarioCandidates[iScenarioCapacity].actions[0].centroid, action);
|
| | | int destination = -1;
|
| | | int source = -1;
|
| | | if(!FindNearestScenarioPair(scenarioCandidates, destination, source) ||
|
| | | !MergeScenarioCandidates(scenarioCandidates[destination], scenarioCandidates[source]))
|
| | | ReturnFalse;
|
| | | RebindScenarioActions(scenarioCandidates[destination], (uint)destination);
|
| | | if(!CopyScenarioFlexible(scenarioCandidates[destination], candidateSnapshot[destination]))
|
| | | ReturnFalse;
|
| | | if(source == (int)iScenarioCapacity)
|
| | | return true;
|
| | | RebindScenarioActions(scenarioCandidates[iScenarioCapacity], (uint)source);
|
| | | if(!CopyScenarioFlexible(scenarioCandidates[iScenarioCapacity], candidateSnapshot[source]))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory validate snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ValidateSnapshot(const SRAGScenarioCentroid &snapshot[],
|
| | | const uint scenarioCount,
|
| | | const uint scenarioEmbedding,
|
| | | const uint actionDimension,
|
| | | const uint scenarioCapacity,
|
| | | const uint actionCapacity) const
|
| | | {
|
| | | if(scenarioEmbedding == 0 || actionDimension == 0 || scenarioCapacity == 0 || actionCapacity == 0 ||
|
| | | scenarioCapacity > (uint)INT_MAX || actionCapacity > (uint)INT_MAX ||
|
| | | ArraySize(snapshot) != (int)scenarioCapacity || scenarioCount > scenarioCapacity)
|
| | | ReturnFalse;
|
| | | uint usedScenarios = 0;
|
| | | for(uint scenario = 0; scenario < scenarioCapacity; scenario++)
|
| | | {
|
| | | if(snapshot[scenario].scenarioId != scenario ||
|
| | | ArraySize(snapshot[scenario].centroid) != (int)scenarioEmbedding ||
|
| | | ArraySize(snapshot[scenario].actions) != (int)actionCapacity)
|
| | | ReturnFalse;
|
| | | double norm = 0.0;
|
| | | for(uint element = 0; element < scenarioEmbedding; element++)
|
| | | {
|
| | | if(!MathIsValidNumber(snapshot[scenario].centroid[element]))
|
| | | ReturnFalse;
|
| | | norm += (double)snapshot[scenario].centroid[element] * snapshot[scenario].centroid[element];
|
| | | }
|
| | | if(snapshot[scenario].used)
|
| | | {
|
| | | if(snapshot[scenario].hit_count == 0 || norm < RAG_NORM_MIN || norm > RAG_NORM_MAX)
|
| | | ReturnFalse;
|
| | | usedScenarios++;
|
| | | }
|
| | | else
|
| | | if(snapshot[scenario].hit_count != 0 || norm > DBL_EPSILON)
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < actionCapacity; action++)
|
| | | {
|
| | | if(snapshot[scenario].actions[action].scenarioId != scenario ||
|
| | | ArraySize(snapshot[scenario].actions[action].centroid) != (int)actionDimension ||
|
| | | !MathIsValidNumber(snapshot[scenario].actions[action].mean_reward) ||
|
| | | !ValidTerminal(snapshot[scenario].actions[action].mean_terminal))
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < actionDimension; element++)
|
| | | if(!MathIsValidNumber(snapshot[scenario].actions[action].centroid[element]) ||
|
| | | snapshot[scenario].actions[action].centroid[element] < 0.0f ||
|
| | | snapshot[scenario].actions[action].centroid[element] > 1.0f)
|
| | | ReturnFalse;
|
| | | if(snapshot[scenario].actions[action].used &&
|
| | | (!snapshot[scenario].used || snapshot[scenario].actions[action].hit_count == 0))
|
| | | ReturnFalse;
|
| | | if(!snapshot[scenario].actions[action].used &&
|
| | | snapshot[scenario].actions[action].hit_count != 0)
|
| | | ReturnFalse;
|
| | | }
|
| | | }
|
| | | return (usedScenarios == scenarioCount);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory commit snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CommitSnapshot(const SRAGScenarioCentroid &snapshot[],
|
| | | const uint scenarioCount)
|
| | | {
|
| | | SRAGScenarioCentroid committed[];
|
| | | if(ArrayResize(committed, (int)iScenarioCapacity) != (int)iScenarioCapacity)
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!CopyScenario(snapshot[scenario], committed[scenario]))
|
| | | ReturnFalse;
|
| | | ArraySwap(scenarios, committed);
|
| | | iScenarioCount = scenarioCount;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy completed record operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopyCompletedRecord(const SRAGCompletedRecord &source,
|
| | | SRAGCompletedRecord &destination) const
|
| | | {
|
| | | if(ArraySize(source.scenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | !ValidAction(source.action) || !ValidTerminal(source.terminal) ||
|
| | | ArrayResize(destination.scenarioEmbedding, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(destination.action, (int)iActionDimension) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | ArrayCopy(destination.scenarioEmbedding, source.scenarioEmbedding);
|
| | | ArrayCopy(destination.action, source.action);
|
| | | destination.terminal = source.terminal;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy pending event operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopyPendingEvent(const SRAGPendingEvent &source,
|
| | | SRAGPendingEvent &destination) const
|
| | | {
|
| | | if(!ValidAction(source.action) ||
|
| | | ArrayResize(destination.action, (int)iActionDimension) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | if(!MathIsValidNumber(source.action[element]))
|
| | | ReturnFalse;
|
| | | ArrayCopy(destination.action, source.action);
|
| | | destination.confirmed = source.confirmed;
|
| | | destination.deal = source.deal;
|
| | | destination.position = source.position;
|
| | | return (!destination.confirmed || (destination.deal >= 0 && destination.position > 0));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy pending fact operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopyPendingFact(const SRAGPendingFact &source,
|
| | | SRAGPendingFact &destination) const
|
| | | {
|
| | | const int eventCount = ArraySize(source.events);
|
| | | if(source.id == 0 || eventCount <= 0 || eventCount >= INT_MAX ||
|
| | | ArraySize(source.scenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(destination.scenarioEmbedding, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(destination.events, eventCount) != eventCount)
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | if(!MathIsValidNumber(source.scenarioEmbedding[element]))
|
| | | ReturnFalse;
|
| | | ArrayCopy(destination.scenarioEmbedding, source.scenarioEmbedding);
|
| | | destination.id = source.id;
|
| | | for(int event = 0; event < eventCount; event++)
|
| | | if(!CopyPendingEvent(source.events[event], destination.events[event]))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory find pending fact operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | int CNeuronRAGMemory::FindPendingFact(const ulong pendingId) const
|
| | | {
|
| | | if(pendingId == 0)
|
| | | return -1;
|
| | | for(int fact = 0; fact < ArraySize(pending_facts); fact++)
|
| | | if(pending_facts[fact].id == pendingId)
|
| | | return fact;
|
| | | return -1;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory build remaining pending facts |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::BuildRemainingPendingFacts(const int excluded,
|
| | | SRAGPendingFact &remaining[]) const
|
| | | {
|
| | | const int total = ArraySize(pending_facts);
|
| | | if(excluded < 0 || excluded >= total || ArrayResize(remaining, total - 1) != total - 1)
|
| | | ReturnFalse;
|
| | | int destination = 0;
|
| | | for(int source = 0; source < total; source++)
|
| | | {
|
| | | if(source == excluded)
|
| | | continue;
|
| | | if(!CopyPendingFact(pending_facts[source], remaining[destination]))
|
| | | ReturnFalse;
|
| | | destination++;
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory copy snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CopySnapshot(const SRAGScenarioCentroid &source[],
|
| | | SRAGScenarioCentroid &destination[]) const
|
| | | {
|
| | | if(ArraySize(source) != (int)iScenarioCapacity ||
|
| | | ArrayResize(destination, (int)iScenarioCapacity) != (int)iScenarioCapacity)
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!CopyScenario(source[scenario], destination[scenario]))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Returns whether hash value CNeuronRAGMemory. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | ulong CNeuronRAGMemory::HashValue(const ulong hash, const ulong value) const
|
| | | {
|
| | | return (hash ^ value) * RAG_FNV_PRIME;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Returns whether hash float bits CNeuronRAGMemory. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | ulong CNeuronRAGMemory::HashFloatBits(const ulong hash, const float value) const
|
| | | {
|
| | | URAGFloatBits raw;
|
| | | raw.value = value;
|
| | | return HashValue(hash, raw.bits);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory snapshot fingerprint operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | ulong CNeuronRAGMemory::SnapshotFingerprint(const SRAGScenarioCentroid &snapshot[],
|
| | | const uint scenarioCount) const
|
| | | {
|
| | | ulong hash = RAG_FNV_OFFSET;
|
| | | hash = HashValue(hash, iFormatVersion);
|
| | | hash = HashValue(hash, iScenarioEmbedding);
|
| | | hash = HashValue(hash, iActionDimension);
|
| | | hash = HashValue(hash, iTokenWidth);
|
| | | hash = HashValue(hash, iScenarioCapacity);
|
| | | hash = HashValue(hash, iActionCapacity);
|
| | | hash = HashValue(hash, iTopK);
|
| | | hash = HashValue(hash, iOnlineMemorySize);
|
| | | hash = HashValue(hash, scenarioCount);
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | {
|
| | | hash = HashValue(hash, (snapshot[scenario].used ? 1 : 0));
|
| | | hash = HashValue(hash, snapshot[scenario].scenarioId);
|
| | | hash = HashValue(hash, snapshot[scenario].hit_count);
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | hash = HashFloatBits(hash, snapshot[scenario].centroid[element]);
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | {
|
| | | hash = HashValue(hash, (snapshot[scenario].actions[action].used ? 1 : 0));
|
| | | hash = HashValue(hash, snapshot[scenario].actions[action].scenarioId);
|
| | | hash = HashValue(hash, snapshot[scenario].actions[action].hit_count);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_reward);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_terminal.relative_return);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_terminal.maxDrawdown);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_terminal.costs);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_terminal.riskUsage);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_terminal.duration);
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].mean_terminal.reward);
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | hash = HashFloatBits(hash, snapshot[scenario].actions[action].centroid[element]);
|
| | | }
|
| | | }
|
| | | return (hash == 0 ? 1 : hash);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory build inference set operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::BuildInferenceSet(const SRAGScenarioCentroid &snapshot[],
|
| | | const ulong fingerprint,
|
| | | CRAGInferenceSet *&result) const
|
| | | {
|
| | | result = NULL;
|
| | | if(OpenCL == NULL || ArraySize(snapshot) != (int)iScenarioCapacity)
|
| | | ReturnFalse;
|
| | | const ulong centroidTotal = (ulong)iScenarioCapacity * iScenarioEmbedding;
|
| | | const ulong actionSlots = (ulong)iScenarioCapacity * iActionCapacity;
|
| | | if(actionSlots > ULONG_MAX / iActionDimension)
|
| | | ReturnFalse;
|
| | | const ulong actionTotal = actionSlots * iActionDimension;
|
| | | const uint partialCount = (iScenarioCapacity + RAG_TOPK_LOCAL_WIDTH - 1) / RAG_TOPK_LOCAL_WIDTH;
|
| | | const ulong partialTotal = (ulong)partialCount * iTopK * 2;
|
| | | const ulong topTotal = (ulong)iTopK * 2;
|
| | | const ulong topActions = (ulong)iTopK * iActionCapacity;
|
| | | if(topActions > ULONG_MAX / iTokenWidth)
|
| | | ReturnFalse;
|
| | | const ulong tokenTotal = topActions * iTokenWidth;
|
| | | if(centroidTotal > (ulong)INT_MAX || actionSlots > (ulong)INT_MAX ||
|
| | | actionTotal > (ulong)INT_MAX || partialTotal > (ulong)INT_MAX ||
|
| | | topTotal > (ulong)INT_MAX || tokenTotal > (ulong)INT_MAX)
|
| | | ReturnFalse;
|
| | | float flatCentroids[], flatUsed[], flatActions[], flatActionUsed[];
|
| | | float flatActionMeanReward[], flatActionHitCount[];
|
| | | if(ArrayResize(flatCentroids, (int)centroidTotal) != (int)centroidTotal ||
|
| | | ArrayResize(flatUsed, (int)iScenarioCapacity) != (int)iScenarioCapacity ||
|
| | | ArrayResize(flatActions, (int)actionTotal) != (int)actionTotal ||
|
| | | ArrayResize(flatActionUsed, (int)actionSlots) != (int)actionSlots ||
|
| | | ArrayResize(flatActionMeanReward, (int)actionSlots) != (int)actionSlots ||
|
| | | ArrayResize(flatActionHitCount, (int)actionSlots) != (int)actionSlots)
|
| | | ReturnFalse;
|
| | | ArrayInitialize(flatCentroids, 0.0f);
|
| | | ArrayInitialize(flatUsed, 0.0f);
|
| | | ArrayInitialize(flatActions, 0.0f);
|
| | | ArrayInitialize(flatActionUsed, 0.0f);
|
| | | ArrayInitialize(flatActionMeanReward, 0.0f);
|
| | | ArrayInitialize(flatActionHitCount, 0.0f);
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | {
|
| | | if(!snapshot[scenario].used)
|
| | | continue;
|
| | | flatUsed[scenario] = 1.0f;
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | flatCentroids[scenario * iScenarioEmbedding + element] = snapshot[scenario].centroid[element];
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | {
|
| | | if(!snapshot[scenario].actions[action].used)
|
| | | continue;
|
| | | const uint slot = scenario * iActionCapacity + action;
|
| | | flatActionUsed[slot] = 1.0f;
|
| | | flatActionMeanReward[slot] = snapshot[scenario].actions[action].mean_reward;
|
| | | flatActionHitCount[slot] = float(snapshot[scenario].actions[action].hit_count);
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | flatActions[slot * iActionDimension + element] = snapshot[scenario].actions[action].centroid[element];
|
| | | }
|
| | | }
|
| | | CRAGInferenceSet *candidate = new CRAGInferenceSet();
|
| | | if(candidate == NULL)
|
| | | ReturnFalse;
|
| | | candidate.opencl = OpenCL;
|
| | | candidate.fingerprint = fingerprint;
|
| | | candidate.centroids = new CBufferFloat();
|
| | | candidate.used = new CBufferFloat();
|
| | | candidate.scores = new CBufferFloat();
|
| | | candidate.partial = new CBufferFloat();
|
| | | candidate.merge = new CBufferFloat();
|
| | | candidate.top = new CBufferFloat();
|
| | | candidate.actions = new CBufferFloat();
|
| | | candidate.actionUsed = new CBufferFloat();
|
| | | candidate.actionMeanReward = new CBufferFloat();
|
| | | candidate.actionHitCount = new CBufferFloat();
|
| | | candidate.absoluteRelevance = new CBufferFloat();
|
| | | candidate.tokens = new CBufferFloat();
|
| | | if(candidate.centroids == NULL || candidate.used == NULL || candidate.scores == NULL ||
|
| | | candidate.partial == NULL || candidate.merge == NULL || candidate.top == NULL || candidate.actions == NULL ||
|
| | | candidate.actionUsed == NULL || candidate.actionMeanReward == NULL ||
|
| | | candidate.actionHitCount == NULL || candidate.absoluteRelevance == NULL || candidate.tokens == NULL ||
|
| | | !candidate.centroids.AssignArray(flatCentroids) || !candidate.centroids.BufferCreate(OpenCL) ||
|
| | | !candidate.used.AssignArray(flatUsed) || !candidate.used.BufferCreate(OpenCL) ||
|
| | | !candidate.actions.AssignArray(flatActions) || !candidate.actions.BufferCreate(OpenCL) ||
|
| | | !candidate.actionUsed.AssignArray(flatActionUsed) || !candidate.actionUsed.BufferCreate(OpenCL) ||
|
| | | !candidate.actionMeanReward.AssignArray(flatActionMeanReward) ||
|
| | | !candidate.actionMeanReward.BufferCreate(OpenCL) ||
|
| | | !candidate.actionHitCount.AssignArray(flatActionHitCount) ||
|
| | | !candidate.actionHitCount.BufferCreate(OpenCL) ||
|
| | | !candidate.absoluteRelevance.BufferInit(1, 0.0f) ||
|
| | | !candidate.absoluteRelevance.BufferCreate(OpenCL) ||
|
| | | !candidate.scores.BufferInit(iScenarioCapacity, 0.0f) || !candidate.scores.BufferCreate(OpenCL) ||
|
| | | !candidate.partial.BufferInit((uint)partialTotal, 0.0f) || !candidate.partial.BufferCreate(OpenCL) ||
|
| | | !candidate.merge.BufferInit((uint)partialTotal, 0.0f) || !candidate.merge.BufferCreate(OpenCL) ||
|
| | | !candidate.top.BufferInit((uint)topTotal, 0.0f) || !candidate.top.BufferCreate(OpenCL) ||
|
| | | !candidate.tokens.BufferInit((uint)tokenTotal, 0.0f) || !candidate.tokens.BufferCreate(OpenCL))
|
| | | {
|
| | | delete candidate;
|
| | | ReturnFalse;
|
| | | }
|
| | | result = candidate;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Initializes the CNeuronRAGMemory layer and allocates its GPU |
|
| | | //| buffers. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
|
| | | CLayerDescription &description)
|
| | | {
|
| | | if(!Configure(description))
|
| | | ReturnFalse;
|
| | | if(iTopK > UINT_MAX / iActionCapacity)
|
| | | ReturnFalse;
|
| | | const uint tokenCount = iTopK * iActionCapacity;
|
| | | if(tokenCount == 0 || iTokenWidth > UINT_MAX / tokenCount)
|
| | | ReturnFalse;
|
| | | if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, tokenCount * iTokenWidth,
|
| | | description.optimization, description.batch))
|
| | | ReturnFalse;
|
| | | if(ArrayResize(scenarios, (int)iScenarioCapacity) != (int)iScenarioCapacity)
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!InitializeScenario(scenarios[scenario], scenario))
|
| | | ReturnFalse;
|
| | | iScenarioCount = 0;
|
| | | return Clear();
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Sets the online memory size of the CNeuronRAGMemory. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::SetOnlineMemorySize(const uint value)
|
| | | {
|
| | | if(value >= (uint)INT_MAX || (value > 0 && value < (uint)ArraySize(pending_records)))
|
| | | ReturnFalse;
|
| | | iOnlineMemorySize = value;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory bind null inference operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::BindNullInference(void)
|
| | | {
|
| | | SRAGScenarioCentroid empty[];
|
| | | if(ArrayResize(empty, (int)iScenarioCapacity) != (int)iScenarioCapacity)
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | if(!InitializeScenario(empty[scenario], scenario))
|
| | | ReturnFalse;
|
| | | CRAGInferenceSet *candidate = NULL;
|
| | | if(!BuildInferenceSet(empty, 0, candidate))
|
| | | ReturnFalse;
|
| | | CRAGInferenceSet *previous = m_inference;
|
| | | m_inference = candidate;
|
| | | if(previous != NULL)
|
| | | delete previous;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Read and sanitize a model-owned scenario embedding from OpenCL. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ReadScenarioEmbedding(CBufferFloat *source,
|
| | | float &destination[], uint &replaced) const
|
| | | {
|
| | | replaced = 0;
|
| | | if(source == NULL)
|
| | | ReturnFalse;
|
| | | if(OpenCL == NULL)
|
| | | ReturnFalse;
|
| | | if(source.GetOpenCL() != OpenCL)
|
| | | ReturnFalse;
|
| | | if(source.GetIndex() == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | if(source.Total() != (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | if(ArrayResize(destination, (int)iScenarioEmbedding) != (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | if(source.GetData(destination) != (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | double squareSum = 0.0;
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | {
|
| | | if(!MathIsValidNumber(destination[element]))
|
| | | {
|
| | | destination[element] = 0.0f;
|
| | | replaced++;
|
| | | }
|
| | | squareSum += (double)destination[element] * destination[element];
|
| | | }
|
| | | if(!MathIsValidNumber(squareSum))
|
| | | ReturnFalse;
|
| | | if(squareSum <= DBL_EPSILON)
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory add completed record operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::AddCompletedRecord(const float &scenarioEmbedding[],
|
| | | const float &action[],
|
| | | const SRAGTerminalOutcome &terminal)
|
| | | {
|
| | | const uint limit = (iOnlineMemorySize > 0 ? iOnlineMemorySize : iScenarioCapacity);
|
| | | const int total = ArraySize(pending_records);
|
| | | if(limit == 0 || total >= (int)limit || ArraySize(scenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | !ValidAction(action) || !ValidTerminal(terminal) ||
|
| | | ArrayResize(pending_records, total + 1) != total + 1)
|
| | | ReturnFalse;
|
| | | SRAGCompletedRecord source;
|
| | | if(ArrayResize(source.scenarioEmbedding, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(source.action, (int)iActionDimension) != (int)iActionDimension)
|
| | | {
|
| | | ArrayResize(pending_records, total);
|
| | | ReturnFalse;
|
| | | }
|
| | | ArrayCopy(source.scenarioEmbedding, scenarioEmbedding);
|
| | | ArrayCopy(source.action, action);
|
| | | source.terminal = terminal;
|
| | | if(!CopyCompletedRecord(source, pending_records[total]))
|
| | | {
|
| | | ArrayResize(pending_records, total);
|
| | | ReturnFalse;
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory open pending operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | ulong CNeuronRAGMemory::OpenPending(const float &scenarioEmbedding[],
|
| | | const float &action[])
|
| | | {
|
| | | const int total = ArraySize(pending_facts);
|
| | | if(iNextPendingId == 0 || iNextPendingId == ULONG_MAX || total >= INT_MAX - 1 ||
|
| | | ArraySize(scenarioEmbedding) != (int)iScenarioEmbedding || !ValidAction(action))
|
| | | return 0;
|
| | | const ulong pendingId = iNextPendingId;
|
| | | SRAGPendingFact source;
|
| | | source.id = pendingId;
|
| | | if(ArrayResize(source.scenarioEmbedding, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(source.events, 1) != 1 ||
|
| | | ArrayResize(source.events[0].action, (int)iActionDimension) != (int)iActionDimension)
|
| | | return 0;
|
| | | ArrayCopy(source.scenarioEmbedding, scenarioEmbedding);
|
| | | ArrayCopy(source.events[0].action, action);
|
| | | source.events[0].confirmed = false;
|
| | | source.events[0].deal = 0;
|
| | | source.events[0].position = 0;
|
| | | SRAGPendingFact candidateFacts[];
|
| | | if(ArrayResize(candidateFacts, total + 1) != total + 1)
|
| | | return 0;
|
| | | for(int fact = 0; fact < total; fact++)
|
| | | if(!CopyPendingFact(pending_facts[fact], candidateFacts[fact]))
|
| | | return 0;
|
| | | if(!CopyPendingFact(source, candidateFacts[total]))
|
| | | return 0;
|
| | | ArraySwap(pending_facts, candidateFacts);
|
| | | iNextPendingId++;
|
| | | return pendingId;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory confirm pending execution operation.|
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ConfirmPendingExecution(const ulong pendingId,
|
| | | const ulong deal,
|
| | | const ulong position)
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0 || deal == 0 || position == 0)
|
| | | ReturnFalse;
|
| | | const int event = ArraySize(pending_facts[fact].events) - 1;
|
| | | if(event < 0 || pending_facts[fact].events[event].confirmed)
|
| | | ReturnFalse;
|
| | | pending_facts[fact].events[event].deal = deal;
|
| | | pending_facts[fact].events[event].position = position;
|
| | | pending_facts[fact].events[event].confirmed = true;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory correct pending operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CorrectPending(const ulong pendingId, const float &action[])
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0 || !ValidAction(action))
|
| | | ReturnFalse;
|
| | | const int eventCount = ArraySize(pending_facts[fact].events);
|
| | | if(eventCount <= 0 || eventCount >= INT_MAX ||
|
| | | !pending_facts[fact].events[eventCount - 1].confirmed)
|
| | | ReturnFalse;
|
| | | SRAGPendingFact candidateFacts[];
|
| | | const int total = ArraySize(pending_facts);
|
| | | if(ArrayResize(candidateFacts, total) != total)
|
| | | ReturnFalse;
|
| | | for(int source = 0; source < total; source++)
|
| | | if(!CopyPendingFact(pending_facts[source], candidateFacts[source]))
|
| | | ReturnFalse;
|
| | | if(ArrayResize(candidateFacts[fact].events, eventCount + 1) != eventCount + 1 ||
|
| | | ArrayResize(candidateFacts[fact].events[eventCount].action,
|
| | | (int)iActionDimension) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | ArrayCopy(candidateFacts[fact].events[eventCount].action, action);
|
| | | candidateFacts[fact].events[eventCount].confirmed = false;
|
| | | candidateFacts[fact].events[eventCount].deal = 0;
|
| | | candidateFacts[fact].events[eventCount].position = 0;
|
| | | ArraySwap(pending_facts, candidateFacts);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory confirm pending correction |
|
| | | //| operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ConfirmPendingCorrection(const ulong pendingId, const ulong position)
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0 || position == 0)
|
| | | ReturnFalse;
|
| | | const int eventCount = ArraySize(pending_facts[fact].events);
|
| | | const int event = eventCount - 1;
|
| | | // A correction has no terminal deal. It is valid only after the opening
|
| | | // event is confirmed for the same live position.
|
| | | if(eventCount < 2 || event < 0 || pending_facts[fact].events[event].confirmed ||
|
| | | !pending_facts[fact].events[0].confirmed ||
|
| | | pending_facts[fact].events[0].position != position)
|
| | | ReturnFalse;
|
| | | pending_facts[fact].events[event].deal = 0;
|
| | | pending_facts[fact].events[event].position = position;
|
| | | pending_facts[fact].events[event].confirmed = true;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-04 17:39:51 +03:00 | | | //| Confirm an executed correction for a position in the same plan. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ConfirmPendingCorrection(const ulong pendingId,
|
| | | const ulong deal,
|
| | | const ulong position)
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0 || deal == 0 || position == 0)
|
| | | ReturnFalse;
|
| | | const int eventCount = ArraySize(pending_facts[fact].events);
|
| | | const int event = eventCount - 1;
|
| | | if(eventCount < 2 || event < 0 || pending_facts[fact].events[event].confirmed ||
|
| | | !pending_facts[fact].events[0].confirmed)
|
| | | ReturnFalse;
|
| | | pending_facts[fact].events[event].deal = deal;
|
| | | pending_facts[fact].events[event].position = position;
|
| | | pending_facts[fact].events[event].confirmed = true;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory cancel pending correction operation.|
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CancelPendingCorrection(const ulong pendingId)
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0)
|
| | | ReturnFalse;
|
| | | const int eventCount = ArraySize(pending_facts[fact].events);
|
| | | if(eventCount < 2 || pending_facts[fact].events[eventCount - 1].confirmed)
|
| | | ReturnFalse;
|
| | | SRAGPendingFact candidateFacts[];
|
| | | const int total = ArraySize(pending_facts);
|
| | | if(ArrayResize(candidateFacts, total) != total)
|
| | | ReturnFalse;
|
| | | for(int source = 0; source < total; source++)
|
| | | if(!CopyPendingFact(pending_facts[source], candidateFacts[source]))
|
| | | ReturnFalse;
|
| | | if(ArrayResize(candidateFacts[fact].events, eventCount - 1) != eventCount - 1)
|
| | | ReturnFalse;
|
| | | ArraySwap(pending_facts, candidateFacts);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory close pending operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ClosePending(const ulong pendingId,
|
| | | const SRAGTerminalOutcome &terminal)
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0 || !ValidTerminal(terminal))
|
| | | ReturnFalse;
|
| | | int confirmedCount = 0;
|
| | | for(int event = 0; event < ArraySize(pending_facts[fact].events); event++)
|
| | | if(pending_facts[fact].events[event].confirmed)
|
| | | confirmedCount++;
|
| | | const int current = ArraySize(pending_records);
|
| | | if(confirmedCount <= 0 || current < 0 ||
|
| | | (ulong)current + (ulong)confirmedCount > (ulong)INT_MAX)
|
| | | ReturnFalse;
|
| | | SRAGPendingFact remainingFacts[];
|
| | | if(!BuildRemainingPendingFacts(fact, remainingFacts))
|
| | | ReturnFalse;
|
| | | SRAGCompletedRecord candidateRecords[];
|
| | | const int candidateCount = current + confirmedCount;
|
| | | if(ArrayResize(candidateRecords, candidateCount) != candidateCount)
|
| | | ReturnFalse;
|
| | | for(int record = 0; record < current; record++)
|
| | | if(!CopyCompletedRecord(pending_records[record], candidateRecords[record]))
|
| | | ReturnFalse;
|
| | | int destination = current;
|
| | | for(int event = 0; event < ArraySize(pending_facts[fact].events); event++)
|
| | | {
|
| | | if(!pending_facts[fact].events[event].confirmed)
|
| | | continue;
|
| | | SRAGCompletedRecord source;
|
| | | if(ArrayResize(source.scenarioEmbedding, (int)iScenarioEmbedding) != (int)iScenarioEmbedding ||
|
| | | ArrayResize(source.action, (int)iActionDimension) != (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | ArrayCopy(source.scenarioEmbedding, pending_facts[fact].scenarioEmbedding);
|
| | | ArrayCopy(source.action, pending_facts[fact].events[event].action);
|
| | | source.terminal = terminal;
|
| | | if(!CopyCompletedRecord(source, candidateRecords[destination]))
|
| | | ReturnFalse;
|
| | | destination++;
|
| | | }
|
| | | ArraySwap(pending_records, candidateRecords);
|
| | | ArraySwap(pending_facts, remainingFacts);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory cancel pending operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::CancelPending(const ulong pendingId)
|
| | | {
|
| | | const int fact = FindPendingFact(pendingId);
|
| | | if(fact < 0)
|
| | | ReturnFalse;
|
| | | for(int event = 0; event < ArraySize(pending_facts[fact].events); event++)
|
| | | if(pending_facts[fact].events[event].confirmed)
|
| | | ReturnFalse;
|
| | | SRAGPendingFact remainingFacts[];
|
| | | if(!BuildRemainingPendingFacts(fact, remainingFacts))
|
| | | ReturnFalse;
|
| | | ArraySwap(pending_facts, remainingFacts);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory publish operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Publish(const bool episodeClosed)
|
| | | {
|
| | | const uint pending = PendingRecordCount();
|
| | | if(pending == 0)
|
| | | return true;
|
| | | if(!episodeClosed && (iOnlineMemorySize == 0 || pending < iOnlineMemorySize))
|
| | | return true;
|
| | | SRAGScenarioCentroid candidateSnapshot[];
|
| | | uint candidateCount = iScenarioCount;
|
| | | if(!CopySnapshot(scenarios, candidateSnapshot))
|
| | | ReturnFalse;
|
| | | for(uint record = 0; record < pending; record++)
|
| | | if(!ApplyCompletedRecordToSnapshot(candidateSnapshot, candidateCount,
|
| | | iScenarioCapacity + record,
|
| | | pending_records[record].scenarioEmbedding,
|
| | | pending_records[record].action,
|
| | | pending_records[record].terminal))
|
| | | ReturnFalse;
|
| | | if(!ConsolidateActionCandidates(candidateSnapshot))
|
| | | ReturnFalse;
|
| | | const ulong fingerprint = SnapshotFingerprint(candidateSnapshot, candidateCount);
|
| | | CRAGInferenceSet *candidate = NULL;
|
| | | if(!BuildInferenceSet(candidateSnapshot, fingerprint, candidate))
|
| | | ReturnFalse;
|
| | | ArraySwap(scenarios, candidateSnapshot);
|
| | | iScenarioCount = candidateCount;
|
| | | CRAGInferenceSet *previous = m_inference;
|
| | | m_inference = candidate;
|
| | | if(previous != NULL)
|
| | | delete previous;
|
| | | ArrayResize(pending_records, 0);
|
| | | iPublicationGeneration++;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Runs the CNeuronRAGMemory feed-forward pass and writes the layer |
|
| | | //| output. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::feedForward(CNeuronBaseOCL *NeuronOCL)
|
| | | {
|
| | | return Retrieve(NeuronOCL == NULL ? NULL : NeuronOCL.getOutput());
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Clears the CNeuronRAGMemory execution state and its GPU buffers. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Clear(void)
|
| | | {
|
| | | if(!CNeuronBaseOCL::Clear())
|
| | | ReturnFalse;
|
| | | if(m_inference == NULL || m_inference.tokens == NULL)
|
| | | return true;
|
| | | if(!m_inference.tokens.BufferInit(iTopK * iActionCapacity * iTokenWidth, 0.0f))
|
| | | ReturnFalse;
|
| | | return m_inference.tokens.BufferCreate(OpenCL);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Rebinds the CNeuronRAGMemory GPU buffers to the given OpenCL |
|
| | | //| context. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | void CNeuronRAGMemory::SetOpenCL(COpenCLMy *obj)
|
| | | {
|
| | | const bool wasBound = (m_inference != NULL);
|
| | | const ulong fingerprint = (wasBound ? m_inference.fingerprint : 0);
|
| | | if(m_inference != NULL)
|
| | | delete m_inference;
|
| | | m_inference = NULL;
|
| | | cNearestCentroids.BufferFree();
|
| | | cNearestUsed.BufferFree();
|
| | | cNearestResult.BufferFree();
|
| | | CNeuronBaseOCL::SetOpenCL(obj);
|
| | | if(obj == NULL || !wasBound)
|
| | | return;
|
| | | if(fingerprint == 0)
|
| | | BindNullInference();
|
| | | else
|
| | | {
|
| | | CRAGInferenceSet *candidate = NULL;
|
| | | if(BuildInferenceSet(scenarios, fingerprint, candidate))
|
| | | m_inference = candidate;
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Returns whether has bytes CNeuronRAGMemory. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::HasBytes(const int file_handle, const ulong bytes) const
|
| | | {
|
| | | const ulong position = (ulong)FileTell(file_handle);
|
| | | const ulong size = (ulong)FileSize(file_handle);
|
| | | return (position <= size && bytes <= size - position);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory write u int operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::WriteUInt(const int file_handle, const uint value) const
|
| | | {
|
| | | return (FileWriteInteger(file_handle, (int)value, INT_VALUE) == sizeof(int));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory write u long operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::WriteULong(const int file_handle, const ulong value) const
|
| | | {
|
| | | return (FileWriteLong(file_handle, (long)value) == sizeof(long));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory write float operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::WriteFloat(const int file_handle, const float value) const
|
| | | {
|
| | | return (MathIsValidNumber(value) && FileWriteFloat(file_handle, value) == sizeof(float));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory read u int operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ReadUInt(const int file_handle, uint &value) const
|
| | | {
|
| | | if(!HasBytes(file_handle, sizeof(int)) || FileIsEnding(file_handle))
|
| | | ReturnFalse;
|
| | | value = (uint)FileReadInteger(file_handle, INT_VALUE);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory read u long operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ReadULong(const int file_handle, ulong &value) const
|
| | | {
|
| | | if(!HasBytes(file_handle, sizeof(long)) || FileIsEnding(file_handle))
|
| | | ReturnFalse;
|
| | | value = (ulong)FileReadLong(file_handle);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory read float operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ReadFloat(const int file_handle, float &value) const
|
| | | {
|
| | | if(!HasBytes(file_handle, sizeof(float)) || FileIsEnding(file_handle))
|
| | | ReturnFalse;
|
| | | value = FileReadFloat(file_handle);
|
| | | return MathIsValidNumber(value);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory snapshot payload bytes operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::SnapshotPayloadBytes(const uint scenarioEmbedding,
|
| | | const uint actionDimension,
|
| | | const uint scenarioCapacity,
|
| | | const uint actionCapacity,
|
| | | ulong &bytes) const
|
| | | {
|
| | | const ulong maximum = ulong(-1);
|
| | | if(scenarioEmbedding > (maximum - 16) / sizeof(float) ||
|
| | | actionDimension > (maximum - 44) / sizeof(float))
|
| | | ReturnFalse;
|
| | | const ulong actionBytes = 44 + (ulong)actionDimension * sizeof(float);
|
| | | if(actionCapacity > maximum / actionBytes)
|
| | | ReturnFalse;
|
| | | const ulong allActions = (ulong)actionCapacity * actionBytes;
|
| | | const ulong scenarioPrefix = 16 + (ulong)scenarioEmbedding * sizeof(float);
|
| | | if(allActions > maximum - scenarioPrefix)
|
| | | ReturnFalse;
|
| | | const ulong scenarioBytes = scenarioPrefix + allActions;
|
| | | if(scenarioCapacity > maximum / scenarioBytes)
|
| | | ReturnFalse;
|
| | | bytes = (ulong)scenarioCapacity * scenarioBytes;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory write published snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::WritePublishedSnapshot(const int file_handle, const ulong fingerprint) const
|
| | | {
|
| | | const uint magic = RAG_SNAPSHOT_MAGIC;
|
| | | const uint format = RAG_FORMAT_PUBLISHED;
|
| | | ulong payloadBytes = 0;
|
| | | if(!ValidateSnapshot(scenarios, iScenarioCount, iScenarioEmbedding, iActionDimension,
|
| | | iScenarioCapacity, iActionCapacity) ||
|
| | | !SnapshotPayloadBytes(iScenarioEmbedding, iActionDimension, iScenarioCapacity,
|
| | | iActionCapacity, payloadBytes) ||
|
| | | !WriteUInt(file_handle, magic) || !WriteUInt(file_handle, format) ||
|
| | | !WriteUInt(file_handle, iScenarioEmbedding) || !WriteUInt(file_handle, iActionDimension) ||
|
| | | !WriteUInt(file_handle, iTokenWidth) || !WriteUInt(file_handle, iScenarioCapacity) ||
|
| | | !WriteUInt(file_handle, iActionCapacity) || !WriteUInt(file_handle, iTopK) ||
|
| | | !WriteUInt(file_handle, iOnlineMemorySize) ||
|
| | | !WriteUInt(file_handle, iScenarioCount) || !WriteULong(file_handle, fingerprint) ||
|
| | | !WriteULong(file_handle, payloadBytes))
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | {
|
| | | if(!WriteUInt(file_handle, (scenarios[scenario].used ? 1 : 0)) ||
|
| | | !WriteUInt(file_handle, scenarios[scenario].scenarioId) ||
|
| | | !WriteULong(file_handle, scenarios[scenario].hit_count))
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | if(!WriteFloat(file_handle, scenarios[scenario].centroid[element]))
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | {
|
| | | if(!WriteUInt(file_handle, (scenarios[scenario].actions[action].used ? 1 : 0)) ||
|
| | | !WriteUInt(file_handle, scenarios[scenario].actions[action].scenarioId) ||
|
| | | !WriteULong(file_handle, scenarios[scenario].actions[action].hit_count) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_reward) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.relative_return) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.maxDrawdown) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.costs) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.riskUsage) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.duration) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.reward))
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | if(!WriteFloat(file_handle, scenarios[scenario].actions[action].centroid[element]))
|
| | | ReturnFalse;
|
| | | }
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory read published snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ReadPublishedSnapshot(const int file_handle,
|
| | | SRAGScenarioCentroid &snapshot[],
|
| | | uint &scenarioCount, ulong &fingerprint) const
|
| | | {
|
| | | uint magic = 0, format = 0, embedding = 0, actionDimension = 0, tokenWidth = 0, scenarioCapacity = 0,
|
| | | actionCapacity = 0, topK = 0, onlineMemorySize = 0;
|
| | | ulong payloadBytes = 0;
|
| | | if(!ReadUInt(file_handle, magic) || !ReadUInt(file_handle, format) ||
|
| | | !ReadUInt(file_handle, embedding) || !ReadUInt(file_handle, actionDimension) ||
|
| | | !ReadUInt(file_handle, tokenWidth) || !ReadUInt(file_handle, scenarioCapacity) ||
|
| | | !ReadUInt(file_handle, actionCapacity) || !ReadUInt(file_handle, topK) ||
|
| | | !ReadUInt(file_handle, onlineMemorySize) ||
|
| | | !ReadUInt(file_handle, scenarioCount) || !ReadULong(file_handle, fingerprint) ||
|
| | | !ReadULong(file_handle, payloadBytes) || magic != RAG_SNAPSHOT_MAGIC || format != RAG_FORMAT_PUBLISHED ||
|
| | | embedding != iScenarioEmbedding || actionDimension != iActionDimension ||
|
| | | tokenWidth != iTokenWidth || scenarioCapacity != iScenarioCapacity ||
|
| | | actionCapacity != iActionCapacity || topK != iTopK || onlineMemorySize != iOnlineMemorySize ||
|
| | | topK > RAG_TOPK_MAX || scenarioCapacity > RAG_FLOAT_INDEX_LIMIT ||
|
| | | scenarioCount > scenarioCapacity || fingerprint == 0)
|
| | | ReturnFalse;
|
| | | ulong expected = 0;
|
| | | if(!SnapshotPayloadBytes(embedding, actionDimension, scenarioCapacity, actionCapacity, expected) ||
|
| | | expected != payloadBytes || !HasBytes(file_handle, payloadBytes) ||
|
| | | ArrayResize(snapshot, (int)scenarioCapacity) != (int)scenarioCapacity)
|
| | | ReturnFalse;
|
| | | const ulong snapshotEnd = (ulong)FileTell(file_handle) + payloadBytes;
|
| | | for(uint scenario = 0; scenario < scenarioCapacity; scenario++)
|
| | | {
|
| | | uint used = 0;
|
| | | if(!ReadUInt(file_handle, used) || used > 1 ||
|
| | | !ReadUInt(file_handle, snapshot[scenario].scenarioId) ||
|
| | | !ReadULong(file_handle, snapshot[scenario].hit_count) ||
|
| | | ArrayResize(snapshot[scenario].centroid, (int)embedding) != (int)embedding ||
|
| | | ArrayResize(snapshot[scenario].actions, (int)actionCapacity) != (int)actionCapacity)
|
| | | ReturnFalse;
|
| | | snapshot[scenario].used = (used == 1);
|
| | | for(uint element = 0; element < embedding; element++)
|
| | | if(!ReadFloat(file_handle, snapshot[scenario].centroid[element]))
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < actionCapacity; action++)
|
| | | {
|
| | | uint actionUsed = 0;
|
| | | if(!ReadUInt(file_handle, actionUsed) || actionUsed > 1 ||
|
| | | !ReadUInt(file_handle, snapshot[scenario].actions[action].scenarioId) ||
|
| | | !ReadULong(file_handle, snapshot[scenario].actions[action].hit_count) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_reward) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_terminal.relative_return) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_terminal.maxDrawdown) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_terminal.costs) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_terminal.riskUsage) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_terminal.duration) ||
|
| | | !ReadFloat(file_handle, snapshot[scenario].actions[action].mean_terminal.reward) ||
|
| | | ArrayResize(snapshot[scenario].actions[action].centroid, (int)actionDimension) != (int)actionDimension)
|
| | | ReturnFalse;
|
| | | snapshot[scenario].actions[action].used = (actionUsed == 1);
|
| | | for(uint element = 0; element < actionDimension; element++)
|
| | | if(!ReadFloat(file_handle, snapshot[scenario].actions[action].centroid[element]))
|
| | | ReturnFalse;
|
| | | }
|
| | | }
|
| | | return ((ulong)FileTell(file_handle) == snapshotEnd &&
|
| | | snapshotEnd == (ulong)FileSize(file_handle) &&
|
| | | ValidateSnapshot(snapshot, scenarioCount, embedding, actionDimension,
|
| | | scenarioCapacity, actionCapacity) &&
|
| | | SnapshotFingerprint(snapshot, scenarioCount) == fingerprint);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory save published snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::SavePublishedSnapshot(const string path) const
|
| | | {
|
| | | if(StringLen(path) == 0)
|
| | | ReturnFalse;
|
| | | const ulong fingerprint = SnapshotFingerprint(scenarios, iScenarioCount);
|
| | | const int file_handle = FileOpen(path, FILE_WRITE | FILE_BIN | FILE_COMMON);
|
| | | if(file_handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | const bool result = WritePublishedSnapshot(file_handle, fingerprint);
|
| | | if(result)
|
| | | FileFlush(file_handle);
|
| | | FileClose(file_handle);
|
| | | return result;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory load published snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::LoadPublishedSnapshot(const string path)
|
| | | {
|
| | | if(StringLen(path) == 0 || OpenCL == NULL)
|
| | | ReturnFalse;
|
| | | const int file_handle = FileOpen(path, FILE_READ | FILE_BIN | FILE_COMMON | FILE_SHARE_READ);
|
| | | if(file_handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | SRAGScenarioCentroid loaded[];
|
| | | uint scenarioCount = 0;
|
| | | ulong fingerprint = 0;
|
| | | const bool read = ReadPublishedSnapshot(file_handle, loaded, scenarioCount, fingerprint);
|
| | | const bool exactEnd = (FileTell(file_handle) == FileSize(file_handle));
|
| | | FileClose(file_handle);
|
| | | if(!read || !exactEnd)
|
| | | ReturnFalse;
|
| | | CRAGInferenceSet *candidate = NULL;
|
| | | if(!BuildInferenceSet(loaded, fingerprint, candidate))
|
| | | ReturnFalse;
|
| | | if(!CommitSnapshot(loaded, scenarioCount))
|
| | | {
|
| | | delete candidate;
|
| | | ReturnFalse;
|
| | | }
|
| | | CRAGInferenceSet *previous = m_inference;
|
| | | m_inference = candidate;
|
| | | if(previous != NULL)
|
| | | delete previous;
|
| | | ArrayResize(pending_records, 0);
|
| | | iPublicationGeneration++;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory bind inference snapshot operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::BindInferenceSnapshot(const string path)
|
| | | {
|
| | | return LoadPublishedSnapshot(path);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Saves the CNeuronRAGMemory state to the file handle. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Save(const int file_handle)
|
| | | {
|
| | | if(!ValidateSnapshot(scenarios, iScenarioCount, iScenarioEmbedding, iActionDimension,
|
| | | iScenarioCapacity, iActionCapacity) ||
|
| | | !CNeuronBaseOCL::Save(file_handle))
|
| | | ReturnFalse;
|
| | | ulong payloadBytes = 0;
|
| | | if(!SnapshotPayloadBytes(iScenarioEmbedding, iActionDimension, iScenarioCapacity,
|
| | | iActionCapacity, payloadBytes) ||
|
| | | !WriteUInt(file_handle, iFormatVersion) ||
|
| | | !WriteUInt(file_handle, iScenarioEmbedding) ||
|
| | | !WriteUInt(file_handle, iActionDimension) ||
|
| | | !WriteUInt(file_handle, iTokenWidth) ||
|
| | | !WriteUInt(file_handle, iScenarioCapacity) ||
|
| | | !WriteUInt(file_handle, iActionCapacity) ||
|
| | | !WriteUInt(file_handle, iTopK) ||
|
| | | !WriteUInt(file_handle, iOnlineMemorySize) ||
|
| | | !WriteUInt(file_handle, iScenarioCount) ||
|
| | | !WriteULong(file_handle, payloadBytes))
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < iScenarioCapacity; scenario++)
|
| | | {
|
| | | if(!WriteUInt(file_handle, (scenarios[scenario].used ? 1 : 0)) ||
|
| | | !WriteUInt(file_handle, scenarios[scenario].scenarioId) ||
|
| | | !WriteULong(file_handle, scenarios[scenario].hit_count))
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iScenarioEmbedding; element++)
|
| | | if(!WriteFloat(file_handle, scenarios[scenario].centroid[element]))
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < iActionCapacity; action++)
|
| | | {
|
| | | if(!WriteUInt(file_handle, (scenarios[scenario].actions[action].used ? 1 : 0)) ||
|
| | | !WriteUInt(file_handle, scenarios[scenario].actions[action].scenarioId) ||
|
| | | !WriteULong(file_handle, scenarios[scenario].actions[action].hit_count) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_reward) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.relative_return) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.maxDrawdown) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.costs) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.riskUsage) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.duration) ||
|
| | | !WriteFloat(file_handle, scenarios[scenario].actions[action].mean_terminal.reward))
|
| | | ReturnFalse;
|
| | | for(uint element = 0; element < iActionDimension; element++)
|
| | | if(!WriteFloat(file_handle, scenarios[scenario].actions[action].centroid[element]))
|
| | | ReturnFalse;
|
| | | }
|
| | | }
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Restores the CNeuronRAGMemory state from the file handle. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Load(const int file_handle)
|
| | | {
|
| | | if(!CNeuronBaseOCL::Load(file_handle))
|
| | | ReturnFalse;
|
| | | uint formatVersion = 0;
|
| | | uint scenarioEmbedding = 0;
|
| | | uint actionDimension = 0;
|
| | | uint tokenWidth = 0;
|
| | | uint scenarioCapacity = 0;
|
| | | uint actionCapacity = 0;
|
| | | uint topK = 0;
|
| | | uint onlineMemorySize = 0;
|
| | | uint scenarioCount = 0;
|
| | | ulong payloadBytes = 0;
|
| | | if(!ReadUInt(file_handle, formatVersion) || !ReadUInt(file_handle, scenarioEmbedding) ||
|
| | | !ReadUInt(file_handle, actionDimension) || !ReadUInt(file_handle, tokenWidth) ||
|
| | | !ReadUInt(file_handle, scenarioCapacity) || !ReadUInt(file_handle, actionCapacity) ||
|
| | | !ReadUInt(file_handle, topK) || !ReadUInt(file_handle, onlineMemorySize) ||
|
| | | !ReadUInt(file_handle, scenarioCount) || !ReadULong(file_handle, payloadBytes) ||
|
| | | formatVersion != RAG_FORMAT_SAVE || scenarioEmbedding == 0 || actionDimension == 0 || tokenWidth == 0 ||
|
| | | scenarioCapacity == 0 || actionCapacity == 0 || topK == 0 ||
|
| | | topK > RAG_TOPK_MAX || scenarioCapacity > RAG_FLOAT_INDEX_LIMIT ||
|
| | | scenarioCapacity >= (uint)INT_MAX || actionCapacity >= (uint)INT_MAX ||
|
| | | scenarioCount > scenarioCapacity)
|
| | | ReturnFalse;
|
| | | ulong expectedPayload = 0;
|
| | | if(!SnapshotPayloadBytes(scenarioEmbedding, actionDimension, scenarioCapacity,
|
| | | actionCapacity, expectedPayload) || payloadBytes != expectedPayload ||
|
| | | !HasBytes(file_handle, payloadBytes))
|
| | | ReturnFalse;
|
| | | const ulong snapshotEnd = (ulong)FileTell(file_handle) + payloadBytes;
|
| | | SRAGScenarioCentroid loaded[];
|
| | | if(ArrayResize(loaded, (int)scenarioCapacity) != (int)scenarioCapacity)
|
| | | ReturnFalse;
|
| | | for(uint scenario = 0; scenario < scenarioCapacity; scenario++)
|
| | | {
|
| | | uint used = 0;
|
| | | if(!ReadUInt(file_handle, used) || used > 1 ||
|
| | | !ReadUInt(file_handle, loaded[scenario].scenarioId) ||
|
| | | !ReadULong(file_handle, loaded[scenario].hit_count) ||
|
| | | ArrayResize(loaded[scenario].centroid, (int)scenarioEmbedding) != (int)scenarioEmbedding ||
|
| | | ArrayResize(loaded[scenario].actions, (int)actionCapacity) != (int)actionCapacity)
|
| | | ReturnFalse;
|
| | | loaded[scenario].used = (used == 1);
|
| | | for(uint element = 0; element < scenarioEmbedding; element++)
|
| | | if(!ReadFloat(file_handle, loaded[scenario].centroid[element]))
|
| | | ReturnFalse;
|
| | | for(uint action = 0; action < actionCapacity; action++)
|
| | | {
|
| | | uint actionUsed = 0;
|
| | | if(!ReadUInt(file_handle, actionUsed) || actionUsed > 1 ||
|
| | | !ReadUInt(file_handle, loaded[scenario].actions[action].scenarioId) ||
|
| | | !ReadULong(file_handle, loaded[scenario].actions[action].hit_count) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_reward) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_terminal.relative_return) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_terminal.maxDrawdown) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_terminal.costs) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_terminal.riskUsage) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_terminal.duration) ||
|
| | | !ReadFloat(file_handle, loaded[scenario].actions[action].mean_terminal.reward) ||
|
| | | ArrayResize(loaded[scenario].actions[action].centroid, (int)actionDimension) != (int)actionDimension)
|
| | | ReturnFalse;
|
| | | loaded[scenario].actions[action].used = (actionUsed == 1);
|
| | | for(uint element = 0; element < actionDimension; element++)
|
| | | if(!ReadFloat(file_handle, loaded[scenario].actions[action].centroid[element]))
|
| | | ReturnFalse;
|
| | | }
|
| | | }
|
| | | if((ulong)FileTell(file_handle) != snapshotEnd ||
|
| | | !ValidateSnapshot(loaded, scenarioCount, scenarioEmbedding, actionDimension,
|
| | | scenarioCapacity, actionCapacity))
|
| | | ReturnFalse;
|
| | | const uint oldFormatVersion = iFormatVersion;
|
| | | const uint oldScenarioEmbedding = iScenarioEmbedding;
|
| | | const uint oldActionDimension = iActionDimension;
|
| | | const uint oldTokenWidth = iTokenWidth;
|
| | | const uint oldScenarioCapacity = iScenarioCapacity;
|
| | | const uint oldActionCapacity = iActionCapacity;
|
| | | const uint oldTopK = iTopK;
|
| | | const uint oldOnlineMemorySize = iOnlineMemorySize;
|
| | | iFormatVersion = formatVersion;
|
| | | iScenarioEmbedding = scenarioEmbedding;
|
| | | iActionDimension = actionDimension;
|
| | | iTokenWidth = tokenWidth;
|
| | | iScenarioCapacity = scenarioCapacity;
|
| | | iActionCapacity = actionCapacity;
|
| | | iTopK = topK;
|
| | | iOnlineMemorySize = onlineMemorySize;
|
| | | if(CommitSnapshot(loaded, scenarioCount))
|
| | | return true;
|
| | | iFormatVersion = oldFormatVersion;
|
| | | iScenarioEmbedding = oldScenarioEmbedding;
|
| | | iActionDimension = oldActionDimension;
|
| | | iTokenWidth = oldTokenWidth;
|
| | | iScenarioCapacity = oldScenarioCapacity;
|
| | | iActionCapacity = oldActionCapacity;
|
| | | iTopK = oldTopK;
|
| | | iOnlineMemorySize = oldOnlineMemorySize;
|
| | | ReturnFalse;
|
| | | }
|
| | |
|
| | | // Generic device gateway. Callers provide buffers sized from their own
|
| | | // CLayerDescription; no Expert constant participates in launch geometry.
|
| | | bool DispatchRAGPipeline(COpenCLMy *opencl, CBufferFloat *embedding,
|
| | | CBufferFloat *centroids, CBufferFloat *used,
|
| | | CBufferFloat *scores, CBufferFloat *partial, CBufferFloat *merge,
|
| | | CBufferFloat *top, CBufferFloat *actions,
|
| | | CBufferFloat *actionUsed, CBufferFloat *actionMeanReward,
|
| | | CBufferFloat *actionHitCount, CBufferFloat *absoluteRelevance,
|
| | | CBufferFloat *tokens, const uint scenarioCount,
|
| | | const uint embeddingDimension, const uint actionCount,
|
| | | const uint actionDimension, const uint tokenWidth, const uint topK)
|
| | | {
|
| | | if(opencl == NULL || embedding == NULL || centroids == NULL || used == NULL ||
|
| | | scores == NULL || partial == NULL || merge == NULL || top == NULL || actions == NULL || actionUsed == NULL || tokens == NULL ||
|
| | | actionMeanReward == NULL || actionHitCount == NULL ||
|
| | | absoluteRelevance == NULL ||
|
| | | scenarioCount == 0 || embeddingDimension == 0 || actionCount == 0 || actionDimension == 0 || topK == 0 ||
|
| | | actionDimension > INT_MAX - 2 || tokenWidth != actionDimension + 2 ||
|
| | | scenarioCount > INT_MAX || embeddingDimension > INT_MAX || actionCount > INT_MAX ||
|
| | | tokenWidth > INT_MAX || topK > INT_MAX || topK > RAG_TOPK_MAX ||
|
| | | scenarioCount > RAG_FLOAT_INDEX_LIMIT)
|
| | | ReturnFalse;
|
| | | const uint blockSize = RAG_TOPK_LOCAL_WIDTH;
|
| | | // The staged ping-pong compaction keeps every intermediate buffer bounded
|
| | | // by the initial partial image. TopK must therefore reduce a full block.
|
| | | if(topK > RAG_TOPK_MAX)
|
| | | ReturnFalse;
|
| | | const uint partialCount = (scenarioCount + blockSize - 1) / blockSize;
|
| | | if((ulong)scenarioCount > ULONG_MAX / (ulong)embeddingDimension ||
|
| | | (ulong)scenarioCount > ULONG_MAX / (ulong)actionCount)
|
| | | ReturnFalse;
|
| | | const ulong centroidFloats = (ulong)scenarioCount * (ulong)embeddingDimension;
|
| | | const ulong actionSlots = (ulong)scenarioCount * (ulong)actionCount;
|
| | | if(actionSlots > ULONG_MAX / (ulong)actionDimension || (ulong)topK > ULONG_MAX / (ulong)actionCount)
|
| | | ReturnFalse;
|
| | | const ulong actionFloats = actionSlots * (ulong)actionDimension;
|
| | | const ulong topActions = (ulong)topK * (ulong)actionCount;
|
| | | if(topActions > ULONG_MAX / (ulong)tokenWidth)
|
| | | ReturnFalse;
|
| | | const ulong tokenFloats = topActions * (ulong)tokenWidth;
|
| | | const int embeddingTotal = embedding.Total();
|
| | | const int centroidsTotal = centroids.Total();
|
| | | const int usedTotal = used.Total();
|
| | | const int scoresTotal = scores.Total();
|
| | | const int partialTotal = partial.Total();
|
| | | const int mergeTotal = merge.Total();
|
| | | const int topTotal = top.Total();
|
| | | const int actionsTotal = actions.Total();
|
| | | const int actionUsedTotal = actionUsed.Total();
|
| | | const int actionMeanRewardTotal = actionMeanReward.Total();
|
| | | const int actionHitCountTotal = actionHitCount.Total();
|
| | | const int absoluteRelevanceTotal = absoluteRelevance.Total();
|
| | | const int tokensTotal = tokens.Total();
|
| | | if(embeddingTotal < 0 || centroidsTotal < 0 || usedTotal < 0 || scoresTotal < 0 || partialTotal < 0 || mergeTotal < 0 ||
|
| | | topTotal < 0 || actionsTotal < 0 || actionUsedTotal < 0 || actionMeanRewardTotal < 0 ||
|
| | | actionHitCountTotal < 0 || absoluteRelevanceTotal < 0 || tokensTotal < 0)
|
| | | ReturnFalse;
|
| | | if(centroidFloats > UINT_MAX || actionSlots > UINT_MAX || actionFloats > UINT_MAX || tokenFloats > UINT_MAX ||
|
| | | partialCount > UINT_MAX / topK || partialCount * topK > UINT_MAX / 2 || (ulong)embeddingTotal < embeddingDimension ||
|
| | | (ulong)centroidsTotal < centroidFloats || (ulong)usedTotal < scenarioCount || (ulong)scoresTotal < scenarioCount ||
|
| | | (ulong)partialTotal < partialCount * topK * 2 || (ulong)mergeTotal < partialCount * topK * 2 ||
|
| | | (ulong)topTotal < topK * 2 || (ulong)actionsTotal < actionFloats ||
|
| | | (ulong)actionUsedTotal < actionSlots || (ulong)actionMeanRewardTotal < actionSlots ||
|
| | | (ulong)actionHitCountTotal < actionSlots || absoluteRelevanceTotal < 1 ||
|
| | | (ulong)tokensTotal < tokenFloats)
|
| | | ReturnFalse;
|
| | | uint offset[] = {0};
|
| | | uint scoreGlobal[] = {scenarioCount};
|
| | | uint partialGlobal[] = {partialCount * blockSize};
|
| | | uint partialLocal[] = {blockSize};
|
| | | uint gatherGlobal[] = {topK * actionCount};
|
| | | if(!opencl.SetArgumentBuffer(def_k_RAGScore, def_k_rags_embedding, embedding.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGScore, def_k_rags_centroids, centroids.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGScore, def_k_rags_used, used.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGScore, def_k_rags_scores, scores.GetIndex()) ||
|
| | | !opencl.SetArgument(def_k_RAGScore, def_k_rags_scenario_count, scenarioCount) ||
|
| | | !opencl.SetArgument(def_k_RAGScore, def_k_rags_embedding_dimension, embeddingDimension) ||
|
| | | !opencl.Execute(def_k_RAGScore, 1, offset, scoreGlobal))
|
| | | ReturnFalse;
|
| | | if(!opencl.SetArgumentBuffer(def_k_RAGPartial, def_k_ragp_scores, scores.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGPartial, def_k_ragp_partial, partial.GetIndex()) ||
|
| | | !opencl.SetArgument(def_k_RAGPartial, def_k_ragp_scenario_count, scenarioCount) ||
|
| | | !opencl.SetArgument(def_k_RAGPartial, def_k_ragp_block_size, blockSize) ||
|
| | | !opencl.SetArgument(def_k_RAGPartial, def_k_ragp_top_k, topK) ||
|
| | | !opencl.Execute(def_k_RAGPartial, 1, offset, partialGlobal, partialLocal))
|
| | | ReturnFalse;
|
| | | uint mergeCount = partialCount * topK;
|
| | | CBufferFloat *source = partial;
|
| | | CBufferFloat *destination = merge;
|
| | | while(mergeCount > blockSize)
|
| | | {
|
| | | const uint mergeBlocks = (mergeCount + blockSize - 1) / blockSize;
|
| | | if(mergeBlocks > UINT_MAX / topK)
|
| | | ReturnFalse;
|
| | | const uint nextCount = mergeBlocks * topK;
|
| | | uint mergeGlobal[] = {mergeBlocks * blockSize};
|
| | | uint mergeLocal[] = {blockSize};
|
| | | if(!opencl.SetArgumentBuffer(def_k_RAGMerge, def_k_ragm_source, source.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGMerge, def_k_ragm_destination, destination.GetIndex()) ||
|
| | | !opencl.SetArgument(def_k_RAGMerge, def_k_ragm_candidate_count, mergeCount) ||
|
| | | !opencl.SetArgument(def_k_RAGMerge, def_k_ragm_block_size, blockSize) ||
|
| | | !opencl.SetArgument(def_k_RAGMerge, def_k_ragm_top_k, topK) ||
|
| | | !opencl.Execute(def_k_RAGMerge, 1, offset, mergeGlobal, mergeLocal))
|
| | | ReturnFalse;
|
| | | mergeCount = nextCount;
|
| | | CBufferFloat *swap = source;
|
| | | source = destination;
|
| | | destination = swap;
|
| | | }
|
| | | uint mergeGlobal[] = {blockSize};
|
| | | uint mergeLocal[] = {blockSize};
|
| | | if(!opencl.SetArgumentBuffer(def_k_RAGMerge, def_k_ragm_source, source.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGMerge, def_k_ragm_destination, top.GetIndex()) ||
|
| | | !opencl.SetArgument(def_k_RAGMerge, def_k_ragm_candidate_count, mergeCount) ||
|
| | | !opencl.SetArgument(def_k_RAGMerge, def_k_ragm_block_size, blockSize) ||
|
| | | !opencl.SetArgument(def_k_RAGMerge, def_k_ragm_top_k, topK) ||
|
| | | !opencl.Execute(def_k_RAGMerge, 1, offset, mergeGlobal, mergeLocal))
|
| | | ReturnFalse;
|
| | | if(!opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_top, top.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_actions, actions.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_tokens, tokens.GetIndex()) ||
|
| | | !opencl.SetArgument(def_k_RAGGather, def_k_ragg_scenario_count, scenarioCount) ||
|
| | | !opencl.SetArgument(def_k_RAGGather, def_k_ragg_action_count, actionCount) ||
|
| | | !opencl.SetArgument(def_k_RAGGather, def_k_ragg_action_dimension, actionDimension) ||
|
| | | !opencl.SetArgument(def_k_RAGGather, def_k_ragg_token_width, tokenWidth) ||
|
| | | !opencl.SetArgument(def_k_RAGGather, def_k_ragg_top_k, topK) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_action_used, actionUsed.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_action_mean_reward, actionMeanReward.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_action_hit_count, actionHitCount.GetIndex()) ||
|
| | | !opencl.SetArgumentBuffer(def_k_RAGGather, def_k_ragg_absolute_relevance, absoluteRelevance.GetIndex()) ||
|
| | | !opencl.Execute(def_k_RAGGather, 1, offset, gatherGlobal))
|
| | | ReturnFalse;
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory retrieve operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Retrieve(CNet *scenarioModel, const uint layer)
|
| | | {
|
| | | if(!scenarioModel)
|
| | | ReturnFalse;
|
| | | CBufferFloat *scenarioEmbedding = NULL;
|
| | | if(!scenarioModel.GetLayerOutputDevice(layer, scenarioEmbedding))
|
| | | ReturnFalse;
|
| | | return Retrieve(scenarioEmbedding);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory retrieve operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::Retrieve(CBufferFloat *scenarioEmbedding)
|
| | | {
|
| | | if(m_inference == NULL || m_inference.opencl == NULL || m_inference.opencl != OpenCL ||
|
| | | scenarioEmbedding == NULL || scenarioEmbedding.GetOpenCL() != OpenCL ||
|
| | | m_inference.centroids == NULL || m_inference.used == NULL || m_inference.scores == NULL ||
|
| | | m_inference.partial == NULL || m_inference.merge == NULL || m_inference.top == NULL || m_inference.actions == NULL ||
|
| | | m_inference.actionUsed == NULL || m_inference.actionMeanReward == NULL ||
|
| | | m_inference.actionHitCount == NULL || m_inference.absoluteRelevance == NULL ||
|
| | | m_inference.tokens == NULL)
|
| | | ReturnFalse;
|
| | | return DispatchRAGPipeline(OpenCL, scenarioEmbedding, m_inference.centroids, m_inference.used,
|
| | | m_inference.scores, m_inference.partial, m_inference.merge, m_inference.top,
|
| | | m_inference.actions, m_inference.actionUsed, m_inference.actionMeanReward,
|
| | | m_inference.actionHitCount, m_inference.absoluteRelevance, m_inference.tokens,
|
| | | iScenarioCapacity, iScenarioEmbedding, iActionCapacity,
|
| | | iActionDimension, iTokenWidth, iTopK);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory scenario centroid operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ScenarioCentroid(const uint scenarioId, float &destination[]) const
|
| | | {
|
| | | if(scenarioId >= iScenarioCapacity || !scenarios[scenarioId].used ||
|
| | | ArraySize(destination) < (int)iScenarioEmbedding)
|
| | | ReturnFalse;
|
| | | ArrayCopy(destination, scenarios[scenarioId].centroid);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory action centroid operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | bool CNeuronRAGMemory::ActionCentroid(const uint scenarioId, const uint actionId,
|
| | | float &destination[]) const
|
| | | {
|
| | | if(scenarioId >= iScenarioCapacity || actionId >= iActionCapacity ||
|
| | | !scenarios[scenarioId].actions[actionId].used ||
|
| | | ArraySize(destination) < (int)iActionDimension)
|
| | | ReturnFalse;
|
| | | ArrayCopy(destination, scenarios[scenarioId].actions[actionId].centroid);
|
| | | return true;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory scenario hit count operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | ulong CNeuronRAGMemory::ScenarioHitCount(const uint scenarioId) const
|
| | | {
|
| | | return (scenarioId < iScenarioCapacity && scenarios[scenarioId].used ?
|
| | | scenarios[scenarioId].hit_count : 0);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Handles the CNeuronRAGMemory action hit count operation. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | ulong CNeuronRAGMemory::ActionHitCount(const uint scenarioId, const uint actionId) const
|
| | | {
|
| | | return (scenarioId < iScenarioCapacity && actionId < iActionCapacity &&
|
| | | scenarios[scenarioId].actions[actionId].used ?
|
| | | scenarios[scenarioId].actions[actionId].hit_count : 0);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-09-07 23:58:52 +03:00 | | | //| Returns the layer info of the CNeuronRAGMemory. |
|
2026-08-18 02:41:45 +03:00 | | | //+------------------------------------------------------------------+
|
| | | CLayerDescription* CNeuronRAGMemory::GetLayerInfo(void)
|
| | | {
|
| | | CLayerDescription *result = CNeuronBaseOCL::GetLayerInfo();
|
| | | if(result == NULL)
|
| | | return NULL;
|
| | | result.type = defNeuronRAGMemory;
|
| | | result.count = iTokenWidth;
|
| | | result.window = iScenarioEmbedding;
|
| | | result.window_out = iActionDimension;
|
| | | result.layers = iTopK;
|
| | | ArrayResize(result.units, 1);
|
| | | ArrayResize(result.heads, 1);
|
| | | result.units[0] = iScenarioCapacity;
|
| | | result.heads[0] = iActionCapacity;
|
| | | return result;
|
| | | }
|
| | |
|
| | | #endif // NEURONET_RAG_MEMORY_MQH
|
| | |
|