//+------------------------------------------------------------------+ //| 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 DNG /// \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 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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. //+------------------------------------------------------------------+ 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) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CNeuronRAGMemory::~CNeuronRAGMemory(void) { if(m_inference != NULL) delete m_inference; m_inference = NULL; ArrayResize(pending_records, 0); ArrayResize(pending_facts, 0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CNeuronRAGMemory::FirstFreeScenario() const { for(uint scenario = 0; scenario < iScenarioCapacity; scenario++) if(!scenarios[scenario].used) return (int)scenario; return -1; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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. 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong CNeuronRAGMemory::HashValue(const ulong hash, const ulong value) const { return (hash ^ value) * RAG_FNV_PRIME; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong CNeuronRAGMemory::HashFloatBits(const ulong hash, const float value) const { URAGFloatBits raw; raw.value = value; return HashValue(hash, raw.bits); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::SetOnlineMemorySize(const uint value) { if(value >= (uint)INT_MAX || (value > 0 && value < (uint)ArraySize(pending_records))) ReturnFalse; iOnlineMemorySize = value; return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| Confirm an executed correction for a position in the same plan. | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::feedForward(CNeuronBaseOCL *NeuronOCL) { return Retrieve(NeuronOCL == NULL ? NULL : NeuronOCL.getOutput()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::WriteUInt(const int file_handle, const uint value) const { return (FileWriteInteger(file_handle, (int)value, INT_VALUE) == sizeof(int)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::WriteULong(const int file_handle, const ulong value) const { return (FileWriteLong(file_handle, (long)value) == sizeof(long)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::WriteFloat(const int file_handle, const float value) const { return (MathIsValidNumber(value) && FileWriteFloat(file_handle, value) == sizeof(float)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::BindInferenceSnapshot(const string path) { return LoadPublishedSnapshot(path); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CNeuronRAGMemory::Retrieve(CNet *scenarioModel, const uint layer) { if(!scenarioModel) ReturnFalse; CBufferFloat *scenarioEmbedding = NULL; if(!scenarioModel.GetLayerOutputDevice(layer, scenarioEmbedding)) ReturnFalse; return Retrieve(scenarioEmbedding); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong CNeuronRAGMemory::ScenarioHitCount(const uint scenarioId) const { return (scenarioId < iScenarioCapacity && scenarios[scenarioId].used ? scenarios[scenarioId].hit_count : 0); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ 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