forked from dng/NN_in_Trading
2426 lines
114 KiB
MQL5
2426 lines
114 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| NeuroNet_RAGMemory.mqh |
|
||
|
|
//| RAG memory inference layer implementation (library DNG) |
|
||
|
|
//| Copyright DNG® |
|
||
|
|
//| https://www.mql5.com/ru/users/dng |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#ifndef NEURONET_RAG_MEMORY_MQH
|
||
|
|
#define NEURONET_RAG_MEMORY_MQH
|
||
|
|
/// \file
|
||
|
|
/// \brief NeuroNet_RAGMemory.mqh
|
||
|
|
/// Immutable RAG memory layer implementation: GPU TopK retrieval
|
||
|
|
/// and copy-on-write snapshot publication.
|
||
|
|
/// \author <A HREF="https://www.mql5.com/en/users/dng"> DNG </A>
|
||
|
|
/// \copyright Copyright 2026, DNG
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| RAG memory local constants |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#define RAG_MAX_FLOAT 3.402823466e+38f
|
||
|
|
#define RAG_TERMINAL_EPSILON 1.0e-6f
|
||
|
|
#define RAG_NORM_MIN 0.999
|
||
|
|
#define RAG_NORM_MAX 1.001
|
||
|
|
#define RAG_SNAPSHOT_MAGIC 0x52414731
|
||
|
|
#define RAG_FNV_PRIME 1099511628211
|
||
|
|
#define RAG_FNV_OFFSET 1469598103934665603
|
||
|
|
#define RAG_MAX_CANDIDATES 65536
|
||
|
|
#define RAG_FORMAT_SAVE 2
|
||
|
|
#define RAG_FORMAT_PUBLISHED 3
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
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
|
||
|
|
|