#ifndef NEURONET_BUILDING_FACADE
#include "NeuroNet.mqh"
#endif // NEURONET_BUILDING_FACADE
//+------------------------------------------------------------------+
//| Logical method implementations generated from NeuroNet.mqh: AgentsRL
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CNeuronFQF::CNeuronFQF(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CNeuronFQF::~CNeuronFQF(void)
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl, uint actions, uint quantiles, uint numInputs, ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, actions, optimization_type, batch))
      ReturnFalse;
   SetActivationFunction(None);
//---
   if(!cFraction.Init(0, myIndex, open_cl, actions * quantiles, optimization, batch))
      ReturnFalse;
   cFraction.SetActivationFunction(None);
//---
   if(!cSoftMax.Init(0, myIndex, open_cl, actions * quantiles, optimization, batch))
      ReturnFalse;
   cSoftMax.SetHeads(actions);
   cSoftMax.SetActivationFunction(None);
//---
   if(!cCosine.Init(numInputs, myIndex, open_cl, actions * quantiles, optimization, batch))
      ReturnFalse;
   cCosine.SetActivationFunction(None);
//---
   if(!cCosineEmbeding.Init(0, myIndex, open_cl, numInputs, optimization, batch))
      ReturnFalse;
   cCosineEmbeding.SetActivationFunction(LReLU);
//---
   if(!cQuantile0.Init(4 * actions * quantiles, myIndex, open_cl, numInputs, optimization, batch))
      ReturnFalse;
   cQuantile0.SetActivationFunction(None);
//---
   if(!cQuantile1.Init(actions * quantiles, myIndex, open_cl, 4 * actions * quantiles, optimization, batch))
      ReturnFalse;
   cQuantile1.SetActivationFunction(LReLU);
//---
   if(!cQuantile2.Init(0, myIndex, open_cl, actions * quantiles, optimization, batch))
      ReturnFalse;
   cQuantile2.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cFraction.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cSoftMax.FeedForward(GetPointer(cFraction)))
      ReturnFalse;
//---
     {
      uint global_work_offset[2] = {0, 0};
      uint global_work_size[2];
      global_work_size[1] = Output.Total();
      global_work_size[0] = cSoftMax.Neurons() / global_work_size[1];
      setBuffer(def_k_FQF_Cosine, def_k_fqf_cosine_softmax, cSoftMax.getOutputIndex());
      setBuffer(def_k_FQF_Cosine, def_k_fqf_cosine_outputs, cCosine.getOutputIndex());
      kernelExecute(def_k_FQF_Cosine, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!cCosine.getOutput().BufferRead())
         ReturnFalse;
#endif
     }
//---
   if(!cCosineEmbeding.FeedForward(GetPointer(cCosine)))
      ReturnFalse;
//---
   if(!ElementMult(NeuronOCL.getOutput(), cCosineEmbeding.getOutput(), cQuantile0.getOutput()))
      ReturnFalse;
//---
   if(!cQuantile1.FeedForward(GetPointer(cQuantile0)))
      ReturnFalse;
//---
   if(!cQuantile2.FeedForward(GetPointer(cQuantile1)))
      ReturnFalse;
//---
     {
      uint global_work_offset[1] = {0};
      uint global_work_size[1] = { Neurons() };
      setBuffer(def_k_FQF_Output, def_k_fqfout_quantiles, cQuantile2.getOutputIndex());
      setBuffer(def_k_FQF_Output, def_k_fqfout_delta_taus, cSoftMax.getOutputIndex());
      setBuffer(def_k_FQF_Output, def_k_fqfout_output, getOutputIndex());
      setArgument(def_k_FQF_Output, def_k_fqfout_total, (int)(cQuantile2.Neurons() / global_work_size[0]));
      kernelExecute(def_k_FQF_Output, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!Output.BufferRead())
         ReturnFalse;
#endif
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL || !Gradient || !Output)
      ReturnFalse;
//---
     {
      uint global_work_offset[2] = {0, 0};
      uint global_work_size[2] = { cSoftMax.Neurons() / Neurons(), Neurons() };
      setBuffer(def_k_FQF_OutputGradient, def_k_fqfoutgr_quantiles, cQuantile2.getOutputIndex());
      setBuffer(def_k_FQF_OutputGradient, def_k_fqfoutgr_taus, cSoftMax.getOutputIndex());
      setBuffer(def_k_FQF_OutputGradient, def_k_fqfoutgr_output_gr, getGradientIndex());
      setBuffer(def_k_FQF_OutputGradient, def_k_fqfoutgr_quantiles_gr, cQuantile2.getGradientIndex());
      setBuffer(def_k_FQF_OutputGradient, def_k_fqfoutgr_taus_gr, cSoftMax.getGradientIndex());
      kernelExecute(def_k_FQF_OutputGradient, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!cQuantile2.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
//---
   if(!cQuantile1.CalcHiddenGradients(cQuantile2.AsObject()))
      ReturnFalse;
   if(!cQuantile0.CalcHiddenGradients(cQuantile1.AsObject()))
      ReturnFalse;
//---
     {
      uint global_work_offset[2] = {0, 0};
      uint global_work_size[2] = { cCosineEmbeding.Neurons(), 1 };
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_state_enbeding, NeuronOCL.getOutputIndex());
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_taus_embedding, cCosineEmbeding.getOutputIndex());
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_quantiles_gr, cQuantile0.getGradientIndex());
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_state_gr, NeuronOCL.getGradientIndex());
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_taus_gr, cCosineEmbeding.getGradientIndex());
      kernelExecute(def_k_FQF_QuantileGradient, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!NeuronOCL.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
//---
   if(!cCosineEmbeding.CalcHiddenGradients(cCosine.AsObject()))
      ReturnFalse;
//---
     {
      uint global_work_offset[2] = {0, 0};
      uint global_work_size[2] = { cSoftMax.Neurons() / Neurons(), Neurons() };
      setBuffer(def_k_FQF_CosineGradient, def_k_fqfcosgr_softmax, cSoftMax.getOutputIndex());
      setBuffer(def_k_FQF_CosineGradient, def_k_fqfcosgr_output_gr, cCosine.getGradientIndex());
      setBuffer(def_k_FQF_CosineGradient, def_k_fqfcosgr_softmax_gr, cSoftMax.getGradientIndex());
      kernelExecute(def_k_FQF_CosineGradient, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!cSoftMax.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
//---
   if(!cFraction.CalcHiddenGradients(cSoftMax.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cFraction.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cCosineEmbeding.UpdateInputWeights(GetPointer(cCosine)))
      ReturnFalse;
   if(!cQuantile1.UpdateInputWeights(GetPointer(cQuantile0)))
      ReturnFalse;
   if(!cQuantile2.UpdateInputWeights(GetPointer(cQuantile1)))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(!cFraction.Save(file_handle))
      ReturnFalse;
   if(!cSoftMax.Save(file_handle))
      ReturnFalse;
   if(!cCosine.Save(file_handle))
      ReturnFalse;
   if(!cCosineEmbeding.Save(file_handle))
      ReturnFalse;
   if(!cQuantile0.Save(file_handle))
      ReturnFalse;
   if(!cQuantile1.Save(file_handle))
      ReturnFalse;
   if(!cQuantile2.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cFraction.Type() ||
      !cFraction.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cSoftMax.Type() ||
      !cSoftMax.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cCosine.Type() ||
      !cCosine.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cCosineEmbeding.Type() ||
      !cCosineEmbeding.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cQuantile0.Type() ||
      !cQuantile0.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cQuantile1.Type() ||
      !cQuantile1.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cQuantile2.Type() ||
      !cQuantile2.Load(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CLayerDescription* CNeuronFQF::GetLayerInfo(void)
  {
   CLayerDescription *result = CNeuronBaseOCL::GetLayerInfo();
   if(!result)
      return result;
   result.window_out = cSoftMax.Neurons() / Neurons();
//---
   return result;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFQF::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cFraction.SetOpenCL(OpenCL);
   cSoftMax.SetOpenCL(OpenCL);
   cCosine.SetOpenCL(OpenCL);
   cCosineEmbeding.SetOpenCL(OpenCL);
   cQuantile0.SetOpenCL(OpenCL);
   cQuantile1.SetOpenCL(OpenCL);
   cQuantile2.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl, uint actions, uint quantiles, uint numInputs, ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronFQF::Init(numOutputs, myIndex, open_cl, actions, quantiles, numInputs, optimization_type, batch))
      ReturnFalse;
//---
   if(!cAlphas.Init(0, 0, OpenCL, actions, numInputs, cQuantile2.Neurons(), optimization_type, batch))
      ReturnFalse;
   cAlphas.SetActivationFunction(SIGMOID);
//---
   if(!cLogProbs.BufferInit(actions, 0) || !cLogProbs.BufferCreate(OpenCL))
      ReturnFalse;
//---
   if(!cRandomize.BufferInit(actions, 0) || !cRandomize.BufferCreate(OpenCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronFQF::feedForward(NeuronOCL))
      ReturnFalse;
   if(!cAlphas.FeedForward(GetPointer(cQuantile0), cQuantile2.getOutput()))
      ReturnFalse;
//---
   int actions = cRandomize.Total();
   for(int i = 0; i < actions; i++)
     {
      float probability = (float)MathRand() / 32767.0f;
      cRandomize.Update(i, probability);
     }
   if(!cRandomize.BufferWrite())
      ReturnFalse;
//---
   uint global_work_offset[1] = {0};
   uint global_work_size[1] = {Neurons()};
   setBuffer(def_k_SAC_AlphaLogProbs, def_k_sac_alp_alphas, cAlphas.getOutputIndex())
   setBuffer(def_k_SAC_AlphaLogProbs, def_k_sac_alp_log_probs, cLogProbs.GetIndex())
   setBuffer(def_k_SAC_AlphaLogProbs, def_k_sac_alp_outputs, getOutputIndex())
   setBuffer(def_k_SAC_AlphaLogProbs, def_k_sac_alp_probs, cSoftMax.getOutputIndex())
   setBuffer(def_k_SAC_AlphaLogProbs, def_k_sac_alp_quantiles, cQuantile2.getOutputIndex())
   setBuffer(def_k_SAC_AlphaLogProbs, def_k_sac_alp_random, cRandomize.GetIndex())
   setArgument(def_k_SAC_AlphaLogProbs, def_k_sac_alp_count_quants, (int)(cSoftMax.Neurons() / global_work_size[0]))
   setArgument(def_k_SAC_AlphaLogProbs, def_k_sac_alp_activation, (int)activation)
   kernelExecute(def_k_SAC_AlphaLogProbs, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!cRandomize.BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::calcAlphaGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL || !NeuronOCL.getGradient() ||
      NeuronOCL.getGradientIndex() < 0)
      ReturnFalse;
//---
   uint global_work_offset[1] = {0};
   uint global_work_size[1] = {Neurons()};
   setBuffer(def_k_SAC_AlphaGradients, def_k_sac_alg_outputs, cAlphas.getOutputIndex())
   setBuffer(def_k_SAC_AlphaGradients, def_k_sac_alg_alphas_grad, cAlphas.getGradientIndex())
   setBuffer(def_k_SAC_AlphaGradients, def_k_sac_alg_gradient, NeuronOCL.getGradientIndex())
   setBuffer(def_k_SAC_AlphaGradients, def_k_sac_alg_log_probs, cLogProbs.GetIndex())
   setArgument(def_k_SAC_AlphaGradients, def_k_sac_alg_activation, (int)cAlphas.Activation())
   kernelExecute(def_k_SAC_AlphaGradients, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!cLogProbs.BufferRead())
      ReturnFalse;
#endif
//---
   return cQuantile0.CalcHiddenGradients((CObject*)GetPointer(cAlphas), cQuantile2.getOutput(), cQuantile2.getGradient());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronFQF::updateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return cAlphas.UpdateInputWeights(cQuantile0.AsObject(), cQuantile2.getOutput());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::WeightsUpdate(CNeuronBaseOCL* source, float tau)
  {
   if(!CNeuronFQF::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronSoftActorCritic *Source = source;
//---
   return cAlphas.WeightsUpdate(GetPointer(Source.cAlphas), tau);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSoftActorCritic::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronFQF::SetOpenCL(obj);
   cAlphas.SetOpenCL(OpenCL);
   if(cLogProbs.Total() != Neurons())
      cLogProbs.BufferInit(Neurons(), 0);
   cLogProbs.BufferCreate(OpenCL);
   if(cRandomize.Total() != Neurons())
      cRandomize.BufferInit(Neurons(), 0);
   cRandomize.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::Save(const int file_handle)
  {
   if(!CNeuronFQF::Save(file_handle))
      ReturnFalse;
   return cAlphas.Save(file_handle);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::Load(const int file_handle)
  {
   if(!CNeuronFQF::Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cAlphas.Type() ||
      !cAlphas.Load(file_handle))
      ReturnFalse;
//---
   if(cLogProbs.Total() != Neurons())
     {
      cLogProbs.BufferFree();
      if(!cLogProbs.BufferInit(Neurons(), 0) ||
         (!!OpenCL && !cLogProbs.BufferCreate(OpenCL)))
         ReturnFalse;
     }
//---
   if(cRandomize.Total() != Neurons())
     {
      cRandomize.BufferFree();
      if(!cRandomize.BufferInit(Neurons(), 0) ||
         (!!OpenCL && !cRandomize.BufferCreate(OpenCL)))
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::CalcLogProbs(CBufferFloat* buffer)
  {
   if(!buffer || buffer.Total() < Neurons())
      ReturnFalse;
   if(buffer.GetIndex() < 0 && !buffer.BufferCreate(OpenCL))
      ReturnFalse;
//---
   uint global_work_offset[1] = {0};
   uint global_work_size[1] = {Neurons()};
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_alphas, cAlphas.getOutputIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_log_probs, buffer.GetIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_outputs, buffer.GetIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_probs, cSoftMax.getOutputIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_quantiles, cQuantile2.getOutputIndex())
   setArgument(def_k_SAC_CalcLogProbs, def_k_sacclp_count_quants, (int)(cSoftMax.Neurons() / global_work_size[0]))
   setArgument(def_k_SAC_CalcLogProbs, def_k_sacclp_activation, (int)Activation())
   kernelExecute(def_k_SAC_CalcLogProbs, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!cQuantile2.getOutput().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::ReCalcLogProbs(void)
  {
   if(!OpenCL)
      ReturnFalse;
//---
   uint global_work_offset[1] = {0};
   uint global_work_size[1] = {Neurons()};
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_alphas, cAlphas.getOutputIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_log_probs, cLogProbs.GetIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_outputs, Output.GetIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_probs, cSoftMax.getOutputIndex())
   setBuffer(def_k_SAC_CalcLogProbs, def_k_sacclp_quantiles, cQuantile2.getOutputIndex())
   setArgument(def_k_SAC_CalcLogProbs, def_k_sacclp_count_quants, (int)(cSoftMax.Neurons() / global_work_size[0]))
   setArgument(def_k_SAC_CalcLogProbs, def_k_sacclp_activation, (int)Activation())
   kernelExecute(def_k_SAC_CalcLogProbs, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!cQuantile2.getOutput().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSoftActorCritic::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL || !Gradient || !Output)
      ReturnFalse;
//---
     {
      uint global_work_offset[1] = {0};
      uint global_work_size[1] = { Neurons() };
      setBuffer(def_k_SAC_OutputGradient, def_k_sacoutgr_quantiles, cQuantile2.getOutputIndex())
      setBuffer(def_k_SAC_OutputGradient, def_k_sacoutgr_taus, cSoftMax.getOutputIndex())
      setBuffer(def_k_SAC_OutputGradient, def_k_sacoutgr_output_gr, getGradientIndex())
      setBuffer(def_k_SAC_OutputGradient, def_k_sacoutgr_quantiles_gr, cQuantile2.getGradientIndex())
      setBuffer(def_k_SAC_OutputGradient, def_k_sacoutgr_taus_gr, cSoftMax.getGradientIndex())
      setBuffer(def_k_SAC_OutputGradient, def_k_sacoutgr_outputs, getOutputIndex())
      setArgument(def_k_SAC_OutputGradient, def_k_sacoutgr_activation, (int)Activation())
      setArgument(def_k_SAC_OutputGradient, def_k_sacoutgr_count_quants, (int)(cSoftMax.Neurons() / Neurons()))
      kernelExecute(def_k_SAC_OutputGradient, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!cSoftMax.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
//---
   if(!cQuantile1.CalcHiddenGradients((CObject *)GetPointer(cQuantile2)))
      ReturnFalse;
   if(!cQuantile0.CalcHiddenGradients((CObject *)GetPointer(cQuantile1)))
      ReturnFalse;
//---
     {
      uint global_work_offset[2] = {0, 0};
      uint global_work_size[2] = { cCosineEmbeding.Neurons(), 1 };
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_state_enbeding, NeuronOCL.getOutputIndex())
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_taus_embedding, cCosineEmbeding.getOutputIndex())
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_quantiles_gr, cQuantile0.getGradientIndex())
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_state_gr, NeuronOCL.getGradientIndex())
      setBuffer(def_k_FQF_QuantileGradient, def_k_fqfqgr_taus_gr, cCosineEmbeding.getGradientIndex())
      kernelExecute(def_k_FQF_QuantileGradient, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!cCosineEmbeding.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
//---
   if(!cCosine.CalcHiddenGradients((CObject *)GetPointer(cCosineEmbeding)))
      ReturnFalse;
//---
     {
      uint global_work_offset[2] = {0, 0};
      uint global_work_size[2] = { cSoftMax.Neurons() / Neurons(), Neurons() };
      setBuffer(def_k_FQF_CosineGradient, def_k_fqfcosgr_softmax, cSoftMax.getOutputIndex())
      setBuffer(def_k_FQF_CosineGradient, def_k_fqfcosgr_output_gr, cCosine.getGradientIndex())
      setBuffer(def_k_FQF_CosineGradient, def_k_fqfcosgr_softmax_gr, cSoftMax.getGradientIndex())
      kernelExecute(def_k_FQF_CosineGradient, global_work_offset, global_work_size)
#ifdef  _DEBUG
      if(!cSoftMax.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
   if(!cSoftMax.CalcHiddenGradients((CObject *)GetPointer(cFraction)))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSoftActorCritic::getCheckData(vector<float> &output, vector<float> &grad, vector<float> &quantiles, vector<float> &probability, vector<float> &alphas)
  {
   Output.GetData(output);
   Gradient.GetData(grad);
   cQuantile2.getGradient().GetData(quantiles);
   cSoftMax.getGradient().GetData(probability);
   cAlphas.getGradient().GetData(alphas);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFQF::WeightsUpdate(CNeuronBaseOCL* source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronFQF *temp = source;
   if(!cFraction.WeightsUpdate(GetPointer(temp.cFraction), tau))
      ReturnFalse;
   if(!cSoftMax.WeightsUpdate(GetPointer(temp.cSoftMax), tau))
      ReturnFalse;
   if(!cCosine.WeightsUpdate(GetPointer(temp.cCosine), tau))
      ReturnFalse;
   if(!cCosineEmbeding.WeightsUpdate(GetPointer(temp.cCosineEmbeding), tau))
      ReturnFalse;
   if(!cQuantile0.WeightsUpdate(GetPointer(temp.cQuantile0), tau))
      ReturnFalse;
   if(!cQuantile1.WeightsUpdate(GetPointer(temp.cQuantile1), tau))
      ReturnFalse;
   if(!cQuantile2.WeightsUpdate(GetPointer(temp.cQuantile2), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLROCL::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl, uint window_in, uint units_count, bool transpose, ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_in * units_count, optimization_type, batch))
      ReturnFalse;
//---
   iVariables = (int)window_in;
   iCount = (int)units_count;
   bTranspose = transpose;
//---
   icIsTTP = OpenCL.AddBuffer(sizeof(int) * Neurons(), CL_MEM_READ_WRITE);
   if(icIsTTP < 0)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLROCL::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL || !NeuronOCL.getOutput())
      ReturnFalse;
//---
   uint global_work_offset[2] = {0};
   uint global_work_size[2] = {iCount, iVariables};
   uint local_work_size[2] = {iCount, 1};
   ResetLastError();
   setBuffer(def_k_PLR, def_k_plr_inputs, NeuronOCL.getOutputIndex())
   setBuffer(def_k_PLR, def_k_plr_outputs, getOutputIndex())
   setBuffer(def_k_PLR, def_k_plt_isttp, icIsTTP)
   setArgument(def_k_PLR, def_k_plr_transpose, (int)bTranspose)
   setArgument(def_k_PLR, def_k_plr_step, (float)0.2)
//---
   kernelExecuteLoc(def_k_PLR, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!Output.BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLROCL::calcInputGradients(CNeuronBaseOCL* prevLayer)
  {
   if(!OpenCL || !prevLayer || !prevLayer.getGradient())
      ReturnFalse;
//---
   uint global_work_offset[2] = {0};
   uint global_work_size[2] = {iCount, iVariables};
   ResetLastError();
   setBuffer(def_k_PLRGrad, def_k_plrg_inputs_gr, prevLayer.getGradientIndex())
   setBuffer(def_k_PLRGrad, def_k_plrg_outputs, getOutputIndex())
   setBuffer(def_k_PLRGrad, def_k_plrg_outputs_gr, getGradientIndex())
   setArgument(def_k_PLRGrad, def_k_plr_transpose, (int)bTranspose)
//---
   kernelExecute(def_k_PLRGrad, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!prevLayer.getGradient().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLROCL::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(FileWriteInteger(file_handle, iCount) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, iVariables) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, int(bTranspose)) < INT_VALUE)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLROCL::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iCount = FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iVariables = FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   bTranspose = (bool)FileReadInteger(file_handle);
//---
   OpenCL.BufferFree(icIsTTP);
   icIsTTP = OpenCL.AddBuffer(sizeof(int) * Neurons(), CL_MEM_READ_WRITE);
   if(icIsTTP < 0)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronPLROCL::SetOpenCL(COpenCLMy * obj)
  {
   if(!!OpenCL && icIsTTP >= 0)
      OpenCL.BufferFree(icIsTTP);
//---
   CNeuronBaseOCL::SetOpenCL(obj);
   icIsTTP = OpenCL.AddBuffer(sizeof(float) * Neurons(), CL_MEM_READ_WRITE);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMarketObserver::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                 uint window, uint window_key, uint units_count,
                                 uint heads, uint layers, uint forecast,
                                 ENUM_OPTIMIZATION optimization_type, uint batch)
  {
//--- Init parent object
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window * forecast, optimization_type, batch))
      ReturnFalse;
//--- Clear layers' array
   cLayers.Clear();
   cLayers.SetOpenCL(OpenCL);
//--- Tranpose input data
   int lay_count = 0;
   CNeuronTransposeOCL *transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(0, lay_count, OpenCL, units_count, window, optimization, iBatch) ||
      !cLayers.Add(transp))
     {
      DeleteObj(transp);
      ReturnFalse;
     }
//--- Piecewise linear representation
   lay_count++;
   CNeuronPLROCL *plr = new CNeuronPLROCL();
   if(!plr ||
      !plr.Init(0, lay_count, OpenCL, units_count, window, false, optimization, iBatch) ||
      !cLayers.Add(plr))
     {
      DeleteObj(plr);
      ReturnFalse;
     }
//--- Self-Attention for Variables
   lay_count++;
   CNeuronRMAT *att = new CNeuronRMAT();
   if(!att ||
      !att.Init(0, lay_count, OpenCL, units_count, window_key, window, heads, layers, optimization, iBatch) ||
      !cLayers.Add(att))
     {
      DeleteObj(att);
      ReturnFalse;
     }
//--- Forecast mapping
   lay_count++;
   CResidualConv *conv = new CResidualConv();
   if(!conv ||
      !conv.Init(0, lay_count, OpenCL, units_count, forecast, window, optimization, iBatch) ||
      !cLayers.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
//--- Back transpose forecast
   lay_count++;
   transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(0, lay_count, OpenCL, window, forecast, optimization, iBatch) ||
      !cLayers.Add(transp))
     {
      DeleteObj(transp);
      ReturnFalse;
     }
//---
   if(!SetOutput(transp.getOutput(), true) ||
      !SetGradient(transp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronRLAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                          uint window, uint units_count, uint segments,
                          float rho, uint layers, uint n_actions,
                          ENUM_OPTIMIZATION optimization_type, uint batch)
  {
//--- Init parent object
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, n_actions, optimization_type, batch))
      ReturnFalse;
//--- Clear layers' array
   cLayers.Clear();
   cLayers.SetOpenCL(OpenCL);
//--- State observation
   int lay_count = 0;
   for(uint i = 0; i < layers; i++)
     {
      CNeuronPSformer *psf = new CNeuronPSformer();
      if(!psf ||
         !psf.Init(0, lay_count, OpenCL, window, units_count, segments, rho, optimization, iBatch) ||
         !cLayers.Add(psf))
        {
         DeleteObj(psf);
         ReturnFalse;
        }
      lay_count++;
     }
//---
   CNeuronConvSAMOCL *conv = new CNeuronConvSAMOCL();
   if(!conv ||
      !conv.Init(n_actions, lay_count, OpenCL, window, window, 1, units_count, 1, optimization, iBatch) ||
      !cLayers.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   conv.SetActivationFunction(GELU);
   lay_count++;
   CNeuronBaseSAMOCL *flat = new CNeuronBaseSAMOCL();
   if(!flat ||
      !flat.Init(0, lay_count, OpenCL, n_actions, optimization, iBatch) ||
      !cLayers.Add(flat))
     {
      DeleteObj(flat);
      ReturnFalse;
     }
   SetActivationFunction(SIGMOID);
//---
   if(!SetOutput(flat.getOutput(), true) ||
      !SetGradient(flat.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronControlAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                               uint window, uint window_key, uint units_count,
                               uint heads, uint window_kv, uint units_kv, uint layers,
                               ENUM_OPTIMIZATION optimization_type, uint batch)
  {
//--- Init parent object
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window * units_count, optimization_type, batch))
      ReturnFalse;
//--- Clear layers' array
   cLayers.Clear();
   cLayers.SetOpenCL(OpenCL);
//--- Second buffer prepare
   int lay_count = 0;
   CNeuronBaseOCL *flat = new CNeuronBaseOCL();
   if(!flat ||
      !flat.Init(0, lay_count, OpenCL, window_kv * units_kv, optimization, iBatch) ||
      !cLayers.Add(flat))
     {
      DeleteObj(flat);
      ReturnFalse;
     }
   lay_count++;
   CNeuronTransposeOCL *transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(0, lay_count, OpenCL, units_kv, window_kv, optimization, iBatch) ||
      !cLayers.Add(transp))
     {
      DeleteObj(transp);
      ReturnFalse;
     }
   lay_count++;
//--- Attention Action To Observation
   for(uint i = 0; i < layers; i++)
     {
      if(units_count > 1)
        {
         CNeuronRelativeSelfAttention *self = new CNeuronRelativeSelfAttention();
         if(!self ||
            !self.Init(0, lay_count, OpenCL, window, window_key, units_count, heads, optimization, iBatch) ||
            !cLayers.Add(self))
           {
            DeleteObj(self);
            ReturnFalse;
           }
         lay_count++;
        }
      CNeuronRelativeCrossAttention *cross = new CNeuronRelativeCrossAttention();
      if(!cross ||
         !cross.Init(0, lay_count, OpenCL, window, window_key, units_count, heads, units_kv, window_kv, optimization, iBatch) ||
         !cLayers.Add(cross))
        {
         DeleteObj(cross);
         ReturnFalse;
        }
      lay_count++;
      CResidualConv *ffn = new CResidualConv();
      if(!ffn ||
         !ffn.Init(0, lay_count, OpenCL, window, window, units_count, optimization, iBatch) ||
         !cLayers.Add(ffn))
        {
         DeleteObj(ffn);
         ReturnFalse;
        }
      lay_count++;
     }
   CNeuronConvSAMOCL *conv = new CNeuronConvSAMOCL();
   if(!conv ||
      !conv.Init(0, lay_count, OpenCL, window, window, window, units_count, 1, optimization, iBatch) ||
      !cLayers.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   SetActivationFunction(SIGMOID);
//---
   if(!SetOutput(conv.getOutput(), true) ||
      !SetGradient(conv.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronControlAgent::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!SecondInput)
      ReturnFalse;
//---
   CNeuronBaseOCL *second = cLayers[0];
   if(!second)
      ReturnFalse;
   if(!second.SetOutput(SecondInput, true))
      ReturnFalse;
//---
   second = cLayers[1];
   if(!second || !second.FeedForward(cLayers[0]))
      ReturnFalse;
//---
   CNeuronBaseOCL *first = NeuronOCL;
   CNeuronBaseOCL *main = NULL;
   for(int i = 2; i < cLayers.Total(); i++)
     {
      main = cLayers[i];
      if(!main ||
         !main.FeedForward(first, second.getOutput()))
         ReturnFalse;
      first = main;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronControlAgent::calcInputGradients(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput, CBufferFloat* SecondGradient, ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient)
      ReturnFalse;
//---
   CNeuronBaseOCL *main = cLayers[0];
   if(!main)
      ReturnFalse;
   if(!main.SetGradient(SecondGradient, true))
      ReturnFalse;
   main.SetActivationFunction(SecondActivation);
//---
   CNeuronBaseOCL *second = cLayers[1];
   if(!second)
      ReturnFalse;
   second.SetActivationFunction(SecondActivation);
   CBufferFloat *second_out = second.getOutput();
   CBufferFloat *second_gr = second.getGradient();
   CBufferFloat *temp = second.getPrevOutput();
   if(!second_gr.Fill(0))
      ReturnFalse;
//---
   for(int i = cLayers.Total() - 2; i >= 2; i--)
     {
      main = cLayers[i];
      if(!main)
         ReturnFalse;
      if(cLayers[i + 1].Type() == defNeuronRelativeCrossAttention)
        {
         if(!main.CalcHiddenGradients(cLayers[i + 1], second_out, temp, SecondActivation) ||
            !SumAndNormalize(temp, second_gr, second_gr, 1, false, 0, 0, 0, 1))
            ReturnFalse;
        }
      else
        {
         if(!main.CalcHiddenGradients(cLayers[i + 1]))
            ReturnFalse;
        }
     }
//---
   if(!NeuronOCL.CalcHiddenGradients(main.AsObject(), second_out, temp, SecondActivation))
      ReturnFalse;
   if(main.Type() == defNeuronRelativeCrossAttention)
     {
      if(!SumAndNormalize(temp, second_gr, second_gr, 1, false, 0, 0, 0, 1))
         ReturnFalse;
     }
   main = cLayers[0];
   if(!main.CalcHiddenGradients(second.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronControlAgent::updateInputWeights(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   CNeuronBaseOCL *second = cLayers[1];
   CNeuronBaseOCL *main = NULL;
   for(int i = cLayers.Total() - 1; i > 2; i--)
     {
      main = cLayers[i];
      if(!main ||
         !main.UpdateInputWeights(cLayers[i - 1], second.getOutput()))
         ReturnFalse;
     }
   main = cLayers[2];
   if(!main ||
      !main.UpdateInputWeights(NeuronOCL, second.getOutput()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                       uint window, uint window_key, uint units_count,
                       uint heads_mo, uint layers_mo, uint forecast, uint segments_rl,
                       float rho, uint layers_rl, uint n_actions, uint heads_contr,
                       uint layers_contr, int NormLayer, CNeuronBatchNormOCL * normLayer,
                       ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseSAMOCL::Init(numOutputs, myIndex, open_cl, n_actions, rho, optimization_type, batch))
      ReturnFalse;
//--- Market Observation
   if(!cMarketObserver.Init(0, 0, OpenCL, window, window_key, units_count, heads_mo, layers_mo, forecast, optimization, iBatch))
      ReturnFalse;
   if(!cRevIN.Init(0, 1, OpenCL, cMarketObserver.Neurons(), NormLayer, normLayer))
      ReturnFalse;
//--- RL Agent
   if(!cRLAgent.Init(0, 2, OpenCL, window, units_count, segments_rl, fRho, layers_rl, n_actions, optimization, iBatch))
      ReturnFalse;
//---
   if(!cControlAgent.Init(0, 3, OpenCL, 3, window_key, n_actions / 3, heads_contr, window, forecast, layers_contr, optimization, iBatch))
      ReturnFalse;
//---
   if(!SetOutput(cControlAgent.getOutput(), true) ||
      !SetGradient(cControlAgent.getGradient(), true))
      ReturnFalse;
   SetActivationFunction(SIGMOID);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMASA::SetActivationFunction(ENUM_ACTIVATION value)
  {
   cControlAgent.SetActivationFunction(value);
   cRLAgent.SetActivationFunction((ENUM_ACTIVATION)cControlAgent.Activation());
   CNeuronBaseSAMOCL::SetActivationFunction((ENUM_ACTIVATION)cControlAgent.Activation());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cMarketObserver.FeedForward(NeuronOCL.AsObject()))
      ReturnFalse;
   if(!cRLAgent.FeedForward(NeuronOCL.AsObject()))
      ReturnFalse;
   if(!cControlAgent.FeedForward(cRLAgent.AsObject(), cMarketObserver.getOutput()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::calcInputGradients(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput, CBufferFloat* SecondGradient, ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!cRevIN.FeedForward(cMarketObserver.AsObject()))
      ReturnFalse;
   float error = 1.0f;
   if(!cRevIN.calcOutputGradients(SecondGradient, error))
      ReturnFalse;
   if(!cMarketObserver.CalcHiddenGradients(cRevIN.AsObject()))
      ReturnFalse;
   if(!cRLAgent.CalcHiddenGradients(cControlAgent.AsObject(), cMarketObserver.getOutput(),
                                    cMarketObserver.getPrevOutput(),
                                    (ENUM_ACTIVATION)cMarketObserver.Activation()) ||
      !SumAndNormalize(cMarketObserver.getGradient(), cMarketObserver.getPrevOutput(), cMarketObserver.getGradient(), 1, false, 0, 0, 0, 1))
      ReturnFalse;
//---
   CBufferFloat *temp = cRLAgent.getGradient();
   if(!cRLAgent.SetGradient(cRLAgent.getPrevOutput(), false) ||
      !cRLAgent.calcOutputGradients(cControlAgent.getOutput(), error) ||
      !SumAndNormalize(temp, cRLAgent.getPrevOutput(), temp, 1, false, 0, 0, 0, 1) ||
      !cRLAgent.SetGradient(temp, false))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cMarketObserver.AsObject()))
      ReturnFalse;
   temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(NeuronOCL.getPrevOutput(), false) ||
      !NeuronOCL.calcOutputGradients(cRLAgent.getOutput(), error) ||
      !SumAndNormalize(temp, NeuronOCL.getPrevOutput(), temp, 1, false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cControlAgent.UpdateInputWeights(cRLAgent.AsObject(), cMarketObserver.getOutput()))
      ReturnFalse;
   if(!cRLAgent.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cMarketObserver.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMASA::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronBaseSAMOCL::SetOpenCL(obj);
   cMarketObserver.SetOpenCL(OpenCL);
   cRevIN.SetOpenCL(OpenCL);
   cRLAgent.SetOpenCL(OpenCL);
   cControlAgent.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::Save(const int file_handle)
  {
   if(!CNeuronBaseSAMOCL::Save(file_handle))
      ReturnFalse;
   if(!cMarketObserver.Save(file_handle))
      ReturnFalse;
   if(!cRevIN.Save(file_handle))
      ReturnFalse;
   if(!cRLAgent.Save(file_handle))
      ReturnFalse;
   if(!cControlAgent.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::Load(const int file_handle)
  {
   if(!CNeuronBaseSAMOCL::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMarketObserver.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cRevIN.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cRLAgent.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cControlAgent.AsObject()))
      ReturnFalse;
//---
   if(!SetOutput(cControlAgent.getOutput(), true) ||
      !SetGradient(cControlAgent.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASA::SetNormLayer(int NormLayer, CNeuronBatchNormOCL * normLayer)
  {
   return cRevIN.Init(0, 1, OpenCL, cMarketObserver.Neurons(), NormLayer, normLayer);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLRMultiAgentsOCL::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                    uint window_in, uint units_count, bool transpose,
                                    vector<float> &min_distance,
                                    ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   iAgents = (int)min_distance.Size();
   if(iAgents <= 0)
      ReturnFalse;
//---
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_in * units_count * iAgents, optimization_type, batch))
      ReturnFalse;
//---
   iVariables = (int)window_in;
   iCount = (int)units_count;
   bTranspose = transpose;
//---
   icIsTTP = OpenCL.AddBuffer(sizeof(int) * Neurons(), CL_MEM_READ_WRITE);
   if(icIsTTP < 0)
      ReturnFalse;
//---
   if(!cMinDistance.AssignArray(min_distance) ||
      !cMinDistance.BufferCreate(OpenCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLRMultiAgentsOCL::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL || !NeuronOCL.getOutput())
      ReturnFalse;
//---
   uint global_work_offset[3] = {0};
   uint global_work_size[3] = {iCount, iVariables, iAgents};
   uint local_work_size[3] = {iCount, 1, 1};
   ResetLastError();
   const int kernel = def_k_PLRMultiAgents;
   setBuffer(kernel, def_k_plr_inputs, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_plr_outputs, getOutputIndex())
   setBuffer(kernel, def_k_plt_isttp, icIsTTP)
   setArgument(kernel, def_k_plr_transpose, (int)bTranspose)
   setBuffer(kernel, def_k_plr_step, cMinDistance.GetIndex())
//---
   kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!Output.BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLRMultiAgentsOCL::calcInputGradients(CNeuronBaseOCL* prevLayer)
  {
   if(!OpenCL || !prevLayer || !prevLayer.getGradient())
      ReturnFalse;
//---
   uint global_work_offset[2] = {0};
   uint global_work_size[2] = {iCount, iVariables};
   ResetLastError();
   const int kernel = def_k_PLRMultiAgentsGrad;
   setBuffer(kernel, def_k_plrg_inputs_gr, prevLayer.getGradientIndex())
   setBuffer(kernel, def_k_plrg_outputs, getOutputIndex())
   setBuffer(kernel, def_k_plrg_outputs_gr, getGradientIndex())
   setArgument(kernel, def_k_plrg_transpose, (int)bTranspose)
   setArgument(kernel, def_k_plrg_agents, (int)iAgents)
//---
   kernelExecute(kernel, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!prevLayer.getGradient().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLRMultiAgentsOCL::Save(const int file_handle)
  {
   if(!CNeuronPLROCL::Save(file_handle))
      ReturnFalse;
   if(FileWriteInteger(file_handle, iAgents) < INT_VALUE)
      ReturnFalse;
   if(!cMinDistance.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPLRMultiAgentsOCL::Load(const int file_handle)
  {
   if(!CNeuronPLROCL::Load(file_handle))
      ReturnFalse;
//---
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iAgents = FileReadInteger(file_handle);
   if(!cMinDistance.Load(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronPLRMultiAgentsOCL::SetOpenCL(COpenCLMy * obj)
  {
//---
   CNeuronPLROCL::SetOpenCL(obj);
   cMinDistance.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCrossSectionalAnalysis::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
      uint window, uint window_key, uint heads, uint heads_kv,
      uint units_count, uint layers, uint layers_to_one_kv, uint variables,
      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMVMHAttentionMLKV::Init(numOutputs, myIndex, open_cl, window_key, window_key, heads, heads_kv,
                                      variables, layers, layers_to_one_kv, units_count, optimization_type, batch))
      ReturnFalse;
//---
   if(!cEmbeding.Init(0, 0, OpenCL, window, window, window_key, units_count, variables, optimization, iBatch))
      ReturnFalse;
   cEmbeding.SetActivationFunction(GELU);
   if(!cTransposeRCD.Init(0, 1, OpenCL, variables, units_count, window_key, optimization, iBatch))
      ReturnFalse;
   SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCrossSectionalAnalysis::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cEmbeding.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cTransposeRCD.FeedForward(cEmbeding.AsObject()))
      ReturnFalse;
//---
   return CNeuronMVMHAttentionMLKV::feedForward(cTransposeRCD.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCrossSectionalAnalysis::calcInputGradients(CNeuronBaseOCL* prevLayer)
  {
   if(!prevLayer)
      ReturnFalse;
   if(!CNeuronMVMHAttentionMLKV::calcInputGradients(cTransposeRCD.AsObject()))
      ReturnFalse;
   if(!cEmbeding.CalcHiddenGradients(cTransposeRCD.AsObject()))
      ReturnFalse;
   if(cEmbeding.Activation() != None)
     {
      if(!DeActivation(cEmbeding.getOutput(), cEmbeding.getGradient(), cEmbeding.getGradient(), cEmbeding.Activation()))
         ReturnFalse;
     }
   if(!prevLayer.CalcHiddenGradients(cEmbeding.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCrossSectionalAnalysis::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronMVMHAttentionMLKV::updateInputWeights(cTransposeRCD.AsObject()))
      ReturnFalse;
   if(!cEmbeding.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronCrossSectionalAnalysis::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronMVMHAttentionMLKV::SetOpenCL(obj);
   cEmbeding.SetOpenCL(OpenCL);
   cTransposeRCD.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCrossSectionalAnalysis::Save(const int file_handle)
  {
   if(!CNeuronMVMHAttentionMLKV::Save(file_handle))
      ReturnFalse;
   if(!cEmbeding.Save(file_handle))
      ReturnFalse;
   if(!cTransposeRCD.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCrossSectionalAnalysis::Load(const int file_handle)
  {
   if(!CNeuronMVMHAttentionMLKV::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEmbeding.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTransposeRCD.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronTemporalAnalysis::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                   uint window, uint window_key, uint heads, uint heads_kv,
                                   uint units_count, uint layers, uint layers_to_one_kv, uint variables,
                                   ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronCrossSectionalAnalysis::Init(numOutputs, myIndex, open_cl, 3 * units_count, window_key, heads, heads_kv,
                                           window / 3, layers, layers_to_one_kv, variables, optimization_type, batch))
      ReturnFalse;
   if(!cTranspose.Init(0, 0, OpenCL, variables, units_count, window, optimization_type, batch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronTemporalAnalysis::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cTranspose.FeedForward(NeuronOCL))
      ReturnFalse;
//---
   return CNeuronCrossSectionalAnalysis::feedForward(cTranspose.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronTemporalAnalysis::calcInputGradients(CNeuronBaseOCL* prevLayer)
  {
   if(!prevLayer)
      ReturnFalse;
   if(!CNeuronCrossSectionalAnalysis::calcInputGradients(cTranspose.AsObject()))
      ReturnFalse;
//---
   return prevLayer.CalcHiddenGradients(cTranspose.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronTemporalAnalysis::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   return CNeuronCrossSectionalAnalysis::updateInputWeights(cTranspose.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronTemporalAnalysis::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronCrossSectionalAnalysis::SetOpenCL(obj);
   cTranspose.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronTemporalAnalysis::Save(const int file_handle)
  {
   if(!CNeuronCrossSectionalAnalysis::Save(file_handle))
      ReturnFalse;
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronTemporalAnalysis::Load(const int file_handle)
  {
   if(!CNeuronCrossSectionalAnalysis::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPortfolioGenerator::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                     uint assets, uint time_points, uint dimension,
                                     uint agents, uint projection,
                                     ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(assets <= 0 || time_points <= 0 || dimension <= 0 || agents <= 0)
      ReturnFalse;
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, projection, optimization_type, batch))
      ReturnFalse;
//---
   iAssets = assets;
   iTimePoints = time_points;
   iDimension = dimension;
   iAgents = agents;
//---
   if(!cTransposeVRC.Init(0, 0, OpenCL, iAgents, iTimePoints, iDimension, optimization, iBatch))
      ReturnFalse;
   if(!cAssetTime[0].Init(0, 1, OpenCL, iAssets * iTimePoints * iAgents, optimization, iBatch))
      ReturnFalse;
   cAssetTime[0].SetActivationFunction(None);
   if(!cSoftMax.Init(0, 2, OpenCL, cAssetTime[0].Neurons(), optimization, iBatch))
      ReturnFalse;
   cSoftMax.SetHeads(iAssets * iAgents);
   if(!cAssetTime[1].Init(Neurons(), 3, OpenCL, iAssets * iDimension * iAgents, optimization, iBatch))
      ReturnFalse;
   cAssetTime[1].SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPortfolioGenerator::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!SecondInput)
      ReturnFalse;
//---
   if(!cTransposeVRC.FeedForward(NeuronOCL))
      ReturnFalse;
//---
   if(!MatMul(SecondInput, cTransposeVRC.getOutput(), cAssetTime[0].getOutput(), iAssets, iDimension, iTimePoints, iAgents))
      ReturnFalse;
   if(!cSoftMax.FeedForward(cAssetTime[0].AsObject()))
      ReturnFalse;
   if(!MatMul(cSoftMax.getOutput(), NeuronOCL.getOutput(), cAssetTime[1].getOutput(), iAssets, iTimePoints, iDimension, iAgents))
      ReturnFalse;
//---
   return CNeuronBaseOCL::feedForward(cAssetTime[1].AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPortfolioGenerator::calcInputGradients(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput, CBufferFloat* SecondGradient, ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient || !SecondInput)
      ReturnFalse;
   if(!CNeuronBaseOCL::calcInputGradients(cAssetTime[1].AsObject()))
      ReturnFalse;
   if(!MatMulGrad(cSoftMax.getOutput(), cSoftMax.getGradient(),
                  NeuronOCL.getOutput(), cTransposeVRC.getPrevOutput(),
                  cAssetTime[1].getGradient(),
                  iAssets, iTimePoints, iDimension, iAgents))
      ReturnFalse;
   if(!cAssetTime[0].CalcHiddenGradients(cSoftMax.AsObject()))
      ReturnFalse;
   if(!MatMulGrad(SecondInput, SecondGradient,
                  cTransposeVRC.getOutput(), cTransposeVRC.getGradient(),
                  cAssetTime[0].getGradient(),
                  iAssets, iDimension, iTimePoints, iAgents))
      ReturnFalse;
   if(SecondActivation != None)
      if(!DeActivation(SecondInput, SecondGradient, SecondGradient, SecondActivation))
         ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cTransposeVRC.AsObject()) ||
      !SumAndNormalize(NeuronOCL.getGradient(), cTransposeVRC.getPrevOutput(), NeuronOCL.getGradient(), iDimension, false, 0, 0, 0, 1))
      ReturnFalse;
   if(NeuronOCL.Activation() != None)
      if(!DeActivation(NeuronOCL.getOutput(), cTransposeVRC.getPrevOutput(), cTransposeVRC.getPrevOutput(), NeuronOCL.Activation()))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPortfolioGenerator::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   return CNeuronBaseOCL::updateInputWeights(cAssetTime[1].AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronPortfolioGenerator::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   for(uint i = 0; i < cAssetTime.Size(); i++)
      cAssetTime[i].SetOpenCL(OpenCL);
   cTransposeVRC.SetOpenCL(OpenCL);
   cSoftMax.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPortfolioGenerator::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cAssetTime.Size(); i++)
      if(!cAssetTime[i].Save(file_handle))
         ReturnFalse;
   if(!cTransposeVRC.Save(file_handle))
      ReturnFalse;
   if(!cSoftMax.Save(file_handle))
      ReturnFalse;
//---
   if(FileWriteInteger(file_handle, (int)iAssets) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, (int)iTimePoints) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, (int)iAgents) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, (int)iDimension) < INT_VALUE)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPortfolioGenerator::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cAssetTime.Size(); i++)
      if(!LoadInsideLayer(file_handle, cAssetTime[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTransposeVRC.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSoftMax.AsObject()))
      ReturnFalse;
//---
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iAssets = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iTimePoints = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iAgents = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iDimension = (uint)FileReadInteger(file_handle);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASAAT::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                         uint window, uint window_key, uint heads, uint units_cout,
                         uint layers, vector<float> &min_distance, uint projection,
                         ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronPortfolioGenerator::Init(numOutputs, myIndex, open_cl, window, units_cout / 3,
                                       window_key, (uint)min_distance.Size() + 1, projection, optimization_type, batch))
      ReturnFalse;
   if(!cTranspose.Init(0, 0, OpenCL, units_cout, window, optimization, iBatch))
      ReturnFalse;
   if(!cPLR.Init(0, 1, OpenCL, window, units_cout, false, min_distance, optimization, iBatch))
      ReturnFalse;
   if(!cConcat.Init(0, 2, OpenCL, cTranspose.Neurons() + cPLR.Neurons(), optimization, iBatch))
      ReturnFalse;
   if(!cCrossSectionalAnalysis.Init(0, 3, OpenCL, units_cout, window_key, heads, heads / 2, window, layers, 1, iAgents, optimization, iBatch))
      ReturnFalse;
   if(!cTemporalAnalysis.Init(0, 4, OpenCL, units_cout, window_key, heads, heads / 2, window, layers, 1, iAgents, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASAAT::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cTranspose.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cPLR.FeedForward(cTranspose.AsObject()))
      ReturnFalse;
   if(!Concat(cTranspose.getOutput(), cPLR.getOutput(), cConcat.getOutput(), cTranspose.Neurons(), cPLR.Neurons(), 1))
      ReturnFalse;
   if(!cCrossSectionalAnalysis.FeedForward(cConcat.AsObject()))
      ReturnFalse;
   if(!cTemporalAnalysis.FeedForward(cConcat.AsObject()))
      ReturnFalse;
//---
   return CNeuronPortfolioGenerator::feedForward(cTemporalAnalysis.AsObject(), cCrossSectionalAnalysis.getOutput());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASAAT::calcInputGradients(CNeuronBaseOCL* prevLayer)
  {
   if(!prevLayer)
      ReturnFalse;
   if(!CNeuronPortfolioGenerator::calcInputGradients(cTemporalAnalysis.AsObject(),
         cCrossSectionalAnalysis.getOutput(),
         cCrossSectionalAnalysis.getGradient(),
         (ENUM_ACTIVATION)cCrossSectionalAnalysis.Activation()))
      ReturnFalse;
   if(!cConcat.CalcHiddenGradients(cCrossSectionalAnalysis.AsObject()))
      ReturnFalse;
   CBufferFloat *grad = cConcat.getGradient();
   if(!cConcat.SetGradient(cConcat.getPrevOutput(), false) ||
      !cConcat.CalcHiddenGradients(cTemporalAnalysis.AsObject()) ||
      !SumAndNormalize(grad, cConcat.getGradient(), grad, 1, 0, 0, 0, 0, 1) ||
      !cConcat.SetGradient(grad, false))
      ReturnFalse;
//---
   if(!DeConcat(cTranspose.getPrevOutput(), cPLR.getGradient(), cConcat.getGradient(), cTranspose.Neurons(), cPLR.Neurons(), 1))
      ReturnFalse;
   if(cPLR.Activation() != None)
      if(!DeActivation(cPLR.getOutput(), cPLR.getGradient(), cPLR.getGradient(), cPLR.Activation()))
         ReturnFalse;
//---
   if(!cTranspose.CalcHiddenGradients(cPLR.AsObject()) ||
      !SumAndNormalize(cTranspose.getGradient(), cTranspose.getPrevOutput(), cTranspose.getGradient(), iDimension, false, 0, 0, 0, 1))
      ReturnFalse;
   if(cTranspose.Activation() != None)
      if(!DeActivation(cTranspose.getOutput(), cTranspose.getGradient(), cTranspose.getGradient(), cTranspose.Activation()))
         ReturnFalse;
   if(!prevLayer.CalcHiddenGradients(cTranspose.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASAAT::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cCrossSectionalAnalysis.UpdateInputWeights(cConcat.AsObject()))
      ReturnFalse;
   if(!cTemporalAnalysis.UpdateInputWeights(cConcat.AsObject()))
      ReturnFalse;
//---
   return CNeuronPortfolioGenerator::updateInputWeights(cTemporalAnalysis.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMASAAT::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronPortfolioGenerator::SetOpenCL(obj);
   cTranspose.SetOpenCL(OpenCL);
   cPLR.SetOpenCL(OpenCL);
   cConcat.SetOpenCL(OpenCL);
   cCrossSectionalAnalysis.SetOpenCL(OpenCL);
   cTemporalAnalysis.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASAAT::Save(const int file_handle)
  {
   if(!CNeuronPortfolioGenerator::Save(file_handle))
      ReturnFalse;
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   if(!cPLR.Save(file_handle))
      ReturnFalse;
   if(!cCrossSectionalAnalysis.Save(file_handle))
      ReturnFalse;
   if(!cTemporalAnalysis.Save(file_handle))
      ReturnFalse;
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMASAAT::Load(const int file_handle)
  {
   if(!CNeuronPortfolioGenerator::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cPLR.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cCrossSectionalAnalysis.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTemporalAnalysis.AsObject()))
      ReturnFalse;
//---
   if(!cConcat.Init(0, 2, OpenCL, cTranspose.Neurons() + cPLR.Neurons(), optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                         uint window, uint window_key, uint units_count, uint heads,
                         ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl, window, window_key,
                                           units_count, heads, window, units_count, optimization_type, batch))
      ReturnFalse;
   if(!cLSTM.Init(0, 0, OpenCL, iWindow, iUnits, optimization, iBatch))
      ReturnFalse;
   if(!cMamba.Init(0, 1, OpenCL, iWindow, 2 * iWindow, iUnits, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cLSTM.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cMamba.FeedForward(NeuronOCL))
      ReturnFalse;
//---
   return CNeuronRelativeCrossAttention::feedForward(cMamba.AsObject(), cLSTM.getOutput());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!CNeuronRelativeCrossAttention::calcInputGradients(cMamba.AsObject(), cLSTM.getOutput(),
         cLSTM.getGradient(), (ENUM_ACTIVATION)cLSTM.Activation()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cMamba.AsObject()))
      ReturnFalse;
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cMamba.getPrevOutput(), false))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cLSTM.AsObject()))
      ReturnFalse;
   if(!NeuronOCL.SetGradient(temp, false) ||
      !SumAndNormalize(temp, cMamba.getPrevOutput(), temp, iWindow, false, 0, 0, 0, 1))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronRelativeCrossAttention::updateInputWeights(cMamba.AsObject(), cLSTM.getOutput()))
      ReturnFalse;
   if(!cLSTM.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cMamba.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMemory::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronRelativeCrossAttention::SetOpenCL(obj);
   cLSTM.SetOpenCL(OpenCL);
   cMamba.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::Save(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Save(file_handle))
      ReturnFalse;
   if(!cLSTM.Save(file_handle))
      ReturnFalse;
   if(!cMamba.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::Load(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cLSTM.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMamba.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemory::Clear(void)
  {
   if(!CNeuronRelativeCrossAttention::Clear())
      ReturnFalse;
   if(!cLSTM.Clear())
      ReturnFalse;
   if(!cMamba.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                         uint window, uint window_key, uint units_count, uint heads,
                         uint account_descr, uint nactions,
                         ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl,
                                           nactions / 2, window_key, 2, heads, window,
                                           units_count, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cTransposeState.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMemory[0].Init(0, index, OpenCL, window, window_key, units_count, heads, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMemory[1].Init(0, index, OpenCL, units_count, window_key, window, heads, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cCrossMemory.Init(0, index, OpenCL, window, window_key, units_count, heads, units_count, window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMemoryToAccount.Init(0, index, OpenCL, window, window_key, units_count, heads, account_descr, 1, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cActionToAccount.Init(0, index, OpenCL, nactions / 2, window_key, 2, heads, account_descr, 1, optimization, iBatch))
      ReturnFalse;
//---
   if(!Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!cTransposeState.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cMemory[0].FeedForward(NeuronOCL) ||
      !cMemory[1].FeedForward(cTransposeState.AsObject()))
      ReturnFalse;
   if(!cCrossMemory.FeedForward(cMemory[0].AsObject(), cMemory[1].getOutput()))
      ReturnFalse;
   if(!cMemoryToAccount.FeedForward(cCrossMemory.AsObject(), SecondInput))
      ReturnFalse;
   if(!cActionToAccount.FeedForward(this.AsObject(), SecondInput))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   if(!CNeuronRelativeCrossAttention::feedForward(cActionToAccount.AsObject(), cMemoryToAccount.getOutput()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::calcInputGradients(CNeuronBaseOCL* NeuronOCL,
                                       CBufferFloat* SecondInput,
                                       CBufferFloat* SecondGradient,
                                       ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondInput || !SecondGradient)
      ReturnFalse;
//---
   if(!CNeuronRelativeCrossAttention::calcInputGradients(cActionToAccount.AsObject(),
         cMemoryToAccount.getOutput(),
         cMemoryToAccount.getGradient(),
         (ENUM_ACTIVATION)cMemoryToAccount.Activation()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   CBufferFloat *temp = Gradient;
   if(!SetGradient(cMemoryToAccount.getPrevOutput(), false))
      ReturnFalse;
   if(!calcHiddenGradients(cActionToAccount.AsObject(), SecondInput, SecondGradient, SecondActivation))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   Gradient = temp;
   if(!cCrossMemory.CalcHiddenGradients(cMemoryToAccount.AsObject(), SecondInput, cMemoryToAccount.getPrevOutput(), SecondActivation))
      ReturnFalse;
   if(!SumAndNormalize(SecondGradient, cMemoryToAccount.getPrevOutput(), SecondGradient, 1, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cMemory[0].CalcHiddenGradients(cCrossMemory.AsObject(), cMemory[1].getOutput(), cMemory[1].getGradient(), (ENUM_ACTIVATION)cMemory[1].Activation()))
      ReturnFalse;
   if(!cTransposeState.CalcHiddenGradients(cMemory[1].AsObject()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cMemory[0].AsObject()))
      ReturnFalse;
   temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cTransposeState.getPrevOutput(), false) ||
      !NeuronOCL.CalcHiddenGradients(cTransposeState.AsObject()) ||
      !NeuronOCL.SetGradient(temp, false) ||
      !SumAndNormalize(temp, cTransposeState.getPrevOutput(), temp, iWindow, false, 0, 0, 0, 1))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::updateInputWeights(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!NeuronOCL || !SecondInput)
      ReturnFalse;
//---
   if(!CNeuronRelativeCrossAttention::updateInputWeights(cActionToAccount.AsObject(),
         cMemoryToAccount.getOutput()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   if(!cActionToAccount.UpdateInputWeights(this.AsObject(), SecondInput))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   if(!cMemoryToAccount.UpdateInputWeights(cCrossMemory.AsObject(), SecondInput))
      ReturnFalse;
   if(!cCrossMemory.UpdateInputWeights(cMemory[0].AsObject(), cMemory[1].getOutput()))
      ReturnFalse;
   if(!cMemory[1].UpdateInputWeights(cTransposeState.AsObject()))
      ReturnFalse;
   if(!cMemory[0].UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::Clear(void)
  {
   if(!Output ||
      !Output.Fill(0))
      ReturnFalse;
   for(uint i = 0; i < cMemory.Size(); i++)
      if(!cMemory[i].Clear())
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFinMem::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronRelativeCrossAttention::SetOpenCL(obj);
   cTransposeState.SetOpenCL(OpenCL);
   for(uint i = 0; i < cMemory.Size(); i++)
      cMemory[i].SetOpenCL(OpenCL);
   cCrossMemory.SetOpenCL(OpenCL);
   cMemoryToAccount.SetOpenCL(OpenCL);
   cActionToAccount.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::Save(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Save(file_handle))
      ReturnFalse;
   if(!cTransposeState.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cMemory.Size(); i++)
      if(!cMemory[i].Save(file_handle))
         ReturnFalse;
   if(!cCrossMemory.Save(file_handle))
      ReturnFalse;
   if(!cMemoryToAccount.Save(file_handle))
      ReturnFalse;
   if(!cActionToAccount.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinMem::Load(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTransposeState.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < cMemory.Size(); i++)
      if(!LoadInsideLayer(file_handle, cMemory[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cCrossMemory.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMemoryToAccount.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cActionToAccount.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                     uint window, uint window_key, uint units_count, uint heads,
                                     ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, window, window_key, units_count, heads,
                           optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cChangeLSTM.Init(0, index, OpenCL, window, units_count, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cChangeMamba.Init(0, index, OpenCL, window, 2 * window, units_count, optimization, iBatch))
      ReturnFalse;
   for(int i = 0; i < 2; i++)
     {
      index++;
      if(!cCrossAttention[i].Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count, optimization, iBatch))
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cChangeLSTM.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cChangeMamba.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cCrossAttention[0].FeedForward(NeuronOCL, cChangeLSTM.getOutput()))
      ReturnFalse;
   if(!cCrossAttention[1].FeedForward(cCrossAttention[0].AsObject(), cChangeMamba.getOutput()))
      ReturnFalse;
//---
   return CNeuronMemory::feedForward(cCrossAttention[1].AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronMemory::calcInputGradients(cCrossAttention[1].AsObject()))
      ReturnFalse;
   if(!cCrossAttention[0].CalcHiddenGradients(cCrossAttention[1].AsObject(), cChangeMamba.getOutput(),
         cChangeMamba.getGradient(), (ENUM_ACTIVATION)cChangeMamba.Activation()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cCrossAttention[0].AsObject(), cChangeLSTM.getOutput(),
                                     cChangeLSTM.getGradient(), (ENUM_ACTIVATION)cChangeLSTM.Activation()))
      ReturnFalse;
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cChangeMamba.getPrevOutput(), false) ||
      !NeuronOCL.CalcHiddenGradients(cChangeMamba.AsObject()) ||
      !SumAndNormalize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cChangeLSTM.AsObject()) ||
      !SumAndNormalize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronMemory::updateInputWeights(cCrossAttention[1].AsObject()))
      ReturnFalse;
   if(!cCrossAttention[1].UpdateInputWeights(cCrossAttention[0].AsObject(), cChangeMamba.getOutput()))
      ReturnFalse;
   if(!cCrossAttention[0].UpdateInputWeights(NeuronOCL, cChangeLSTM.getOutput()))
      ReturnFalse;
   if(!cChangeMamba.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cChangeLSTM.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::Save(const int file_handle)
  {
   if(!CNeuronMemory::Save(file_handle))
      ReturnFalse;
   if(!cChangeLSTM.Save(file_handle))
      ReturnFalse;
   if(!cChangeMamba.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cCrossAttention.Size(); i++)
      if(!cCrossAttention[i].Save(file_handle))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::Load(const int file_handle)
  {
   if(!CNeuronMemory::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cChangeLSTM.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cChangeMamba.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < cCrossAttention.Size(); i++)
      if(!LoadInsideLayer(file_handle, cCrossAttention[i].AsObject()))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronLowLevelReflection::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronMemory::SetOpenCL(obj);
   cChangeLSTM.SetOpenCL(OpenCL);
   cChangeMamba.SetOpenCL(OpenCL);
   for(uint i = 0; i < cCrossAttention.Size(); i++)
      cCrossAttention[i].SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronLowLevelReflection::Clear(void)
  {
   if(!CNeuronMemory::Clear())
      ReturnFalse;
   if(!cChangeLSTM.Clear())
      ReturnFalse;
   if(!cChangeMamba.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                      uint window, uint window_key, uint units_count,
                                      uint heads, uint desc_account, uint actions_state,
                                      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, 3, window_key, actions_state / 3,
                           heads, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cAccount.Init(0, index, OpenCL, desc_account, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cHistoryAccount.Init(0, index, OpenCL, desc_account, 1, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cActionReason.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
                          window, units_count, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cHistoryActions.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cActionResult.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads,
                          desc_account, 1, optimization, iBatch))
      ReturnFalse;;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!NeuronOCL || !SecondInput)
      ReturnFalse;
//---
   if(cAccount.getOutput() != SecondInput)
     {
      if(cAccount.Neurons() != SecondInput.Total())
         if(!cAccount.Init(0, 0, OpenCL, SecondInput.Total(), optimization, iBatch))
            ReturnFalse;
      if(!cAccount.SetOutput(SecondInput, true))
         ReturnFalse;
     }
//---
   if(!cHistoryAccount.FeedForward(cAccount.AsObject()))
      ReturnFalse;
   if(!cActionReason.FeedForward(this.AsObject(), NeuronOCL.getOutput()))
      ReturnFalse;
   if(!cHistoryActions.FeedForward(cActionReason.AsObject()))
      ReturnFalse;
   if(!cActionResult.FeedForward(cHistoryActions.AsObject(), cHistoryAccount.getOutput()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//---
   return CNeuronMemory::feedForward(cActionResult.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::calcInputGradients(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput,
      CBufferFloat* SecondGradient,
      ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient)
      ReturnFalse;
//---
   if(cAccount.getGradient() != SecondGradient)
      if(!cAccount.SetGradient(SecondGradient, true))
         ReturnFalse;
   cAccount.SetActivationFunction(SecondActivation);
//---
   if(!CNeuronMemory::calcInputGradients(cActionResult.AsObject()))
      ReturnFalse;
   if(!cHistoryActions.CalcHiddenGradients(cActionResult.AsObject(),
                                           cHistoryAccount.getOutput(),
                                           cHistoryAccount.getGradient(),
                                           (ENUM_ACTIVATION)cHistoryAccount.Activation()))
      ReturnFalse;
   if(!cActionReason.CalcHiddenGradients(cHistoryActions.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   CBufferFloat *temp = Gradient;
   Gradient = cActionReason.getPrevOutput();
   if(!calcHiddenGradients(cActionReason.AsObject(), NeuronOCL.getOutput(),
                           NeuronOCL.getGradient(), (ENUM_ACTIVATION)NeuronOCL.Activation()))
      ReturnFalse;
//---
   Gradient = temp;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//---
   if(!cAccount.CalcHiddenGradients(cHistoryAccount.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::updateInputWeights(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!CNeuronMemory::updateInputWeights(cActionResult.AsObject()))
      ReturnFalse;
   if(!cActionResult.UpdateInputWeights(cHistoryActions.AsObject(), cHistoryAccount.getOutput()))
      ReturnFalse;
   if(!cHistoryActions.UpdateInputWeights(cActionReason.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   if(!cActionReason.UpdateInputWeights(this.AsObject(), NeuronOCL.getOutput()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   if(!cHistoryAccount.UpdateInputWeights(cAccount.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::Save(const int file_handle)
  {
   if(!CNeuronMemory::Save(file_handle))
      ReturnFalse;
   if(!cAccount.Save(file_handle))
      ReturnFalse;
   if(!cHistoryAccount.Save(file_handle))
      ReturnFalse;
   if(!cActionReason.Save(file_handle))
      ReturnFalse;
   if(!cHistoryActions.Save(file_handle))
      ReturnFalse;
   if(!cActionResult.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::Load(const int file_handle)
  {
   if(!CNeuronMemory::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cAccount.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHistoryAccount.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cActionReason.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHistoryActions.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cActionResult.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHighLevelReflection::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronMemory::SetOpenCL(obj);
   cAccount.SetOpenCL(OpenCL);
   cHistoryAccount.SetOpenCL(OpenCL);
   cActionReason.SetOpenCL(OpenCL);
   cHistoryActions.SetOpenCL(OpenCL);
   cActionResult.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHighLevelReflection::Clear(void)
  {
   if(!CNeuronMemory::Clear())
      ReturnFalse;
   if(!cAccount.Clear())
      ReturnFalse;
   if(!cHistoryAccount.Clear())
      ReturnFalse;
   if(!cActionReason.Clear())
      ReturnFalse;
   if(!cHistoryActions.Clear())
      ReturnFalse;
   if(!cActionResult.Clear())
      ReturnFalse;;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMoreLessEqual::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL)
      ReturnFalse;
   uint global_work_offset[1] = { 0 };
   uint global_work_size[1] = { Neurons() };
   ResetLastError();
   const int kernel = def_k_MoreLessEqual;
   setBuffer(kernel, def_k_mle_inputs, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_mle_outputs, getOutputIndex())
//---
   kernelExecute(kernel, global_work_offset, global_work_size)
#ifdef  _DEBUG
   if(!NeuronOCL.getOutput().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMoreLessEqual::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL || !NeuronOCL.getGradient())
      ReturnFalse;
   return NeuronOCL.getGradient().Fill(0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                           uint window, uint window_key, uint units_count, uint heads,
                           uint account_descr, uint nactions, uint segments,
                           ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl, 3,
                                           window_key, nactions / 3, heads,
                                           window, units_count,
                                           optimization_type, batch))
      ReturnFalse;
   int index = 0;
   if(!cMarketIntelligence.Init(0, index, OpenCL, window, units_count, segments, 0.2f, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMarketMemory.Init(0, index, OpenCL, window, window_key, units_count, heads, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cTransposeState.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cLowLevelReflection[0].Init(0, index, OpenCL, window, window_key, units_count, heads, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cLowLevelReflection[1].Init(0, index, OpenCL, units_count, window_key, window, heads, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cHighLevelReflection.Init(0, index, OpenCL, window, window_key, units_count, heads, account_descr, nactions, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cTools.Init(0, index, OpenCL, window * units_count, optimization, iBatch))
      ReturnFalse;
   cTools.SetActivationFunction(None);
   index++;
   if(!cCrossLowLevel.Init(0, index, OpenCL, window, window_key, units_count, heads, units_count, window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMarketToLowLevel.Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMarketToTools.Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!cMarketIntelligence.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cMarketMemory.FeedForward(cMarketIntelligence.AsObject()))
      ReturnFalse;
   if(!cTransposeState.FeedForward(cMarketIntelligence.AsObject()))
      ReturnFalse;
   if(!cLowLevelReflection[0].FeedForward(cMarketIntelligence.AsObject()))
      ReturnFalse;
   if(!cLowLevelReflection[1].FeedForward(cTransposeState.AsObject()))
      ReturnFalse;
   if(!cHighLevelReflection.FeedForward(cMarketIntelligence.AsObject(), SecondInput))
      ReturnFalse;
   if(!cTools.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cCrossLowLevel.FeedForward(cLowLevelReflection[0].AsObject(), cLowLevelReflection[1].getOutput()))
      ReturnFalse;
   if(!cMarketToLowLevel.FeedForward(cMarketMemory.AsObject(), cCrossLowLevel.getOutput()))
      ReturnFalse;
   if(!cMarketToTools.FeedForward(cMarketToLowLevel.AsObject(), cTools.getOutput()))
      ReturnFalse;
//---
   return CNeuronRelativeCrossAttention::feedForward(cHighLevelReflection.AsObject(), cMarketToTools.getOutput());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::calcInputGradients(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput,
      CBufferFloat* SecondGradient,
      ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient)
      ReturnFalse;
//---
   if(!CNeuronRelativeCrossAttention::calcInputGradients(cHighLevelReflection.AsObject(),
         cMarketToTools.getOutput(),
         cMarketToTools.getGradient(),
         (ENUM_ACTIVATION)cMarketToTools.Activation()))
      ReturnFalse;
//---
   if(!cMarketToLowLevel.CalcHiddenGradients(cMarketToTools.AsObject(),
         cTools.getOutput(),
         cTools.getGradient(),
         (ENUM_ACTIVATION)cTools.Activation()))
      ReturnFalse;
//---
   if(!cMarketMemory.CalcHiddenGradients(cMarketToLowLevel.AsObject(),
                                         cCrossLowLevel.getOutput(),
                                         cCrossLowLevel.getGradient(),
                                         (ENUM_ACTIVATION)cCrossLowLevel.Activation()))
      ReturnFalse;
//---
   if(!cLowLevelReflection[0].CalcHiddenGradients(cCrossLowLevel.AsObject(),
         cLowLevelReflection[1].getOutput(),
         cLowLevelReflection[1].getGradient(),
         (ENUM_ACTIVATION)cLowLevelReflection[1].Activation()))
      ReturnFalse;
   if(!cTransposeState.CalcHiddenGradients(cLowLevelReflection[1].AsObject()))
      ReturnFalse;
//---
   if(!((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).CalcHiddenGradients(cMarketMemory.AsObject()))
      ReturnFalse;
   CBufferFloat *temp = cMarketIntelligence.getGradient();
   if(!cMarketIntelligence.SetGradient(cMarketIntelligence.getPrevOutput(), false) ||
      !((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).CalcHiddenGradients(cHighLevelReflection.AsObject(),
            SecondInput, SecondGradient, SecondActivation) ||
      !SumAndNormalize(temp, cMarketIntelligence.getGradient(), temp, 1, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).CalcHiddenGradients(cLowLevelReflection[0].AsObject()) ||
      !SumAndNormalize(temp, cMarketIntelligence.getGradient(), temp, 1, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!((CNeuronBaseOCL*)cMarketIntelligence.AsObject()).CalcHiddenGradients(cTransposeState.AsObject()) ||
      !SumAndNormalize(temp, cMarketIntelligence.getGradient(), temp, 1, false, 0, 0, 0, 1) ||
      !cMarketIntelligence.SetGradient(temp, false))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cMarketIntelligence.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::updateInputWeights(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
//---
   if(!CNeuronRelativeCrossAttention::updateInputWeights(cHighLevelReflection.AsObject(),
         cMarketToTools.getOutput()))
      ReturnFalse;
//---
   if(!cMarketToTools.UpdateInputWeights(cMarketToLowLevel.AsObject(),
                                         cTools.getOutput()))
      ReturnFalse;
//---
   if(!cMarketToLowLevel.UpdateInputWeights(cMarketMemory.AsObject(),
         cCrossLowLevel.getOutput()))
      ReturnFalse;
   if(!cMarketMemory.UpdateInputWeights(cMarketIntelligence.AsObject()))
      ReturnFalse;
//---
   if(!cCrossLowLevel.UpdateInputWeights(cLowLevelReflection[0].AsObject(),
                                         cLowLevelReflection[1].getOutput()))
      ReturnFalse;
//---
   if(!cLowLevelReflection[0].UpdateInputWeights(cMarketIntelligence.AsObject()))
      ReturnFalse;
   if(!cLowLevelReflection[1].UpdateInputWeights(cTransposeState.AsObject()))
      ReturnFalse;
//---
   if(!cHighLevelReflection.UpdateInputWeights(cMarketIntelligence.AsObject(), SecondInput))
      ReturnFalse;
//---
   if(!cMarketIntelligence.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::Save(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Save(file_handle))
      ReturnFalse;
   if(!cMarketIntelligence.Save(file_handle))
      ReturnFalse;
   if(!cMarketMemory.Save(file_handle))
      ReturnFalse;
   if(!cTransposeState.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cLowLevelReflection.Size(); i++)
      if(!cLowLevelReflection[i].Save(file_handle))
         ReturnFalse;
   if(!cHighLevelReflection.Save(file_handle))
      ReturnFalse;
   if(!cTools.Save(file_handle))
      ReturnFalse;
   if(!cCrossLowLevel.Save(file_handle))
      ReturnFalse;
   if(!cMarketToLowLevel.Save(file_handle))
      ReturnFalse;
   if(!cMarketToTools.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::Load(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMarketIntelligence.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMarketMemory.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTransposeState.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < cLowLevelReflection.Size(); i++)
      if(!LoadInsideLayer(file_handle, cLowLevelReflection[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHighLevelReflection.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTools.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cCrossLowLevel.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMarketToLowLevel.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMarketToTools.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFinAgent::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronRelativeCrossAttention::SetOpenCL(obj);
   cMarketIntelligence.SetOpenCL(OpenCL);
   cMarketMemory.SetOpenCL(OpenCL);
   cTransposeState.SetOpenCL(OpenCL);
   for(uint i = 0; i < cLowLevelReflection.Size(); i++)
      cLowLevelReflection[i].SetOpenCL(OpenCL);
   cHighLevelReflection.SetOpenCL(OpenCL);
   cTools.SetOpenCL(OpenCL);
   cCrossLowLevel.SetOpenCL(OpenCL);
   cMarketToLowLevel.SetOpenCL(OpenCL);
   cMarketToTools.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinAgent::Clear(void)
  {
   if(!CNeuronRelativeCrossAttention::Clear())
      ReturnFalse;
   if(!cMarketIntelligence.Clear())
      ReturnFalse;
   if(!cMarketMemory.Clear())
      ReturnFalse;
   if(!cTransposeState.Clear())
      ReturnFalse;
   for(uint i = 0; i < cLowLevelReflection.Size(); i++)
      if(!cLowLevelReflection[i].Clear())
         ReturnFalse;
   if(!cHighLevelReflection.Clear())
      ReturnFalse;
   if(!cTools.Clear())
      ReturnFalse;
   if(!cCrossLowLevel.Clear())
      ReturnFalse;
   if(!cMarketToLowLevel.Clear())
      ReturnFalse;
   if(!cMarketToTools.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                               uint window, uint window_key, uint units_count,
                               uint heads, uint stack_size,
                               ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl, window, window_key,
                                           units_count, heads, window, 2 * stack_size, optimization_type, batch))
      ReturnFalse;
//---
   uint index = 0;
   if(!cLSTM.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMamba.Init(0, index, OpenCL, iWindow, 2 * iWindow, iUnits, optimization, iBatch))
      ReturnFalse;
//---
   index++;
   if(!cConcatenated.Init(0, index, OpenCL, 2 * iWindow * iUnits, optimization, iBatch))
      ReturnFalse;
   cConcatenated.SetActivationFunction(None);
   index++;
   if(!cTransposeConc.Init(0, index, OpenCL, iUnits, 2 * iWindow, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cConvolution.Init(0, index, OpenCL, iUnits, iUnits, iWindowKey, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cConvolution.SetActivationFunction(GELU);
   index++;
   uint windows[] = {iWindowKey * iWindow, iWindowKey * iWindow};
   if(!cDistilMemory.Init(0, index, OpenCL, iUnitsKV / 2, iWindow, windows))
      ReturnFalse;
//---
   index++;
   if(!cCrossAttention.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads, iWindow, 2 * iUnits, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cLSTM.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cMamba.FeedForward(NeuronOCL))
      ReturnFalse;
//---
   if(!Concat(cLSTM.getOutput(), cMamba.getOutput(), cConcatenated.getOutput(), iWindow, iWindow, iUnits))
      ReturnFalse;
   if(!cTransposeConc.FeedForward(cConcatenated.AsObject()))
      ReturnFalse;
   if(!cConvolution.FeedForward(cTransposeConc.AsObject()))
      ReturnFalse;
   if(!cDistilMemory.FeedForward(cConvolution.AsObject()))
      ReturnFalse;
//---
   if(!cCrossAttention.FeedForward(NeuronOCL, cConcatenated.getOutput()))
      ReturnFalse;
//---
   return CNeuronRelativeCrossAttention::feedForward(cCrossAttention.AsObject(), cDistilMemory.getOutput());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronRelativeCrossAttention::calcInputGradients(cCrossAttention.AsObject(),
         cDistilMemory.getOutput(),
         cDistilMemory.getGradient(),
         (ENUM_ACTIVATION)cDistilMemory.Activation()))
      ReturnFalse;
//---
   if(!cConvolution.CalcHiddenGradients(cDistilMemory.AsObject()))
      ReturnFalse;
   if(!cTransposeConc.CalcHiddenGradients(cConvolution.AsObject()))
      ReturnFalse;
   if(!cConcatenated.CalcHiddenGradients(cTransposeConc.AsObject()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cCrossAttention.AsObject(),
                                     cConcatenated.getOutput(),
                                     cConcatenated.getPrevOutput(),
                                     (ENUM_ACTIVATION)cConcatenated.Activation()))
      ReturnFalse;
   if(!SumAndNormalize(cConcatenated.getGradient(), cConcatenated.getPrevOutput(),
                       cConcatenated.getGradient(), iWindow, false, 0, 0, 0, 1) ||
      !DeConcat(cLSTM.getGradient(), cMamba.getGradient(), cConcatenated.getGradient(),
                iWindow, iWindow, iUnits))
      ReturnFalse;
//---
   if(cLSTM.Activation() != None)
      if(!DeActivation(cLSTM.getOutput(), cLSTM.getGradient(), cLSTM.getGradient(), cLSTM.Activation()))
         ReturnFalse;
   if(cMamba.Activation() != None)
      if(!DeActivation(cMamba.getOutput(), cMamba.getGradient(), cMamba.getGradient(), cMamba.Activation()))
         ReturnFalse;
//---
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cConcatenated.getPrevOutput(), false) ||
      !NeuronOCL.CalcHiddenGradients(cLSTM.AsObject()) ||
      !SumAndNormalize(temp, NeuronOCL.getGradient(), temp, iWindow, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cMamba.AsObject()) ||
      !SumAndNormalize(temp, NeuronOCL.getGradient(), temp, iWindow, false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronRelativeCrossAttention::updateInputWeights(cCrossAttention.AsObject(), cDistilMemory.getOutput()))
      ReturnFalse;
   if(!cCrossAttention.UpdateInputWeights(NeuronOCL, cConcatenated.getOutput()))
      ReturnFalse;
//---
   if(!cDistilMemory.UpdateInputWeights(cConvolution.AsObject()))
      ReturnFalse;
   if(!cConvolution.UpdateInputWeights(cTransposeConc.AsObject()))
      ReturnFalse;
//---
   if(!cLSTM.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cMamba.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMemoryDistil::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronMemory::SetOpenCL(obj);
   cConcatenated.SetOpenCL(OpenCL);
   cTransposeConc.SetOpenCL(OpenCL);
   cConvolution.SetOpenCL(OpenCL);
   cDistilMemory.SetOpenCL(OpenCL);
   cCrossAttention.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::Clear(void)
  {
   if(!CNeuronMemory::Clear())
      ReturnFalse;
   if(!cConcatenated.Clear())
      ReturnFalse;
   if(!cTransposeConc.Clear())
      ReturnFalse;
   if(!cConvolution.Clear())
      ReturnFalse;
   if(!cDistilMemory.Clear())
      ReturnFalse;
   if(!cCrossAttention.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::Save(const int file_handle)
  {
   if(!CNeuronMemory::Save(file_handle))
      ReturnFalse;
   if(!cConcatenated.Save(file_handle))
      ReturnFalse;
   if(!cTransposeConc.Save(file_handle))
      ReturnFalse;
   if(!cConvolution.Save(file_handle))
      ReturnFalse;
   if(!cDistilMemory.Save(file_handle))
      ReturnFalse;
   if(!cCrossAttention.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMemoryDistil::Load(const int file_handle)
  {
   if(!CNeuronMemory::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cConcatenated.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTransposeConc.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cConvolution.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cDistilMemory.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cCrossAttention.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                              uint window, uint window_key, uint units_count,
                              uint heads, uint stack_size, uint action_space,
                              ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronRelativeCrossAttention::Init(numOutputs, myIndex, open_cl,
                                           3, window_key, action_space / 3,
                                           heads, window, units_count,
                                           optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cStatesMemory.Init(0, index, OpenCL, window, iWindowKey, iUnitsKV,
                          iHeads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cActionsMemory.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits,
                           iHeads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!caRole[0].Init(10 * iWindow, index, OpenCL, 1, optimization, iBatch))
      ReturnFalse;
   CBufferFloat *out = caRole[0].getOutput();
   if(!out ||
      !out.Fill(1))
      ReturnFalse;
   index++;
   if(!caRole[1].Init(0, index, OpenCL, 10 * iWindow, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cStateToRole.Init(0, index, OpenCL, window, iWindowKey, iUnitsKV,
                         iHeads, iWindow, 10, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(bTrain && !caRole[1].FeedForward(caRole[0].AsObject()))
      ReturnFalse;
//---
   if(!cStateToRole.FeedForward(NeuronOCL, caRole[1].getOutput()))
      ReturnFalse;
   if(!cStatesMemory.FeedForward(cStateToRole.AsObject()))
      ReturnFalse;
//---
   if(!cActionsMemory.FeedForward(this.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//---
   return CNeuronRelativeCrossAttention::feedForward(cActionsMemory.AsObject(), cStatesMemory.getOutput());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronRelativeCrossAttention::calcInputGradients(cActionsMemory.AsObject(),
         cStatesMemory.getOutput(),
         cStatesMemory.getGradient(),
         (ENUM_ACTIVATION)cStatesMemory.Activation()))
      ReturnFalse;
//---
   CBufferFloat *temp = Gradient;
   if(!SwapBuffers(Output, PrevOutput) ||
      !SetGradient(cActionsMemory.getPrevOutput(), false))
      ReturnFalse;
   if(!calcHiddenGradients(cActionsMemory.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   Gradient = temp;
//---
   if(!cStateToRole.CalcHiddenGradients(cStatesMemory.AsObject()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cStateToRole.AsObject(),
                                     caRole[1].getOutput(),
                                     caRole[1].getGradient(),
                                     (ENUM_ACTIVATION)caRole[1].Activation()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CNeuronRelativeCrossAttention::updateInputWeights(cActionsMemory.AsObject(),
         cStatesMemory.getOutput()))
      ReturnFalse;
//---
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   if(!cActionsMemory.UpdateInputWeights(this.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//---
   if(!cStatesMemory.UpdateInputWeights(cStateToRole.AsObject()))
      ReturnFalse;
   if(!cStateToRole.UpdateInputWeights(NeuronOCL, caRole[1].getOutput()))
      ReturnFalse;
//---
   if(!caRole[1].UpdateInputWeights(caRole[0].AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFinConAgent::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronRelativeCrossAttention::SetOpenCL(obj);
   cStatesMemory.SetOpenCL(OpenCL);
   cActionsMemory.SetOpenCL(OpenCL);
   caRole[0].SetOpenCL(OpenCL);
   caRole[1].SetOpenCL(OpenCL);
   cStateToRole.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::Clear(void)
  {
   if(!CNeuronRelativeCrossAttention::Clear())
      ReturnFalse;
   if(!cStatesMemory.Clear())
      ReturnFalse;
   if(!cActionsMemory.Clear())
      ReturnFalse;
   if(!caRole[0].Clear())
      ReturnFalse;
   if(!caRole[1].Clear())
      ReturnFalse;
   if(!cStateToRole.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::Save(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Save(file_handle))
      ReturnFalse;
   if(!cStatesMemory.Save(file_handle))
      ReturnFalse;
   if(!cActionsMemory.Save(file_handle))
      ReturnFalse;
   if(!caRole[0].Save(file_handle))
      ReturnFalse;
   if(!caRole[1].Save(file_handle))
      ReturnFalse;
   if(!cStateToRole.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConAgent::Load(const int file_handle)
  {
   if(!CNeuronRelativeCrossAttention::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cStatesMemory.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cActionsMemory.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, caRole[0].AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, caRole[1].AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cStateToRole.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                uint window, uint window_key, uint units_count, uint heads,
                                uint stack_size, uint account_descr, uint action_space,
                                ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronFinConAgent::Init(numOutputs, myIndex, open_cl, action_space, window_key,
                                caAgents.Size() + caTrAgents.Size() + 1,
                                heads, stack_size, action_space, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cTransposeState.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      ReturnFalse;
//---
   for(uint i = 0; i < caAgents.Size(); i++)
     {
      index++;
      if(!caAgents[i].Init(0, index, OpenCL, window, iWindowKey, units_count, iHeads, stack_size, action_space, optimization, iBatch))
         ReturnFalse;
     }
   for(uint i = 0; i < caTrAgents.Size(); i++)
     {
      index++;
      if(!caTrAgents[i].Init(0, index, OpenCL, units_count, iWindowKey, window, iHeads, stack_size, action_space, optimization, iBatch))
         ReturnFalse;
     }
   index++;
   if(!cRiskAgent.Init(0, index, OpenCL, account_descr, iWindowKey, 1, iHeads, stack_size, action_space, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cConcatenatedAgents.Init(0, index, OpenCL, caAgents.Size()*caAgents[0].Neurons() +
                                caTrAgents.Size()*caTrAgents[0].Neurons() +
                                cRiskAgent.Neurons(), optimization, iBatch))
      ReturnFalse;
//---
   index++;
   if(!cAccount.Init(0, index, OpenCL, account_descr, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(cAccount.getOutput() != SecondInput)
     {
      if(!cAccount.SetOutput(SecondInput, true))
         ReturnFalse;
     }
   if(!cTransposeState.FeedForward(NeuronOCL))
      ReturnFalse;
//--- Agents
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!caAgents[i].FeedForward(NeuronOCL))
         ReturnFalse;
   for(uint i = 0; i < caTrAgents.Size(); i++)
      if(!caTrAgents[i].FeedForward(cTransposeState.AsObject()))
         ReturnFalse;
   if(!cRiskAgent.FeedForward(cAccount.AsObject()))
      ReturnFalse;
//--- Concatenate
   if(!Concat(caAgents[0].getOutput(), caAgents[1].getOutput(), caAgents[2].getOutput(), cRiskAgent.getOutput(),
              cConcatenatedAgents.getPrevOutput(), Neurons(), Neurons(), Neurons(), Neurons(), 1) ||
      !Concat(caTrAgents[0].getOutput(), caTrAgents[1].getOutput(), caTrAgents[2].getOutput(), cConcatenatedAgents.getPrevOutput(),
              cConcatenatedAgents.getOutput(), Neurons(), Neurons(), Neurons(), 4 * Neurons(), 1))
      ReturnFalse;
//--- Manager
   return CNeuronFinConAgent::feedForward(cConcatenatedAgents.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::calcInputGradients(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput,
      CBufferFloat* SecondGradient,
      ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient)
      ReturnFalse;
   if(cAccount.getGradient() != SecondGradient)
     {
      if(!cAccount.SetGradient(SecondGradient, true))
         ReturnFalse;
      cAccount.SetActivationFunction(SecondActivation);
     }
//---
   if(!CNeuronFinConAgent::calcInputGradients(cConcatenatedAgents.AsObject()))
      ReturnFalse;
//---
   if(!DeConcat(caTrAgents[0].getGradient(), caTrAgents[1].getGradient(), caTrAgents[2].getGradient(), cConcatenatedAgents.getPrevOutput(),
                cConcatenatedAgents.getGradient(), Neurons(), Neurons(), Neurons(), 4 * Neurons(), 1) ||
      !DeConcat(caAgents[0].getGradient(), caAgents[1].getGradient(), caAgents[2].getGradient(), cRiskAgent.getGradient(),
                cConcatenatedAgents.getPrevOutput(), Neurons(), Neurons(), Neurons(), Neurons(), 1))
      ReturnFalse;
//---
   if(!cAccount.CalcHiddenGradients(cRiskAgent.AsObject()))
      ReturnFalse;
   if(!cTransposeState.CalcHiddenGradients(caTrAgents[0].AsObject()))
      ReturnFalse;
   CBufferFloat *temp = cTransposeState.getGradient();
   if(!cTransposeState.SetGradient(cTransposeState.getPrevOutput(), false))
      ReturnFalse;
   for(uint i = 1; i < caTrAgents.Size(); i++)
     {
      if(!cTransposeState.CalcHiddenGradients(caTrAgents[i].AsObject()))
         ReturnFalse;
      if(!SumAndNormalize(cTransposeState.getGradient(), temp, temp, 1, false, 0, 0, 0, 1))
         ReturnFalse;
     }
   if(!cTransposeState.SetGradient(temp, false))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cTransposeState.AsObject()))
      ReturnFalse;
   temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cTransposeState.getPrevOutput(), false))
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
     {
      if(!NeuronOCL.CalcHiddenGradients(caAgents[i].AsObject()))
         ReturnFalse;
      if(!SumAndNormalize(NeuronOCL.getGradient(), temp, temp, 1, false, 0, 0, 0, 1))
         ReturnFalse;
     }
   if(!NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::updateInputWeights(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput)
  {
//--- Manager
   if(!CNeuronFinConAgent::updateInputWeights(cConcatenatedAgents.AsObject()))
      ReturnFalse;
//--- Agents
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!caAgents[i].UpdateInputWeights(NeuronOCL))
         ReturnFalse;
   for(uint i = 0; i < caTrAgents.Size(); i++)
      if(!caTrAgents[i].UpdateInputWeights(cTransposeState.AsObject()))
         ReturnFalse;
   if(!cRiskAgent.UpdateInputWeights(cAccount.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFinConManager::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronFinConAgent::SetOpenCL(obj);
   cTransposeState.SetOpenCL(OpenCL);
   for(uint i = 0; i < caAgents.Size(); i++)
      caAgents[i].SetOpenCL(OpenCL);
   for(uint i = 0; i < caTrAgents.Size(); i++)
      caTrAgents[i].SetOpenCL(OpenCL);
   cRiskAgent.SetOpenCL(OpenCL);
   cConcatenatedAgents.SetOpenCL(OpenCL);
   cAccount.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFinConManager::TrainMode(bool flag)
  {
   CNeuronFinConAgent::TrainMode(flag);
   cTransposeState.TrainMode(flag);
   for(uint i = 0; i < caAgents.Size(); i++)
      caAgents[i].TrainMode(flag);
   for(uint i = 0; i < caTrAgents.Size(); i++)
      caTrAgents[i].TrainMode(flag);
   cRiskAgent.TrainMode(flag);
   cConcatenatedAgents.TrainMode(flag);
   cAccount.TrainMode(flag);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::Save(const int file_handle)
  {
   if(!CNeuronFinConAgent::Save(file_handle))
      ReturnFalse;
   if(!cTransposeState.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!caAgents[i].Save(file_handle))
         ReturnFalse;
   for(uint i = 0; i < caTrAgents.Size(); i++)
      if(!caTrAgents[i].Save(file_handle))
         ReturnFalse;
   if(!cRiskAgent.Save(file_handle))
      ReturnFalse;
   if(!cConcatenatedAgents.Save(file_handle))
      ReturnFalse;
   if(!cAccount.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::Load(const int file_handle)
  {
   if(!CNeuronFinConAgent::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTransposeState.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!LoadInsideLayer(file_handle, caAgents[i].AsObject()))
         ReturnFalse;
   for(uint i = 0; i < caTrAgents.Size(); i++)
      if(!LoadInsideLayer(file_handle, caTrAgents[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cRiskAgent.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cConcatenatedAgents.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cAccount.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFinConManager::Clear(void)
  {
   if(!CNeuronFinConAgent::Clear())
      ReturnFalse;
   if(!cTransposeState.Clear())
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!caAgents[i].Clear())
         ReturnFalse;
   for(uint i = 0; i < caTrAgents.Size(); i++)
      if(!caTrAgents[i].Clear())
         ReturnFalse;
   if(!cRiskAgent.Clear())
      ReturnFalse;
   if(!cConcatenatedAgents.Clear())
      ReturnFalse;
   if(!cAccount.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                     uint window, uint window_key, uint units_count,
                                     uint heads, uint layers, uint agents, uint stack_size,
                                     ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronSoftMaxOCL::Init(numOutputs, myIndex, open_cl, agents, optimization_type, batch))
      ReturnFalse;
   SetHeads(1);
//---
   int index = 0;
   if(!cMemory.Init(0, 0, OpenCL, window, window_key, units_count, heads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cStatePrepare.Init(0, index, OpenCL, window, window_key, units_count, heads, layers,
                          optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cTranspose.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cScale.Init(4 * agents, index, OpenCL, 3, 1, 1, units_count - 2, window, optimization, iBatch))
      ReturnFalse;
   cScale.SetActivationFunction(TANH);
   index++;
   if(!cMLP[0].Init(agents, index, OpenCL, 4 * agents, optimization, iBatch))
      ReturnFalse;
   cMLP[0].SetActivationFunction(LReLU);
   index++;
   if(!cMLP[1].Init(0, index, OpenCL, agents, optimization, iBatch))
      ReturnFalse;
   cMLP[0].SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cMemory.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cStatePrepare.FeedForward(cMemory.AsObject()))
      ReturnFalse;
   if(!cTranspose.FeedForward(cStatePrepare.AsObject()))
      ReturnFalse;
   if(!cScale.FeedForward(cTranspose.AsObject()))
      ReturnFalse;
   if(!cMLP[0].FeedForward(cScale.AsObject()))
      ReturnFalse;
   if(!cMLP[1].FeedForward(cMLP[0].AsObject()))
      ReturnFalse;
//---
   return CNeuronSoftMaxOCL::feedForward(cMLP[1].AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!CNeuronSoftMaxOCL::calcInputGradients(cMLP[1].AsObject()))
      ReturnFalse;
   if(!cMLP[0].CalcHiddenGradients(cMLP[1].AsObject()))
      ReturnFalse;
   if(!cScale.CalcHiddenGradients(cMLP[0].AsObject()))
      ReturnFalse;
   if(!cTranspose.CalcHiddenGradients(cScale.AsObject()))
      ReturnFalse;
   if(!cStatePrepare.CalcHiddenGradients(cTranspose.AsObject()))
      ReturnFalse;
   if(!cMemory.CalcHiddenGradients(cStatePrepare.AsObject()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cMemory.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cMLP[1].UpdateInputWeights(cMLP[0].AsObject()))
      ReturnFalse;
   if(!cMLP[0].UpdateInputWeights(cScale.AsObject()))
      ReturnFalse;
   if(!cScale.UpdateInputWeights(cTranspose.AsObject()))
      ReturnFalse;
   if(!cStatePrepare.UpdateInputWeights(cMemory.AsObject()))
      ReturnFalse;
   if(!cMemory.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::Save(const int file_handle)
  {
   if(!CNeuronSoftMaxOCL::Save(file_handle))
      ReturnFalse;
   if(!cStatePrepare.Save(file_handle))
      ReturnFalse;
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   if(!cScale.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cMLP.Size(); i++)
      if(!cMLP[i].Save(file_handle))
         ReturnFalse;
   if(!cMemory.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::Load(const int file_handle)
  {
   if(!CNeuronSoftMaxOCL::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cStatePrepare.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cScale.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < cMLP.Size(); i++)
      if(!LoadInsideLayer(file_handle, cMLP[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMemory.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMacroHFTHyperAgent::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronSoftMaxOCL::SetOpenCL(obj);
   cStatePrepare.SetOpenCL(obj);
   cTranspose.SetOpenCL(obj);
   cScale.SetOpenCL(obj);
   for(uint i = 0; i < cMLP.Size(); i++)
      cMLP[i].SetOpenCL(obj);
   cMemory.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTHyperAgent::Clear(void)
  {
   if(!CNeuronSoftMaxOCL::Clear())
      ReturnFalse;
   if(!cStatePrepare.Clear())
      ReturnFalse;
   if(!cTranspose.Clear())
      ReturnFalse;
   if(!cScale.Clear())
      ReturnFalse;
   for(uint i = 0; i < cMLP.Size(); i++)
      if(!cMLP[i].Clear())
         ReturnFalse;
   if(!cMemory.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                           uint window, uint window_key, uint units_count,
                           uint heads, uint layers, uint stack_size, uint nactions,
                           ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, nactions, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cTranspose.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      ReturnFalse;
   uint half = (caAgents.Size() + 1) / 2;
   for(uint i = 0; i < half; i++)
     {
      index++;
      if(!caAgents[i].Init(0, index, OpenCL, window, window_key, units_count, heads,
                           stack_size, nactions, optimization, iBatch))
         ReturnFalse;
     }
   for(uint i = half; i < caAgents.Size(); i++)
     {
      index++;
      if(!caAgents[i].Init(0, index, OpenCL, units_count, window_key, window, heads,
                           stack_size, nactions, optimization, iBatch))
         ReturnFalse;
     }
   index++;
   if(!cHyperAgent.Init(0, index, OpenCL, window, window_key, units_count, heads, layers,
                        caAgents.Size(), stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cConcatenated.Init(0, index, OpenCL, caAgents.Size()*nactions, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cTranspose.FeedForward(NeuronOCL))
      ReturnFalse;
//---
   uint total = caAgents.Size();
   uint half = (total + 1) / 2;
   for(uint i = 0; i < half; i++)
      if(!caAgents[i].FeedForward(NeuronOCL))
         ReturnFalse;
   for(uint i = half; i < total; i++)
      if(!caAgents[i].FeedForward(cTranspose.AsObject()))
         ReturnFalse;
   if(!cHyperAgent.FeedForward(NeuronOCL))
      ReturnFalse;
//---
   if(!Concat(caAgents[0].getOutput(), caAgents[1].getOutput(), caAgents[2].getOutput(),
              caAgents[3].getOutput(), cConcatenated.getPrevOutput(), Neurons(), Neurons(),
              Neurons(), Neurons(), 1) ||
      !Concat(cConcatenated.getPrevOutput(), caAgents[4].getOutput(), caAgents[5].getOutput(),
              cConcatenated.getOutput(), 4 * Neurons(), Neurons(), Neurons(), 1))
      ReturnFalse;
   if(!MatMul(cHyperAgent.getOutput(), cConcatenated.getOutput(), Output, 1, total, Neurons(), 1))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   uint total = caAgents.Size();
   if(!MatMulGrad(cHyperAgent.getOutput(), cHyperAgent.getGradient(), cConcatenated.getOutput(), cConcatenated.getGradient(), Gradient, 1, total, Neurons(), 1))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cHyperAgent.AsObject()))
      ReturnFalse;
//---
   if(!DeConcat(cConcatenated.getPrevOutput(), caAgents[4].getGradient(), caAgents[5].getGradient(),
                cConcatenated.getGradient(), 4 * Neurons(), Neurons(), Neurons(), 1) ||
      !DeConcat(caAgents[0].getGradient(), caAgents[1].getGradient(), caAgents[2].getGradient(),
                caAgents[3].getGradient(), cConcatenated.getPrevOutput(), Neurons(), Neurons(),
                Neurons(), Neurons(), 1))
      ReturnFalse;
//---
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!temp ||
      !NeuronOCL.SetGradient(cTranspose.getPrevOutput(), false))
      ReturnFalse;
   uint half = (total + 1) / 2;
   for(uint i = 0; i < half; i++)
     {
      if(!NeuronOCL.CalcHiddenGradients(caAgents[i].AsObject()))
         ReturnFalse;
      if(!SumAndNormalize(temp, NeuronOCL.getGradient(), temp, 1, false, 0, 0, 0, 1))
         ReturnFalse;
     }
   for(uint i = half; i < total; i++)
     {
      if(!cTranspose.CalcHiddenGradients(caAgents[i].AsObject()) ||
         !NeuronOCL.CalcHiddenGradients(cTranspose.AsObject()))
         ReturnFalse;
      if(!SumAndNormalize(temp, NeuronOCL.getGradient(), temp, 1, false, 0, 0, 0, 1))
         ReturnFalse;
     }
   if(!NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cHyperAgent.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   uint total = caAgents.Size();
   uint half = (total + 1) / 2;
   for(uint i = 0; i < half; i++)
      if(!caAgents[i].UpdateInputWeights(NeuronOCL))
         ReturnFalse;
   for(uint i = half; i < total; i++)
      if(!caAgents[i].UpdateInputWeights(cTranspose.AsObject()))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!caAgents[i].Save(file_handle))
         ReturnFalse;
   if(!cHyperAgent.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!LoadInsideLayer(file_handle, caAgents[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHyperAgent.AsObject()))
      ReturnFalse;
//---
   if(!cConcatenated.Init(0, 8, OpenCL, caAgents.Size()*Neurons(), optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMacroHFT::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
//---
   cTranspose.SetOpenCL(obj);
   for(uint i = 0; i < caAgents.Size(); i++)
      caAgents[i].SetOpenCL(obj);
   cHyperAgent.SetOpenCL(obj);
   cConcatenated.SetOpenCL(obj);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFT::Clear(void)
  {
   if(!CNeuronBaseOCL::Clear())
      ReturnFalse;
//---
   if(!cTranspose.Clear())
      ReturnFalse;
   for(uint i = 0; i < caAgents.Size(); i++)
      if(!caAgents[i].Clear())
         ReturnFalse;
   if(!cHyperAgent.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                        uint window, uint window_key, uint units_count, uint heads,
                                        uint stack_size, uint nactions, uint account_decr,
                                        ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CResidualConv::Init(numOutputs, myIndex, open_cl, 3, 3, (nactions + 2) / 3, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!caAccountProjection[0].Init(0, index, OpenCL, account_decr, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!caAccountProjection[1].Init(0, index, OpenCL, window * units_count, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMemoryAccount.Init(caAccountProjection[1].Neurons(), index, OpenCL, account_decr,
                           window_key, 1, heads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cMemoryAction.Init(0, index, OpenCL, 3, window_key, (nactions + 2) / 3,
                          heads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cCrossAttention.Init(0, index, OpenCL, 3, window_key, (nactions + 2) / 3,
                            heads, window, units_count, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(caAccountProjection[0].getOutput() != SecondInput)
     {
      if(!caAccountProjection[0].SetOutput(SecondInput, true))
         ReturnFalse;
     }
//---
   if(!cMemoryAccount.FeedForward(caAccountProjection[0].AsObject()))
      ReturnFalse;
   if(!caAccountProjection[1].FeedForward(cMemoryAccount.AsObject()))
      ReturnFalse;
   if(!cMemoryAction.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cCrossAttention.FeedForward(cMemoryAction.AsObject(), caAccountProjection[1].getOutput()))
      ReturnFalse;
//---
   return CResidualConv::feedForward(cCrossAttention.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::calcInputGradients(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput,
      CBufferFloat* SecondGradient,
      ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient)
      ReturnFalse;
   if(caAccountProjection[0].getGradient() != SecondGradient)
     {
      if(!caAccountProjection[0].SetGradient(SecondGradient, true))
         ReturnFalse;
      caAccountProjection[0].SetActivationFunction(SecondActivation);
     }
//---
   if(!CResidualConv::calcInputGradients(cCrossAttention.AsObject()))
      ReturnFalse;
   if(!cMemoryAction.CalcHiddenGradients(cCrossAttention.AsObject(),
                                         caAccountProjection[1].getOutput(),
                                         caAccountProjection[1].getGradient(),
                                         (ENUM_ACTIVATION)caAccountProjection[1].Activation()))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cMemoryAction.AsObject()))
      ReturnFalse;
//---
   if(!cMemoryAccount.CalcHiddenGradients(caAccountProjection[1].AsObject()))
      ReturnFalse;
   if(!caAccountProjection[0].CalcHiddenGradients(cMemoryAccount.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::updateInputWeights(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput)
  {
   if(!CResidualConv::updateInputWeights(cCrossAttention.AsObject()))
      ReturnFalse;
   if(!cCrossAttention.UpdateInputWeights(cMemoryAction.AsObject(), caAccountProjection[1].getOutput()))
      ReturnFalse;
//---
   if(!cMemoryAction.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   if(!caAccountProjection[1].UpdateInputWeights(cMemoryAccount.AsObject()))
      ReturnFalse;
   if(!cMemoryAccount.UpdateInputWeights(caAccountProjection[0].AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::Save(const int file_handle)
  {
   if(!CResidualConv::Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < caAccountProjection.Size(); i++)
      if(!caAccountProjection[i].Save(file_handle))
         ReturnFalse;
   if(!cMemoryAccount.Save(file_handle))
      ReturnFalse;
   if(!cMemoryAction.Save(file_handle))
      ReturnFalse;
   if(!cCrossAttention.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::Load(const int file_handle)
  {
   if(!CResidualConv::Load(file_handle))
      ReturnFalse;
   for(uint i = 0; i < caAccountProjection.Size(); i++)
      if(!LoadInsideLayer(file_handle, caAccountProjection[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMemoryAccount.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cMemoryAction.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cCrossAttention.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronMacroHFTvsRiskManager::SetOpenCL(COpenCLMy * obj)
  {
   CResidualConv::SetOpenCL(obj);
   for(uint i = 0; i < caAccountProjection.Size(); i++)
      caAccountProjection[i].SetOpenCL(OpenCL);
   cMemoryAccount.SetOpenCL(OpenCL);
   cMemoryAction.SetOpenCL(OpenCL);
   cCrossAttention.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronMacroHFTvsRiskManager::Clear(void)
  {
   if(!CResidualConv::Clear())
      ReturnFalse;
   for(uint i = 0; i < caAccountProjection.Size(); i++)
      if(!caAccountProjection[i].Clear())
         ReturnFalse;
   if(!cMemoryAccount.Clear())
      ReturnFalse;
   if(!cMemoryAction.Clear())
      ReturnFalse;
   if(!cCrossAttention.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                   uint window, uint window_key, uint units_count,
                                   uint heads, uint stack_size, uint action_space,
                                   ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CResidualConv::Init(numOutputs, myIndex, open_cl, 3, 3, (action_space + 2) / 3, optimization_type, batch))
      ReturnFalse;
//--- Role
   int index = 0;
   if(!caRole[0].Init(10 * window_key, index, OpenCL, 1, optimization, iBatch))
      ReturnFalse;
   caRole[0].getOutput().Fill(1);
   index++;
   if(!caRole[1].Init(0, index, OpenCL, 10 * window_key, optimization, iBatch))
      ReturnFalse;
//--- State to Role
   index++;
   if(!cStateToRole.Init(0, index, OpenCL, window, window_key, units_count, heads, window_key, 10, optimization, iBatch))
      ReturnFalse;
//--- State
   index++;
   if(!cShuffle.Init(0, index, OpenCL, window, window * units_count, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cRecursiveState.Init(0, index, OpenCL, window, window_key, units_count, heads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cResidualState.Init(0, index, OpenCL, window, window, units_count, optimization, iBatch))
      ReturnFalse;
//--- Action
   index++;
   if(!cRecursiveAction.Init(0, index, OpenCL, 3, window_key, (action_space + 2) / 3, heads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cActionToState.Init(0, index, OpenCL, 3, window_key, (action_space + 2) / 3, heads, window, units_count, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(bTrain && !caRole[1].FeedForward(caRole[0].AsObject()))
      ReturnFalse;
//--- State to Role
   if(!cStateToRole.FeedForward(NeuronOCL, caRole[1].getOutput()))
      ReturnFalse;
//--- State
   if(!cShuffle.FeedForward(cStateToRole.AsObject()))
      ReturnFalse;
   if(!cRecursiveState.FeedForward(cShuffle.AsObject()))
      ReturnFalse;
   if(!cResidualState.FeedForward(cRecursiveState.AsObject()))
      ReturnFalse;
//--- Action
   if(!cRecursiveAction.FeedForward(AsObject()))
      ReturnFalse;
   if(!cActionToState.FeedForward(cRecursiveAction.AsObject(), cResidualState.getOutput()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//---
   return CResidualConv::feedForward(cActionToState.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CResidualConv::calcInputGradients(cActionToState.AsObject()))
      ReturnFalse;
   if(!cRecursiveAction.CalcHiddenGradients(cActionToState.AsObject(),
         cResidualState.getOutput(),
         cResidualState.getGradient(),
         (ENUM_ACTIVATION)cResidualState.Activation()))
      ReturnFalse;
//--- Action
   CBufferFloat *temp = Gradient;
   if(!SwapBuffers(Output, PrevOutput) ||
      !SetGradient(cRecursiveAction.getPrevOutput(), false))
      ReturnFalse;
   if(!calcHiddenGradients(cRecursiveAction.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   Gradient = temp;
//--- State
   if(!cRecursiveState.CalcHiddenGradients(cResidualState.AsObject()))
      ReturnFalse;
   if(!cShuffle.CalcHiddenGradients(cRecursiveState.AsObject()))
      ReturnFalse;
   if(!cStateToRole.CalcHiddenGradients(cShuffle.AsObject()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cStateToRole.AsObject(),
                                     caRole[1].getOutput(),
                                     caRole[1].getGradient(),
                                     (ENUM_ACTIVATION)caRole[1].Activation()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!CResidualConv::updateInputWeights(cActionToState.AsObject()))
      ReturnFalse;
//--- Action
   if(!cActionToState.UpdateInputWeights(cRecursiveAction.AsObject()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput) ||
      !cRecursiveAction.UpdateInputWeights(AsObject()) ||
      !SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//--- State
   if(!cResidualState.UpdateInputWeights(cRecursiveState.AsObject()))
      ReturnFalse;
   if(!cResidualState.UpdateInputWeights(cShuffle.AsObject()))
      ReturnFalse;
   if(!cShuffle.UpdateInputWeights(cStateToRole.AsObject()))
      ReturnFalse;
   if(!cStateToRole.UpdateInputWeights(NeuronOCL, caRole[1].getOutput()))
      ReturnFalse;
//--- Role
   if(!caRole[1].UpdateInputWeights(caRole[0].AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::Save(const int file_handle)
  {
   if(!CResidualConv::Save(file_handle))
      ReturnFalse;
//---
   for(uint i = 0; i < caRole.Size(); i++)
      if(!caRole[i].Save(file_handle))
         ReturnFalse;
   if(!cStateToRole.Save(file_handle))
      ReturnFalse;
   if(!cShuffle.Save(file_handle))
      ReturnFalse;
   if(!cRecursiveState.Save(file_handle))
      ReturnFalse;
   if(!cResidualState.Save(file_handle))
      ReturnFalse;
   if(!cRecursiveAction.Save(file_handle))
      ReturnFalse;
   if(!cActionToState.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::Load(const int file_handle)
  {
   if(!CResidualConv::Load(file_handle))
      ReturnFalse;
//---
   for(uint i = 0; i < caRole.Size(); i++)
      if(!LoadInsideLayer(file_handle, caRole[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cStateToRole.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cShuffle.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cRecursiveState.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cResidualState.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cRecursiveAction.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cActionToState.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHidformerTSAgent::SetOpenCL(COpenCLMy * obj)
  {
   CResidualConv::SetOpenCL(obj);
//---
   for(uint i = 0; i < caRole.Size(); i++)
      caRole[i].SetOpenCL(OpenCL);
   cStateToRole.SetOpenCL(OpenCL);
   cShuffle.SetOpenCL(OpenCL);
   cRecursiveState.SetOpenCL(OpenCL);
   cResidualState.SetOpenCL(OpenCL);
   cRecursiveAction.SetOpenCL(OpenCL);
   cActionToState.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerTSAgent::Clear(void)
  {
   if(!CResidualConv::Clear())
      ReturnFalse;
//---
   for(uint i = 0; i < caRole.Size(); i++)
      if(!caRole[i].Clear())
         ReturnFalse;
   if(!cStateToRole.Clear())
      ReturnFalse;
   if(!cShuffle.Clear())
      ReturnFalse;
   if(!cRecursiveState.Clear())
      ReturnFalse;
   if(!cResidualState.Clear())
      ReturnFalse;
   if(!cRecursiveAction.Clear())
      ReturnFalse;
   if(!cActionToState.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
                                     uint window, uint filters, uint units_count,
                                     uint heads, uint stack_size, uint action_space,
                                     ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CResidualConv::Init(numOutputs, myIndex, open_cl, 3, 3, (action_space + 2) / 3, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cTranspose.Init(0, index, OpenCL, units_count, window, optimization, iBatch))
      ReturnFalse;
   index++;
   uint wind = (units_count >= 20 ? (units_count + 3) / 4 : units_count);
   uint units = (units_count + wind - 1) / wind;
   if(!cLegendre.Init(0, index, OpenCL, wind, wind, units, filters, window, optimization, batch))
      ReturnFalse;
   index++;
   if(!cHLState.Init(0, index, OpenCL, units * window, 2, filters, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cAttentionState.Init(0, index, OpenCL, filters, filters, units * window, 2, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cResidualState.Init(0, index, OpenCL, filters, filters, 2 * units * window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cShuffle.Init(0, index, OpenCL, filters, cResidualState.Neurons(), optimization, iBatch))
      ReturnFalse;
//--- Action
   index++;
   if(!cRecursiveAction.Init(0, index, OpenCL, 3, filters, (action_space + 2) / 3, heads, stack_size, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cActionToState.Init(0, index, OpenCL, 3, filters, (action_space + 2) / 3, heads, filters, 2 * units * window, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
//--- State
   if(!cTranspose.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cLegendre.FeedForward(cTranspose.AsObject()))
      ReturnFalse;
   if(!cHLState.FeedForward(cLegendre.AsObject()))
      ReturnFalse;
   if(!cAttentionState.FeedForward(cHLState.AsObject()))
      ReturnFalse;
   if(!cResidualState.FeedForward(cAttentionState.AsObject()))
      ReturnFalse;
   if(!cShuffle.FeedForward(cResidualState.AsObject()))
      ReturnFalse;
//--- Action
   if(!cRecursiveAction.FeedForward(AsObject()))
      ReturnFalse;
   if(!cActionToState.FeedForward(cRecursiveAction.AsObject(), cShuffle.getOutput()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   return CResidualConv::feedForward(cActionToState.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::calcInputGradients(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//--- Action
   if(!CResidualConv::calcInputGradients(cActionToState.AsObject()))
      ReturnFalse;
   if(!cRecursiveAction.CalcHiddenGradients(cActionToState.AsObject(),
         cShuffle.getOutput(),
         cShuffle.getGradient(),
         (ENUM_ACTIVATION)cShuffle.Activation()))
      ReturnFalse;
   CBufferFloat *temp = Gradient;
   if(!SwapBuffers(Output, PrevOutput) ||
      !SetGradient(cActionToState.getPrevOutput(), false) ||
      !calcHiddenGradients(cRecursiveAction.AsObject()) ||
      !SwapBuffers(Output, PrevOutput))
      ReturnFalse;
   Gradient = temp;
//--- State
   if(!cResidualState.CalcHiddenGradients(cShuffle.AsObject()))
      ReturnFalse;
   if(!cAttentionState.CalcHiddenGradients(cResidualState.AsObject()))
      ReturnFalse;
   if(!cHLState.CalcHiddenGradients(cAttentionState.AsObject()))
      ReturnFalse;
   if(!cLegendre.CalcHiddenGradients(cHLState.AsObject()))
      ReturnFalse;
   if(!cTranspose.CalcHiddenGradients(cLegendre.AsObject()))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cTranspose.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
//--- Action
   if(!CResidualConv::updateInputWeights(cActionToState.AsObject()))
      ReturnFalse;
   if(!cActionToState.UpdateInputWeights(cRecursiveAction.AsObject(),
                                         cShuffle.getOutput()))
      ReturnFalse;
   if(!SwapBuffers(Output, PrevOutput) ||
      !cRecursiveAction.UpdateInputWeights(AsObject()) ||
      !SwapBuffers(Output, PrevOutput))
      ReturnFalse;
//--- State
   if(!cShuffle.UpdateInputWeights(cResidualState.AsObject()))
      ReturnFalse;
   if(!cResidualState.UpdateInputWeights(cAttentionState.AsObject()))
      ReturnFalse;
   if(!cAttentionState.UpdateInputWeights(cHLState.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::Save(const int file_handle)
  {
   if(!CResidualConv::Save(file_handle))
      ReturnFalse;
//---
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   if(!cLegendre.Save(file_handle))
      ReturnFalse;
   if(!cHLState.Save(file_handle))
      ReturnFalse;
   if(!cAttentionState.Save(file_handle))
      ReturnFalse;
   if(!cResidualState.Save(file_handle))
      ReturnFalse;
   if(!cShuffle.Save(file_handle))
      ReturnFalse;
//--- Action
   if(!cRecursiveAction.Save(file_handle))
      ReturnFalse;
   if(!cActionToState.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::Load(const int file_handle)
  {
   if(!CResidualConv::Load(file_handle))
      ReturnFalse;
//--- State
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cLegendre.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHLState.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cAttentionState.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cResidualState.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cShuffle.AsObject()))
      ReturnFalse;
//--- Action
   if(!LoadInsideLayer(file_handle, cRecursiveAction.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cActionToState.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHidformerFreqAgent::Clear(void)
  {
   if(!CResidualConv::Clear())
      ReturnFalse;
//---
   if(!cTranspose.Clear())
      ReturnFalse;
   if(!cLegendre.Clear())
      ReturnFalse;
   if(!cHLState.Clear())
      ReturnFalse;
   if(!cAttentionState.Clear())
      ReturnFalse;
   if(!cResidualState.Clear())
      ReturnFalse;
   if(!cShuffle.Clear())
      ReturnFalse;
//--- Action
   if(!cRecursiveAction.Clear())
      ReturnFalse;
   if(!cActionToState.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHidformerFreqAgent::SetOpenCL(COpenCLMy * obj)
  {
   CResidualConv::SetOpenCL(obj);
//---
   cTranspose.SetOpenCL(OpenCL);
   cLegendre.SetOpenCL(OpenCL);
   cHLState.SetOpenCL(OpenCL);
   cAttentionState.SetOpenCL(OpenCL);
   cResidualState.SetOpenCL(OpenCL);
   cShuffle.SetOpenCL(OpenCL);
//--- Action
   cRecursiveAction.SetOpenCL(OpenCL);
   cActionToState.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl,
      uint time_step, uint variables, uint task_skills,
      uint common_skills, uint n_actions, uint window,
      uint step, uint window_key, uint heads,
      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronConvOCL::Init(numOutputs, myIndex, open_cl, window_key, window_key,
                            n_actions, 1, variables, optimization_type, batch))
      ReturnFalse;
   SetActivationFunction(SIGMOID);
//---
   int index = 0;
   if(!cTaskSpecificSkillsEncoder.Init(0, index, OpenCL, time_step, variables, task_skills,
                                       window, step, window_key, heads, optimization, iBatch))
      ReturnFalse;
   cTaskSpecificSkillsEncoder.SetActivationFunction(None);
//---
   iTaskSkills = task_skills;
   iCommonSkills = MathMax(common_skills, 1);
//---
   index++;
   if(!cTranspose.Init(0, index, OpenCL, time_step, variables, optimization, iBatch))
      ReturnFalse;
//---
   uint window_size = (time_step + iTaskSkills + iCommonSkills);
   index++;
   if(!cObservAndSkillsConcat.Init(0, index, OpenCL, window_size * iVariables,
                                   optimization, iBatch))
      ReturnFalse;
   cObservAndSkillsConcat.SetActivationFunction(None);
   index++;
   if(!cNormalizarion.Init(0, index, OpenCL, cObservAndSkillsConcat.Neurons(),
                           iBatch, optimization))
      ReturnFalse;
   cNormalizarion.SetActivationFunction(None);
   for(uint i = 0; i < cActionDecoder.Size(); i++)
     {
      index++;
      if(!cActionDecoder[i].Init(0, index, OpenCL, window_size, window_size, window_key,
                                 1, iVariables, optimization, iBatch))
         ReturnFalse;
      cActionDecoder[i].SetActivationFunction(SoftPlus);
      window_size = window_key;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::feedForward(CNeuronBaseOCL* NeuronOCL, CBufferFloat* SecondInput)
  {
   if(!SecondInput)
      ReturnFalse;
   if(!cTaskSpecificSkillsEncoder.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cTranspose.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!Concat(cTranspose.getOutput(), cTaskSpecificSkillsEncoder.getOutput(), SecondInput,
              cObservAndSkillsConcat.getOutput(), cTranspose.GetCount(),
              iTaskSkills, iCommonSkills, iVariables))
      ReturnFalse;
   if(!cNormalizarion.FeedForward(cObservAndSkillsConcat.AsObject()))
      ReturnFalse;
   CNeuronBaseOCL *neuron = cNormalizarion.AsObject();
   for(uint i = 0; i < cActionDecoder.Size(); i++)
     {
      if(!cActionDecoder[i].FeedForward(neuron))
         ReturnFalse;
      neuron = cActionDecoder[i].AsObject();
     }
//---
   return CNeuronConvOCL::feedForward(neuron);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::calcInputGradients(CNeuronBaseOCL* NeuronOCL,
      CBufferFloat* SecondInput,
      CBufferFloat* SecondGradient,
      ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondGradient)
      ReturnFalse;
//---
   uint total = cActionDecoder.Size();
   if(total <= 0)
      ReturnFalse;
   CObject *neuron = cActionDecoder[total - 1].AsObject();
//---
   if(!CNeuronConvOCL::calcInputGradients(neuron))
      ReturnFalse;
   for(int i = int(total - 2); i >= 0; i--)
     {
      if(!cActionDecoder[i].CalcHiddenGradients(neuron))
         ReturnFalse;
      neuron = cActionDecoder[i].AsObject();
     }
   if(!cNormalizarion.CalcHiddenGradients(neuron))
      ReturnFalse;
   if(!cObservAndSkillsConcat.CalcHiddenGradients(cNormalizarion.AsObject()))
      ReturnFalse;
   if(!DeConcat(cTranspose.getGradient(), cTaskSpecificSkillsEncoder.getGradient(), SecondGradient,
                cObservAndSkillsConcat.getGradient(), cTranspose.GetCount(),
                iTaskSkills, iCommonSkills, iVariables))
      ReturnFalse;
//---
   if(SecondActivation != None)
     {
      if(!DeActivation(SecondInput, SecondGradient, SecondGradient, SecondActivation))
         ReturnFalse;
     }
   if(NeuronOCL.Activation() != None)
     {
      if(!DeActivation(cTranspose.getOutput(), cTranspose.getGradient(),
                       cTranspose.getGradient(), NeuronOCL.Activation()))
         ReturnFalse;
     }
   if(cTaskSpecificSkillsEncoder.Activation() != None)
     {
      if(!DeActivation(cTaskSpecificSkillsEncoder.getOutput(), cTaskSpecificSkillsEncoder.getGradient(),
                       cTaskSpecificSkillsEncoder.getGradient(), cTaskSpecificSkillsEncoder.Activation()))
         ReturnFalse;
     }
//---
   if(!NeuronOCL.CalcHiddenGradients(cTaskSpecificSkillsEncoder.AsObject()))
      ReturnFalse;
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(cTranspose.getPrevOutput(), false) ||
      !NeuronOCL.CalcHiddenGradients(cTranspose.AsObject()) ||
      !SumAndNormalize(temp, NeuronOCL.getGradient(), temp, iVariables, false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::updateInputWeights(CNeuronBaseOCL* NeuronOCL, CBufferFloat* second)
  {
   if(!cTaskSpecificSkillsEncoder.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cNormalizarion.UpdateInputWeights(cObservAndSkillsConcat.AsObject()))
      ReturnFalse;
   CNeuronBaseOCL *neuron = cNormalizarion.AsObject();
   for(uint i = 0; i < cActionDecoder.Size(); i++)
     {
      if(!cActionDecoder[i].UpdateInputWeights(neuron))
         ReturnFalse;
      neuron = cActionDecoder[i].AsObject();
     }
//---
   return CNeuronConvOCL::updateInputWeights(neuron);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::WeightsUpdate(CNeuronBaseOCL* source, float tau)
  {
   if(!source || source.Type() != Type())
      ReturnFalse;
   CNeuronHiSSDLowLevelControler *Source = source;
   if(!cTaskSpecificSkillsEncoder.WeightsUpdate(GetPointer(Source.cTaskSpecificSkillsEncoder), tau))
      ReturnFalse;
   if(!cNormalizarion.WeightsUpdate(GetPointer(Source.cNormalizarion), tau))
      ReturnFalse;
   for(uint i = 0; i < cActionDecoder.Size(); i++)
      if(!cActionDecoder[i].WeightsUpdate(GetPointer(Source.cActionDecoder[i]), tau))
         ReturnFalse;
//---
   return CNeuronConvOCL::WeightsUpdate(source, tau);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::Save(const int file_handle)
  {
   if(!CNeuronConvOCL::Save(file_handle))
      ReturnFalse;
//---
   if(FileWriteInteger(file_handle, int(iTaskSkills)) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, int(iCommonSkills)) < INT_VALUE)
      ReturnFalse;
//---
   if(!cTaskSpecificSkillsEncoder.Save(file_handle))
      ReturnFalse;
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   if(!cNormalizarion.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < cActionDecoder.Size(); i++)
      if(!cActionDecoder[i].Save(file_handle))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHiSSDLowLevelControler::Load(const int file_handle)
  {
   if(!CNeuronConvOCL::Load(file_handle))
      ReturnFalse;
//---
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iTaskSkills = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iCommonSkills = (uint)FileReadInteger(file_handle);
//---
   if(!LoadInsideLayer(file_handle, cTaskSpecificSkillsEncoder.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cNormalizarion.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < cActionDecoder.Size(); i++)
      if(!LoadInsideLayer(file_handle, cActionDecoder[i].AsObject()))
         ReturnFalse;
//---
   if(!cObservAndSkillsConcat.Init(0, 2, OpenCL, cNormalizarion.Neurons(),
                                   optimization, iBatch))
      ReturnFalse;
   cObservAndSkillsConcat.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHiSSDLowLevelControler::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronConvOCL::SetOpenCL(obj);
//---
   cTaskSpecificSkillsEncoder.SetOpenCL(OpenCL);
   cTranspose.SetOpenCL(OpenCL);
   cObservAndSkillsConcat.SetOpenCL(OpenCL);
   cNormalizarion.SetOpenCL(OpenCL);
   for(uint i = 0; i < cActionDecoder.Size(); i++)
      cActionDecoder[i].SetOpenCL(OpenCL);
  }
