#ifndef NEURONET_BUILDING_FACADE
#include "NeuroNet.mqh"
#endif // NEURONET_BUILDING_FACADE
//+------------------------------------------------------------------+
//| Logical method implementations generated from NeuroNet.mqh: GraphSparse
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::Init(uint numOutputs, uint myIndex, COpenCLMy * open_cl, uint window, uint numNeurons, ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, numNeurons, optimization_type, batch))
      ReturnFalse;
   activation = None;
//---
   if(!cInputF.Init(numNeurons, 0, OpenCL, window, optimization, batch))
      ReturnFalse;
   if(!cInputS.Init(numNeurons, 1, OpenCL, window, optimization, batch))
      ReturnFalse;
   cInputF.SetActivationFunction(None);
   cInputS.SetActivationFunction(None);
//---
   if(!cF.Init(0, 2, OpenCL, numNeurons, optimization, batch))
      ReturnFalse;
   cF.SetActivationFunction(SIGMOID);
   if(!cS.Init(0, 3, OpenCL, numNeurons, optimization, batch))
      ReturnFalse;
   cS.SetActivationFunction(LReLU);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::feedForward(CNeuronBaseOCL* NeuronOCL)
  {
   if(!NeuronOCL || !NeuronOCL.getOutput() || NeuronOCL.getOutputIndex() < 0)
      ReturnFalse;
//---
   if(cInputF.getOutputIndex() != NeuronOCL.getOutputIndex())
     {
      if(!cInputF.getOutput().BufferSet(NeuronOCL.getOutputIndex()))
         ReturnFalse;
      cInputF.SetActivationFunction((ENUM_ACTIVATION)NeuronOCL.Activation());
     }
   if(cInputS.getOutputIndex() != NeuronOCL.getOutputIndex())
     {
      if(!cInputS.getOutput().BufferSet(NeuronOCL.getOutputIndex()))
         ReturnFalse;
      cInputS.SetActivationFunction((ENUM_ACTIVATION)NeuronOCL.Activation());
     }
//---
   if(!cF.FeedForward(GetPointer(cInputF)))
      ReturnFalse;
   if(!cS.FeedForward(GetPointer(cInputS)))
      ReturnFalse;
//---
   if(!ElementMult(cF.getOutput(), cS.getOutput(), Output))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::calcInputGradients(CNeuronBaseOCL* prevLayer)
  {
   if(!prevLayer || !prevLayer.getGradient() || prevLayer.getGradientIndex() < 0)
      ReturnFalse;
//---
   if(!ElementMultGrad(cF.getOutput(), cF.getGradient(), cS.getOutput(), cS.getGradient(), Gradient, cF.Activation(), cS.Activation()))
      ReturnFalse;
//---
   if(!cInputF.CalcHiddenGradients((CObject *)GetPointer(cF)))
      ReturnFalse;
   if(!cInputS.CalcHiddenGradients((CObject *)GetPointer(cS)))
      ReturnFalse;
   if(!SumAndNormalize(cF.getOutput(), cS.getOutput(), prevLayer.getOutput(), 1, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::updateInputWeights(CNeuronBaseOCL* NeuronOCL)
  {
   if(!cF.UpdateInputWeights(cInputF.AsObject()))
      ReturnFalse;
   if(!cS.UpdateInputWeights(cInputS.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(!cInputF.Save(file_handle))
      ReturnFalse;
   if(!cInputS.Save(file_handle))
      ReturnFalse;
   if(!cF.Save(file_handle))
      ReturnFalse;
   if(!cS.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cInputF.Type() || !cInputF.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cInputS.Type() || !cInputS.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cF.Type() || !cF.Load(file_handle))
      ReturnFalse;
   if(FileReadInteger(file_handle) != cS.Type() || !cS.Load(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronCGConvOCL::WeightsUpdate(CNeuronBaseOCL* source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   if(!cInputF.WeightsUpdate(source, tau))
      ReturnFalse;
   if(!cInputS.WeightsUpdate(source, tau))
      ReturnFalse;
   if(!cF.WeightsUpdate(source, tau))
      ReturnFalse;
   if(!cS.WeightsUpdate(source, tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronCGConvOCL::SetOpenCL(COpenCLMy * obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cInputF.SetOpenCL(OpenCL);
   cInputS.SetOpenCL(OpenCL);
   cF.SetOpenCL(OpenCL);
   cS.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                       uint units_count, uint dimension,
                       ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl,
                            units_count * dimension, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cEa.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cEa.SetActivationFunction(None);
   index++;
   if(!cWx.Init(0, index, OpenCL, dimension, dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cWe.Init(0, index, OpenCL, dimension, dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cWconcat_ex.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch))
      ReturnFalse;
   cWconcat_ex.SetActivationFunction(None);
   index++;
   if(!cEn.Init(0, index, OpenCL, 2 * dimension, 2 * dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   cEn.SetActivationFunction(None);
   index++;
   if(!cEnT.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cEnEnT.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cEnEnT.SetActivationFunction(GELU);
   index++;
   if(!cAadapt.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cAadapt.SetHeads(units_count);
   index++;
   if(!cAadaptX.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cAadaptX.SetActivationFunction(None);
   index++;
   if(!cApreX.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cApreX.SetActivationFunction(None);
   index++;
   if(!cWadapt.Init(0, index, OpenCL, dimension, dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   cWadapt.SetActivationFunction(None);
   index++;
   if(!cWpre.Init(0, index, OpenCL, dimension, dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   cWpre.SetActivationFunction(None);
   SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!SecondInput || SecondInput.Total() < cAadapt.Neurons())
      ReturnFalse;
//---
   if(bTrain)
     {
      if(!cEa.FeedForward())
         ReturnFalse;
      if(!cWe.FeedForward(cEa.AsObject()))
         ReturnFalse;
     }
//---
   if(!cWx.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!Concat(cWx.getOutput(), cWe.getOutput(), cWconcat_ex.getOutput(), cWx.GetWindowOut(), cWe.GetWindowOut(), cWx.GetUnits()))
      ReturnFalse;
   if(!cEn.FeedForward(cWconcat_ex.AsObject()))
      ReturnFalse;
   if(!cEnT.FeedForward(cEn.AsObject()))
      ReturnFalse;
   if(!MatMul(cEn.getOutput(), cEnT.getOutput(), cEnEnT.getOutput(), cEnT.GetCount(), cEnT.GetWindow(), cEnT.GetCount()))
      ReturnFalse;
   if(cEnEnT.Activation() != None)
      if(!Activation(cEnEnT.getOutput(), cEnEnT.getOutput(), cEnEnT.Activation()))
         ReturnFalse;
   if(!cAadapt.FeedForward(cEnEnT.AsObject()))
      ReturnFalse;
   if(!IdentSum(cAadapt.getOutput(), cAadapt.getOutput(), cAadapt.Heads()))
      ReturnFalse;
   if(!MatMul(SecondInput, NeuronOCL.getOutput(), cApreX.getOutput(),
              cWpre.GetUnits(), cWpre.GetUnits(), cWpre.GetWindow()))
      ReturnFalse;
   if(!MatMul(cAadapt.getOutput(), NeuronOCL.getOutput(), cAadaptX.getOutput(),
              cWadapt.GetUnits(), cWadapt.GetUnits(), cWadapt.GetWindow()))
      ReturnFalse;
   if(!cWpre.FeedForward(cApreX.AsObject()))
      ReturnFalse;
   if(!cWadapt.FeedForward(cAadaptX.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(cWadapt.getOutput(), cWpre.getOutput(), Output, cWadapt.GetWindowOut(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::calcInputGradients(CNeuronBaseOCL *NeuronOCL,
                                     CBufferFloat *SecondInput,
                                     CBufferFloat *SecondGradient,
                                     ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondInput || !SecondGradient ||
      SecondInput.Total() < cAadapt.Neurons() ||
      SecondGradient.Total() < cAadapt.Neurons())
      ReturnFalse;
//---
   if(!DeActivation(cWadapt.getOutput(), cWadapt.getGradient(), Gradient, cWadapt.Activation()))
      ReturnFalse;
   if(!DeActivation(cWpre.getOutput(), cWpre.getGradient(), Gradient, cWpre.Activation()))
      ReturnFalse;
   if(!cApreX.CalcHiddenGradients(cWpre.AsObject()))
      ReturnFalse;
   if(!cAadaptX.CalcHiddenGradients(cWadapt.AsObject()))
      ReturnFalse;
   if(!MatMulGrad(SecondInput, SecondGradient,
                  NeuronOCL.getOutput(), NeuronOCL.getGradient(),
                  cApreX.getGradient(), cWpre.GetUnits(),
                  cWpre.GetUnits(), cWpre.GetWindow()))
      ReturnFalse;
   if(SecondActivation != None)
      if(!DeActivation(SecondInput, SecondGradient,
                       SecondGradient, SecondActivation))
         ReturnFalse;
//---
   if(!MatMulGrad(cAadapt.getOutput(), cAadapt.getGradient(),
                  NeuronOCL.getOutput(), PrevOutput,
                  cAadaptX.getGradient(),  cWadapt.GetUnits(),
                  cWadapt.GetUnits(), cWadapt.GetWindow()))
      ReturnFalse;
   if(!SumAndNormalize(NeuronOCL.getGradient(), PrevOutput,
                       PrevOutput, cWx.GetWindow(), false))
      ReturnFalse;
   if(NeuronOCL.Activation() != None)
      if(!DeActivation(NeuronOCL.getOutput(), PrevOutput,
                       PrevOutput, NeuronOCL.Activation()))
         ReturnFalse;
   if(!cEnEnT.CalcHiddenGradients(cAadapt.AsObject()))
      ReturnFalse;
   if(cEnEnT.Activation() != None)
      if(!DeActivation(cEnEnT.getOutput(), cEnEnT.getGradient(),
                       cEnEnT.getGradient(), cEnEnT.Activation()))
         ReturnFalse;
   if(!MatMulGrad(cEn.getOutput(), cEn.getPrevOutput(),
                  cEnT.getOutput(), cEnT.getGradient(),
                  cEnEnT.getGradient(), cEnT.GetCount(),
                  cEnT.GetWindow(), cEnT.GetCount()))
      ReturnFalse;
   if(!cEn.CalcHiddenGradients(cEnT.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(cEn.getGradient(), cEn.getPrevOutput(),
                       cEn.getGradient(), cEnT.GetWindow(), false))
      ReturnFalse;
   if(cEn.Activation() != None)
      if(!DeActivation(cEn.getOutput(), cEn.getGradient(),
                       cEn.getGradient(), cEn.Activation()))
         ReturnFalse;
   if(!cWconcat_ex.CalcHiddenGradients(cEn.AsObject()))
      ReturnFalse;
   if(!DeConcat(cWx.getGradient(), cWe.getGradient(), cWconcat_ex.getGradient(),
                cWx.GetWindowOut(), cWe.GetWindowOut(), cWx.GetUnits()))
      ReturnFalse;
   if(cWx.Activation() != None)
      if(!DeActivation(cWx.getOutput(), cWx.getGradient(),
                       cWx.getGradient(), cWx.Activation()))
         ReturnFalse;
   if(cWe.Activation() != None)
      if(!DeActivation(cWe.getOutput(), cWe.getGradient(),
                       cWe.getGradient(), cWe.Activation()))
         ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cWx.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(NeuronOCL.getGradient(), PrevOutput,
                       NeuronOCL.getGradient(), cWx.GetWindow(), false))
      ReturnFalse;
   if(!cEa.CalcHiddenGradients(cWe.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *second)
  {
   if(!cEa.UpdateInputWeights())
      ReturnFalse;
   if(!cWe.UpdateInputWeights(cEa.AsObject()))
      ReturnFalse;
   if(!cWx.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cEn.UpdateInputWeights(cWconcat_ex.AsObject()))
      ReturnFalse;
   if(!cWpre.UpdateInputWeights(cApreX.AsObject()))
      ReturnFalse;
   if(!cWadapt.UpdateInputWeights(cAadaptX.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronAGCN* Source = source;
   if(!cEa.WeightsUpdate(Source.cEa.AsObject(), tau))
      ReturnFalse;
   if(!cWe.WeightsUpdate(Source.cWe.AsObject(), tau))
      ReturnFalse;
   if(!cWx.WeightsUpdate(Source.cWx.AsObject(), tau))
      ReturnFalse;
   if(!cEn.WeightsUpdate(Source.cEn.AsObject(), tau))
      ReturnFalse;
   if(!cWpre.WeightsUpdate(Source.cWpre.AsObject(), tau))
      ReturnFalse;
   if(!cWadapt.WeightsUpdate(Source.cWadapt.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(!cEa.Save(file_handle))
      ReturnFalse;
   if(!cWe.Save(file_handle))
      ReturnFalse;
   if(!cWx.Save(file_handle))
      ReturnFalse;
   if(!cEn.Save(file_handle))
      ReturnFalse;
   if(!cWpre.Save(file_handle))
      ReturnFalse;
   if(!cWadapt.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAGCN::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEa.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cWe.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cWx.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEn.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cWpre.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cWadapt.AsObject()))
      ReturnFalse;
//---
   uint units_count = GetUnits();
   uint dimension = GetWindow();
//---
   int index = 3;
   if(!cWconcat_ex.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch))
      ReturnFalse;
   cWconcat_ex.SetActivationFunction(None);
   index += 2;
   if(!cEnT.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cEnEnT.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cEnEnT.SetActivationFunction(GELU);
   index++;
   if(!cAadapt.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cAadapt.SetHeads(units_count);
   index++;
   if(!cAadaptX.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cAadaptX.SetActivationFunction(None);
   index++;
   if(!cApreX.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cApreX.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronAGCN::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cEa.SetOpenCL(OpenCL);
   cWe.SetOpenCL(OpenCL);
   cWx.SetOpenCL(OpenCL);
   cEn.SetOpenCL(OpenCL);
   cWpre.SetOpenCL(OpenCL);
   cWadapt.SetOpenCL(OpenCL);
   cWconcat_ex.SetOpenCL(OpenCL);
   cEnT.SetOpenCL(OpenCL);
   cEnEnT.SetOpenCL(OpenCL);
   cAadapt.SetOpenCL(OpenCL);
   cAadaptX.SetOpenCL(OpenCL);
   cApreX.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronAGCN::TrainMode(bool flag)
  {
   CNeuronBaseOCL::TrainMode(flag);
   cEa.TrainMode(bTrain);
   cWe.TrainMode(bTrain);
   cWx.TrainMode(bTrain);
   cEn.TrainMode(bTrain);
   cWpre.TrainMode(bTrain);
   cWadapt.TrainMode(bTrain);
   cWconcat_ex.TrainMode(bTrain);
   cEnT.TrainMode(bTrain);
   cEnEnT.TrainMode(bTrain);
   cAadapt.TrainMode(bTrain);
   cAadaptX.TrainMode(bTrain);
   cApreX.TrainMode(bTrain);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                            uint units_count, uint dimension,
                            ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl,
                            units_count * dimension, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cX_IA.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cX_AGCN.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cForgetGate.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cResetGate.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cContext.Init(0, index, OpenCL, units_count * dimension, optimization, iBatch))
      ReturnFalse;
   if(!cContext.getPrevOutput().Fill(0))
      ReturnFalse;
   cContext.SetActivationFunction(None);
   bTemp.BufferFree();
   if(!bTemp.BufferInit(units_count * units_count, 0) ||
      !bTemp.BufferCreate(OpenCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!cX_IA.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cX_AGCN.FeedForward(cX_IA.AsObject(), SecondInput))
      ReturnFalse;
   if(!cForgetGate.FeedForward(cX_IA.AsObject(), SecondInput))
      ReturnFalse;
   if(!cResetGate.FeedForward(cX_IA.AsObject(), SecondInput))
      ReturnFalse;
   if(!Activation(cForgetGate.getOutput(), cForgetGate.getOutput(), GELU))
      ReturnFalse;
   if(!Activation(cResetGate.getOutput(), cResetGate.getOutput(), GELU))
      ReturnFalse;
//--- Context
   if(!cContext.SwapOutputs())
      ReturnFalse;
   if(!GateElementMult(cContext.getPrevOutput(), cX_AGCN.getOutput(), cForgetGate.getOutput(), cContext.getOutput()))
      ReturnFalse;
//--- Output
   if(!Activation(cContext.getOutput(), cContext.getPrevOutput(), ELU))
      ReturnFalse;
   if(!GateElementMult(cContext.getPrevOutput(), cX_IA.getOutput(), cResetGate.getOutput(), Output))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::calcInputGradients(CNeuronBaseOCL *NeuronOCL,
      CBufferFloat *SecondInput,
      CBufferFloat *SecondGradient,
      ENUM_ACTIVATION SecondActivation = None)
  {
   if(!NeuronOCL || !SecondInput || !SecondGradient)
      ReturnFalse;
//--- Output
   if(!GateElementMultGrad(cContext.getPrevOutput(), cContext.getGradient(),
                           cX_IA.getOutput(), cX_IA.getPrevOutput(),
                           cResetGate.getOutput(), cResetGate.getGradient(),
                           Gradient, ELU, cX_IA.Activation(), GELU))
      ReturnFalse;
//--- Context
   if(!GateElementMultGrad(cContext.getOutput(), cContext.getPrevOutput(),
                           cX_AGCN.getOutput(), cX_AGCN.getGradient(),
                           cForgetGate.getOutput(), cForgetGate.getGradient(),
                           cContext.getGradient(), None,
                           cX_AGCN.Activation(), GELU))
      ReturnFalse;
//--- Gradient to Interposition Attention
   if(!cX_IA.CalcHiddenGradients(cX_AGCN.AsObject(), SecondInput, SecondGradient, SecondActivation) ||
      !SumAndNormalize(cX_IA.getGradient(), cX_IA.getPrevOutput(), cX_IA.getPrevOutput(), cForgetGate.GetWindow(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cX_IA.CalcHiddenGradients(cForgetGate.AsObject(), SecondInput, GetPointer(bTemp), SecondActivation) ||
      !SumAndNormalize(cX_IA.getGradient(), cX_IA.getPrevOutput(), cX_IA.getPrevOutput(), cForgetGate.GetWindow(), false, 0, 0, 0, 1) ||
      !SumAndNormalize(SecondGradient, GetPointer(bTemp), SecondGradient, cForgetGate.GetUnits(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cX_IA.CalcHiddenGradients(cResetGate.AsObject(), SecondInput, GetPointer(bTemp), SecondActivation) ||
      !SumAndNormalize(cX_IA.getGradient(), cX_IA.getPrevOutput(), cX_IA.getPrevOutput(), cForgetGate.GetWindow(), false, 0, 0, 0, 1) ||
      !SumAndNormalize(SecondGradient, GetPointer(bTemp), SecondGradient, cForgetGate.GetUnits(), false, 0, 0, 0, 1))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cX_IA.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *second)
  {
   if(!cX_IA.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cX_AGCN.UpdateInputWeights(cX_IA.AsObject(), second))
      ReturnFalse;
   if(!cForgetGate.UpdateInputWeights(cX_IA.AsObject(), second))
      ReturnFalse;
   if(!cResetGate.UpdateInputWeights(cX_IA.AsObject(), second))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronGinARCell* Source = source;
   if(!cX_IA.WeightsUpdate(Source.cX_IA.AsObject(), tau))
      ReturnFalse;
   if(!cX_AGCN.WeightsUpdate(Source.cX_AGCN.AsObject(), tau))
      ReturnFalse;
   if(!cForgetGate.WeightsUpdate(Source.cForgetGate.AsObject(), tau))
      ReturnFalse;
   if(!cResetGate.WeightsUpdate(Source.cResetGate.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(!cX_IA.Save(file_handle))
      ReturnFalse;
   if(!cX_AGCN.Save(file_handle))
      ReturnFalse;
   if(!cForgetGate.Save(file_handle))
      ReturnFalse;
   if(!cResetGate.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cX_IA.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cX_AGCN.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cForgetGate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cResetGate.AsObject()))
      ReturnFalse;
//---
   int index = 4;
   if(!cContext.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   if(!cContext.getPrevOutput().Fill(0))
      ReturnFalse;
   cContext.SetActivationFunction(None);
   bTemp.BufferFree();
   if(!bTemp.BufferInit(uint(MathPow(cForgetGate.GetUnits(), 2)), 0) ||
      !bTemp.BufferCreate(OpenCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGinARCell::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cX_IA.SetOpenCL(OpenCL);
   cX_AGCN.SetOpenCL(OpenCL);
   cForgetGate.SetOpenCL(OpenCL);
   cResetGate.SetOpenCL(OpenCL);
   cContext.SetOpenCL(OpenCL);
   bTemp.BufferFree();
   bTemp.BufferInit(uint(MathPow(cForgetGate.GetUnits(), 2)), 0);
   bTemp.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGinARCell::TrainMode(bool flag)
  {
   CNeuronBaseOCL::TrainMode(flag);
   cX_IA.TrainMode(bTrain);
   cX_AGCN.TrainMode(bTrain);
   cForgetGate.TrainMode(bTrain);
   cResetGate.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinARCell::Clear(void)
  {
   if(!CNeuronBaseOCL::Clear())
      ReturnFalse;
   if(!cContext.getPrevOutput() ||
      !cContext.getPrevOutput().Fill(0))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                        uint units_count, uint dimension,
                        ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronSwiGLUOCL::Init(numOutputs, myIndex, open_cl, caCells.Size()*dimension,
                              caCells.Size()*dimension, dimension, units_count, 1,
                              optimization_type, batch))
      ReturnFalse;
   int index = 0;
   if(!cEa.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cEa.SetActivationFunction(None);
   index++;
   if(!cWx.Init(0, index, OpenCL, dimension, dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cWe.Init(0, index, OpenCL, dimension, dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   if(!cWconcat_ex.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch))
      ReturnFalse;
   cWconcat_ex.SetActivationFunction(None);
   index++;
   if(!cEn.Init(0, index, OpenCL, 2 * dimension, 2 * dimension, dimension, units_count, 1, optimization, iBatch))
      ReturnFalse;
   cEn.SetActivationFunction(None);
   index++;
   if(!cEnT.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cEnEnT.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cEnEnT.SetActivationFunction(GELU);
   index++;
   if(!cApre.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cApre.SetHeads(units_count);
   for(uint i = 0; i < caCells.Size(); i++)
     {
      index++;
      if(!caCells[i].Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
         ReturnFalse;
     }
   index++;
   if(!cConcat.Init(0, index, OpenCL, GetWindow()*units_count, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
//--- Calculate Apre
   if(bTrain)
     {
      if(!cEa.FeedForward())
         ReturnFalse;
      if(!cWe.FeedForward(cEa.AsObject()))
         ReturnFalse;
     }
//---
   if(!cWx.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!Concat(cWx.getOutput(), cWe.getOutput(), cWconcat_ex.getOutput(), cWx.GetWindowOut(), cWe.GetWindowOut(), cWx.GetUnits()))
      ReturnFalse;
   if(!cEn.FeedForward(cWconcat_ex.AsObject()))
      ReturnFalse;
   if(!cEnT.FeedForward(cEn.AsObject()))
      ReturnFalse;
   if(!MatMul(cEn.getOutput(), cEnT.getOutput(), cEnEnT.getOutput(), cEnT.GetCount(), cEnT.GetWindow(), cEnT.GetCount()))
      ReturnFalse;
   if(cEnEnT.Activation() != None)
      if(!Activation(cEnEnT.getOutput(), cEnEnT.getOutput(), cEnEnT.Activation()))
         ReturnFalse;
   if(!cApre.FeedForward(cEnEnT.AsObject()))
      ReturnFalse;
   if(!IdentSum(cApre.getOutput(), cApre.getOutput(), cApre.Heads()))
      ReturnFalse;
//--- GimAR Cells
   CNeuronBaseOCL *temp = NeuronOCL;
   for(uint i = 0; i < caCells.Size(); i++)
     {
      if(!caCells[i].FeedForward(temp, cApre.getOutput()))
         ReturnFalse;
      temp = caCells[i].AsObject();
     }
//---
   if(!Concat(caCells[0].getOutput(), caCells[1].getOutput(), caCells[2].getOutput(), caCells[3].getOutput(),
              cConcat.getOutput(), GetWindow() / 4, GetWindow() / 4, GetWindow() / 4, GetWindow() / 4, GetUnits()))
      ReturnFalse;
//---
   return CNeuronSwiGLUOCL::feedForward(cConcat.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronSwiGLUOCL::calcInputGradients(cConcat.AsObject()))
      ReturnFalse;
   if(!DeConcat(caCells[0].getPrevOutput(), caCells[1].getPrevOutput(),
                caCells[2].getPrevOutput(), caCells[3].getGradient(),
                cConcat.getGradient(), GetWindow() / 4,
                GetWindow() / 4, GetWindow() / 4,
                GetWindow() / 4, GetUnits()))
      ReturnFalse;
//--- GimAR Cells
   cApre.getGradient().Fill(0);
   for(uint i = caCells.Size() - 1; i > 0; i--)
      if(!caCells[i - 1].CalcHiddenGradients(caCells[i].AsObject(), cApre.getOutput(),
                                             cApre.getPrevOutput(),
                                             (ENUM_ACTIVATION)cApre.Activation()) ||
         !SumAndNormalize(caCells[i - 1].getGradient(), caCells[i - 1].getPrevOutput(),
                          caCells[i - 1].getGradient(), cWx.GetWindow(), false, 0, 0, 0, 1) ||
         !SumAndNormalize(cApre.getGradient(), cApre.getPrevOutput(),
                          cApre.getGradient(), GetUnits(), false, 0, 0, 0, 1))
         ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(caCells[0].AsObject(), cApre.getOutput(),
                                     cApre.getPrevOutput(), (ENUM_ACTIVATION)cApre.Activation()) ||
      !SumAndNormalize(cApre.getGradient(), cApre.getPrevOutput(),
                       cApre.getGradient(), GetUnits(), false, 0, 0, 0, 1))
      ReturnFalse;
//---
   if(!cEnEnT.CalcHiddenGradients(cApre.AsObject()))
      ReturnFalse;
   if(cEnEnT.Activation() != None)
      if(!DeActivation(cEnEnT.getOutput(), cEnEnT.getGradient(),
                       cEnEnT.getGradient(), cEnEnT.Activation()))
         ReturnFalse;
   if(!MatMulGrad(cEn.getOutput(), cEn.getPrevOutput(),
                  cEnT.getOutput(), cEnT.getGradient(),
                  cEnEnT.getGradient(), cEnT.GetCount(),
                  cEnT.GetWindow(), cEnT.GetCount()))
      ReturnFalse;
   if(!cEn.CalcHiddenGradients(cEnT.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(cEn.getGradient(), cEn.getPrevOutput(),
                       cEn.getGradient(), cEnT.GetWindow(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(cEn.Activation() != None)
      if(!DeActivation(cEn.getOutput(), cEn.getGradient(),
                       cEn.getGradient(), cEn.Activation()))
         ReturnFalse;
   if(!cWconcat_ex.CalcHiddenGradients(cEn.AsObject()))
      ReturnFalse;
   if(!DeConcat(cWx.getGradient(), cWe.getGradient(), cWconcat_ex.getGradient(),
                cWx.GetWindowOut(), cWe.GetWindowOut(), cWx.GetUnits()))
      ReturnFalse;
   if(cWx.Activation() != None)
      if(!DeActivation(cWx.getOutput(), cWx.getGradient(),
                       cWx.getGradient(), cWx.Activation()))
         ReturnFalse;
   if(cWe.Activation() != None)
      if(!DeActivation(cWe.getOutput(), cWe.getGradient(),
                       cWe.getGradient(), cWe.Activation()))
         ReturnFalse;
   CBufferFloat* temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(NeuronOCL.getPrevOutput(), false))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cWx.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(NeuronOCL.getGradient(), temp,
                       NeuronOCL.getGradient(), cWx.GetWindow(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
   if(!cEa.CalcHiddenGradients(cWe.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
//--- Apre
   if(!cEa.UpdateInputWeights())
      ReturnFalse;
   if(!cWe.UpdateInputWeights(cEa.AsObject()))
      ReturnFalse;
//---
   if(!cWx.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cEn.UpdateInputWeights(cWconcat_ex.AsObject()))
      ReturnFalse;
//--- GimAR Cells
   CNeuronBaseOCL *temp = NeuronOCL;
   for(uint i = 0; i < caCells.Size(); i++)
     {
      if(!caCells[i].UpdateInputWeights(temp, cApre.getOutput()))
         ReturnFalse;
      temp = caCells[i].AsObject();
     }
//---
   return CNeuronSwiGLUOCL::updateInputWeights(cConcat.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronSwiGLUOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronGinAR* Source = source;
//--- Apre
   if(!cEa.WeightsUpdate(Source.cEa.AsObject(), tau))
      ReturnFalse;
   if(!cWe.WeightsUpdate(Source.cWe.AsObject(), tau))
      ReturnFalse;
   if(!cWx.WeightsUpdate(Source.cWx.AsObject(), tau))
      ReturnFalse;
   if(!cEn.WeightsUpdate(Source.cEn.AsObject(), tau))
      ReturnFalse;
//--- GimAR Cells
   for(uint i = 0; i < caCells.Size(); i++)
      if(!caCells[i].WeightsUpdate(Source.caCells[i].AsObject(), tau))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::Save(const int file_handle)
  {
   if(!CNeuronSwiGLUOCL::Save(file_handle))
      ReturnFalse;
//--- Apre
   if(!cEa.Save(file_handle))
      ReturnFalse;
   if(!cWe.Save(file_handle))
      ReturnFalse;
   if(!cWx.Save(file_handle))
      ReturnFalse;
   if(!cEn.Save(file_handle))
      ReturnFalse;
//--- GimAR Cells
   for(uint i = 0; i < caCells.Size(); i++)
      if(!caCells[i].Save(file_handle))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::Load(const int file_handle)
  {
   if(!CNeuronSwiGLUOCL::Load(file_handle))
      ReturnFalse;
//--- Apre
   if(!LoadInsideLayer(file_handle, cEa.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cWe.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cWx.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEn.AsObject()))
      ReturnFalse;
//--- GimAR Cells
   for(uint i = 0; i < caCells.Size(); i++)
      if(!LoadInsideLayer(file_handle, caCells[i].AsObject()))
         ReturnFalse;
//---
   uint units_count = GetUnits();
   uint dimension = cWx.GetWindow();
   uint index = 3;
   if(!cWconcat_ex.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch))
      ReturnFalse;
   cWconcat_ex.SetActivationFunction(None);
   index += 2;
   if(!cEnT.Init(0, index, OpenCL, units_count, dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cEnEnT.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cEnEnT.SetActivationFunction(GELU);
   index++;
   if(!cApre.Init(0, index, OpenCL, units_count * units_count, optimization, iBatch))
      ReturnFalse;
   cApre.SetHeads(units_count);
   index += caCells.Size();
   if(!cConcat.Init(0, index, OpenCL, GetWindow()*units_count, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGinAR::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronSwiGLUOCL::SetOpenCL(obj);
//--- Apre
   cEa.SetOpenCL(OpenCL);
   cWe.SetOpenCL(OpenCL);
   cWx.SetOpenCL(OpenCL);
   cEn.SetOpenCL(OpenCL);
//--- GimAR Cells
   for(uint i = 0; i < caCells.Size(); i++)
      caCells[i].SetOpenCL(OpenCL);
//---
   cWconcat_ex.SetOpenCL(OpenCL);
   cEnT.SetOpenCL(OpenCL);
   cEnEnT.SetOpenCL(OpenCL);
   cApre.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGinAR::TrainMode(bool flag)
  {
   CNeuronSwiGLUOCL::TrainMode(flag);
//--- Apre
   cEa.TrainMode(bTrain);
   cWe.TrainMode(bTrain);
   cWx.TrainMode(bTrain);
   cEn.TrainMode(bTrain);
//--- GimAR Cells
   for(uint i = 0; i < caCells.Size(); i++)
      caCells[i].TrainMode(bTrain);
//---
   cWconcat_ex.TrainMode(bTrain);
   cEnT.TrainMode(bTrain);
   cEnEnT.TrainMode(bTrain);
   cApre.TrainMode(bTrain);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGinAR::Clear(void)
  {
   if(!CNeuronSwiGLUOCL::Clear())
      ReturnFalse;
   for(uint i = 0; i < caCells.Size(); i++)
      if(!caCells[i].Clear())
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                   uint units_count, uint variables,
                                   ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units_count * variables, optimization_type, batch))
      ReturnFalse;
//---
   iVariables = variables;
   iCount = units_count;
//---
   uint dimension = (iVariables + 1) / 2;
   uint index = 0;
   if(!cEn.Init(0, index, OpenCL, iVariables * dimension, optimization, iBatch))
      ReturnFalse;
   cEn.SetActivationFunction(None);
   index++;
   if(!cEnT.Init(0, index, OpenCL, iVariables, dimension, optimization, iBatch))
      ReturnFalse;
   cEnT.SetActivationFunction(None);
   index++;
   if(!cEnEnT.Init(0, index, OpenCL, iVariables * iVariables, optimization, iBatch))
      ReturnFalse;
   cEnEnT.SetActivationFunction(None);
   index++;
   if(!cAttan.Init(0, index, OpenCL, iVariables * iVariables, optimization, iBatch))
      ReturnFalse;
   cAttan.SetHeads(iVariables);
//---
   index++;
   if(!cMeanSTDevs.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch))
      ReturnFalse;
   cMeanSTDevs.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::AdaptSpatialNorm(CNeuronBaseOCL *NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL)
      ReturnFalse;
   uint global_work_offset[3] = { 0 };
   uint global_work_size[] = { iCount, MathMin(iVariables, uint(OpenCL.GetMaxLocalSize(1))), iVariables};
   uint local_work_size[] = { 1, global_work_size[1], 1};
//---
   uint kernel = def_k_AdaptSpatialNorm;
   setBuffer(kernel, def_k_asn_inputs, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_asn_mean_stdevs, cMeanSTDevs.getOutputIndex())
   setBuffer(kernel, def_k_asn_attention, cAttan.getOutputIndex())
   setBuffer(kernel, def_k_asn_outputs, getOutputIndex())
   kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!Output.BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::AdaptSpatialNormGrad(CNeuronBaseOCL *NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL)
      ReturnFalse;
   uint global_work_offset[3] = { 0 };
   uint global_work_size[] = { MathMax(iVariables, iCount), MathMin(MathMax(iVariables, iCount), uint(OpenCL.GetMaxLocalSize(1))), iVariables};
   uint local_work_size[] = { 1, global_work_size[1], 1};
//---
   uint kernel = def_k_AdaptSpatialNormGrad;
   setBuffer(kernel, def_k_asng_inputs, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_asng_inputs_gr, NeuronOCL.getGradientIndex())
   setBuffer(kernel, def_k_asng_mean_stdevs, cMeanSTDevs.getOutputIndex())
   setBuffer(kernel, def_k_asng_mean_stdevs_gr, cMeanSTDevs.getGradientIndex())
   setBuffer(kernel, def_k_asng_attention, cAttan.getOutputIndex())
   setBuffer(kernel, def_k_asng_attention_gr, cAttan.getGradientIndex())
   setBuffer(kernel, def_k_asng_outputs_gr, getGradientIndex())
   setArgument(kernel, def_k_asng_total_inputs, iCount)
   kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!NeuronOCL.getGradient().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(bTrain)
     {
      if(!cEn.FeedForward())
         ReturnFalse;
      if(!cEnT.FeedForward(cEn.AsObject()))
         ReturnFalse;
      if(!MatMul(cEn.getOutput(), cEnT.getOutput(), cEnEnT.getOutput(),
                 iVariables, cEnT.GetWindow(), iVariables, 1, false))
         ReturnFalse;
      if(!cAttan.FeedForward(cEnEnT.AsObject()))
         ReturnFalse;
     }
//---
   return AdaptSpatialNorm(NeuronOCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!AdaptSpatialNormGrad(NeuronOCL))
      ReturnFalse;
   if(!cEnEnT.CalcHiddenGradients(cAttan.AsObject()))
      ReturnFalse;
   if(!MatMulGrad(cEn.getOutput(), cEn.getPrevOutput(),
                  cEnT.getOutput(), cEnT.getGradient(),
                  cEnEnT.getGradient(), iVariables,
                  cEnT.GetWindow(), iVariables, 1, false))
      ReturnFalse;
   if(!cEn.CalcHiddenGradients(cEnT.AsObject()) ||
      !SumAndNormalize(cEn.getGradient(), cEn.getPrevOutput(), cEn.getGradient(),
                       cEnT.GetWindow(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(cEn.Activation() != None)
      if(!DeActivation(cEn.getOutput(), cEn.getGradient(), cEn.getGradient(), cEn.Activation()))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   return cEn.UpdateInputWeights();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(!cEn.Save(file_handle))
      ReturnFalse;
   if(!cAttan.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEn.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cAttan.AsObject()))
      ReturnFalse;
//---
   iVariables = cAttan.Heads();
   iCount = Neurons() / iVariables;
   uint dimension = cEn.Neurons() / iVariables;
//---
   uint index = 1;
   if(!cEnT.Init(0, index, OpenCL, iVariables, dimension, optimization, iBatch))
      ReturnFalse;
   cEnT.SetActivationFunction(None);
   index++;
   if(!cEnEnT.Init(0, index, OpenCL, iVariables * iVariables, optimization, iBatch))
      ReturnFalse;
   cEnEnT.SetActivationFunction(None);
   index += 2;
   if(!cMeanSTDevs.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch))
      ReturnFalse;
   cMeanSTDevs.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronAdaptSpatialNorm::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cEn.SetOpenCL(OpenCL);
   cEnT.SetOpenCL(OpenCL);
   cEnEnT.SetOpenCL(OpenCL);
   cAttan.SetOpenCL(OpenCL);
   cMeanSTDevs.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronAdaptSpatialNorm::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronAdaptSpatialNorm* Source = source;
   if(!cEn.WeightsUpdate(Source.cEn.AsObject(), tau))
      ReturnFalse;
//---
   if(!cEn.FeedForward())
      ReturnFalse;
   if(!cEnT.FeedForward(cEn.AsObject()))
      ReturnFalse;
   if(!MatMul(cEn.getOutput(), cEnT.getOutput(), cEnEnT.getOutput(),
              iVariables, cEnT.GetWindow(), iVariables, 1, false))
      ReturnFalse;
   if(!cAttan.FeedForward(cEnEnT.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                              uint units_count, uint variables, uint forecast,
                              uint season_period, uint short_period,
                              ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronTransposeOCL::Init(numOutputs, myIndex, open_cl, units_count + forecast, variables, optimization_type, batch))
      ReturnFalse;
   SetActivationFunction(None);
//---
   uint index = 0;
   if(!cLongNorm.Init(0, index, OpenCL, 1, units_count, variables, optimization, iBatch))
      ReturnFalse;
   cLongNorm.SetActivationFunction(None);
   index++;
   if(!cSeasonTransp.Init(0, index, OpenCL, variables, units_count / season_period, season_period, optimization, iBatch))
      ReturnFalse;
   cSeasonTransp.SetActivationFunction(None);
   index++;
   if(!cSeasonNorm.Init(0, index, OpenCL, cSeasonTransp.GetCount(), season_period, variables, optimization, iBatch))
      ReturnFalse;
   cSeasonNorm.SetActivationFunction(None);
   index++;
   if(!cUnSeasonTransp.Init(0, index, OpenCL, variables, season_period, cSeasonTransp.GetCount(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cShortNorm.Init(0, index, OpenCL, units_count / short_period, short_period, variables, optimization, iBatch))
      ReturnFalse;
   cSeasonNorm.SetActivationFunction(None);
   index++;
   if(!cAdaptSpatNorm.Init(0, index, OpenCL, units_count, variables, optimization, iBatch))
      ReturnFalse;
   cAdaptSpatNorm.SetActivationFunction(None);
   index++;
   uint concatSize = units_count * variables;                                      //inputs
   concatSize += cLongNorm.Neurons() + cLongNorm.GetMeanSTDevs().Neurons();         // long term
   concatSize += cSeasonNorm.Neurons() + cSeasonNorm.GetMeanSTDevs().Neurons();     // seasons
   concatSize += cShortNorm.Neurons() + cShortNorm.GetMeanSTDevs().Neurons();       // short term
   concatSize += cAdaptSpatNorm.Neurons() + cAdaptSpatNorm.GetMeanSTDevs().Neurons(); // spatial
   if(!cConcatenated.Init(0, index, OpenCL, concatSize, optimization, iBatch))
      ReturnFalse;
   cConcatenated.SetActivationFunction(None);
   index++;
   if(!cProjection.Init(0, index, OpenCL, concatSize / variables, concatSize / variables, units_count + forecast, 1, variables, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cTranspose.Init(0, index, OpenCL, variables, units_count + forecast, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!caFusion[0].Init(0, index, OpenCL, variables, variables, variables, units_count + forecast, optimization, iBatch))
      ReturnFalse;
   caFusion[0].SetActivationFunction(TANH);
   index++;
   if(!caFusion[1].Init(0, index, OpenCL, variables, variables, variables, units_count + forecast, optimization, iBatch))
      ReturnFalse;
   caFusion[1].SetActivationFunction(SIGMOID);
   index++;
   if(!cFusionOut.Init(0, index, OpenCL, caFusion[0].Neurons(), optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cLongNorm.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cSeasonTransp.FeedForward(cLongNorm.AsObject()))
      ReturnFalse;
   if(!cSeasonNorm.FeedForward(cSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cUnSeasonTransp.FeedForward(cSeasonNorm.AsObject()))
      ReturnFalse;
   if(!cShortNorm.FeedForward(cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cAdaptSpatNorm.FeedForward(cShortNorm.AsObject()))
      ReturnFalse;
   uint windows[3] = {NeuronOCL.Neurons() / iWindow,
                      cLongNorm.GetPeriod()*cLongNorm.GetUnits(),
                      2 * cLongNorm.GetUnits()
                     };
   if(!Concat(NeuronOCL.getOutput(), cLongNorm.getOutput(), cLongNorm.GetMeanSTDevs().getOutput(),
              cConcatenated.getOutput(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   windows[0] = windows[0] + windows[1] + windows[2];
   windows[1] = cSeasonNorm.GetPeriod() * cSeasonNorm.GetUnits();
   windows[2] = 2 * cSeasonNorm.GetUnits();
   if(!cConcatenated.SwapOutputs() ||
      !Concat(cConcatenated.getPrevOutput(), cSeasonNorm.getOutput(), cSeasonNorm.GetMeanSTDevs().getOutput(),
              cConcatenated.getOutput(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   windows[0] = windows[0] + windows[1] + windows[2];
   windows[1] = cShortNorm.GetPeriod() * cShortNorm.GetUnits();
   windows[2] = 2 * cShortNorm.GetUnits();
   if(!cConcatenated.SwapOutputs() ||
      !Concat(cConcatenated.getPrevOutput(), cShortNorm.getOutput(), cShortNorm.GetMeanSTDevs().getOutput(),
              cConcatenated.getOutput(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   windows[0] = windows[0] + windows[1] + windows[2];
   windows[1] = cAdaptSpatNorm.GetUnits();
   windows[2] = 2 * cAdaptSpatNorm.GetUnits();
   if(!cConcatenated.SwapOutputs() ||
      !Concat(cConcatenated.getPrevOutput(), cAdaptSpatNorm.getOutput(), cAdaptSpatNorm.GetMeanSTDevs().getOutput(),
              cConcatenated.getOutput(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   if(!cProjection.FeedForward(cConcatenated.AsObject()))
      ReturnFalse;
   if(!cTranspose.FeedForward(cProjection.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < caFusion.Size(); i++)
      if(!caFusion[i].FeedForward(cTranspose.AsObject()))
         ReturnFalse;
   if(!ElementMult(caFusion[0].getOutput(), caFusion[1].getOutput(), cFusionOut.getOutput()))
      ReturnFalse;
//---
   return CNeuronTransposeOCL::feedForward(cFusionOut.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronTransposeOCL::calcInputGradients(cFusionOut.AsObject()))
      ReturnFalse;
   if(!ElementMultGrad(caFusion[0].getOutput(), caFusion[0].getGradient(),
                       caFusion[1].getOutput(), caFusion[1].getGradient(),
                       cFusionOut.getGradient(), caFusion[0].Activation(), caFusion[1].Activation()))
      ReturnFalse;
   if(!cTranspose.CalcHiddenGradients(caFusion[0].AsObject()))
      ReturnFalse;
   CBufferFloat* temp = cTranspose.getGradient();
   if(!cTranspose.SetGradient(cTranspose.getPrevOutput(), false) ||
      !cTranspose.CalcHiddenGradients(caFusion[1].AsObject()) ||
      !SumAndNormalize(temp, cTranspose.getGradient(), temp, cTranspose.GetCount(), false, 0, 0, 0, 1) ||
      !cTranspose.SetGradient(temp, false))
      ReturnFalse;
   if(!cProjection.CalcHiddenGradients(cTranspose.AsObject()))
      ReturnFalse;
   if(!cConcatenated.CalcHiddenGradients(cProjection.AsObject()))
      ReturnFalse;
//---
   uint windows[3] = {0};
   windows[1] = cAdaptSpatNorm.GetUnits();
   windows[2] = 2 * cAdaptSpatNorm.GetUnits();
   windows[0] = cConcatenated.Neurons() / iWindow - windows[1] - windows[2];
   if(!DeConcat(cConcatenated.getPrevOutput(), cAdaptSpatNorm.getGradient(),
                cAdaptSpatNorm.GetMeanSTDevs().getGradient(),
                cConcatenated.getGradient(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   windows[1] = cShortNorm.GetPeriod() * cShortNorm.GetUnits();
   windows[2] = 2 * cShortNorm.GetUnits();
   windows[0] = windows[0] - windows[1] - windows[2];
   if(!DeConcat(cConcatenated.getGradient(), cShortNorm.getPrevOutput(),
                cShortNorm.GetMeanSTDevs().getGradient(),
                cConcatenated.getPrevOutput(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   windows[1] = cSeasonNorm.GetPeriod() * cSeasonNorm.GetUnits();
   windows[2] = 2 * cSeasonNorm.GetUnits();
   windows[0] = windows[0] - windows[1] - windows[2];
   if(!DeConcat(cConcatenated.getPrevOutput(), cSeasonNorm.getPrevOutput(),
                cSeasonNorm.GetMeanSTDevs().getGradient(),
                cConcatenated.getGradient(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
   windows[1] = cLongNorm.GetPeriod() * cLongNorm.GetUnits();
   windows[2] = 2 * cLongNorm.GetUnits();
   windows[0] = windows[0] - windows[1] - windows[2];
   if(!DeConcat(NeuronOCL.getPrevOutput(), cLongNorm.getPrevOutput(),
                cLongNorm.GetMeanSTDevs().getGradient(),
                cConcatenated.getPrevOutput(), windows[0], windows[1], windows[2], iWindow))
      ReturnFalse;
//---
   if(!cShortNorm.CalcHiddenGradients(cAdaptSpatNorm.AsObject()) ||
      !SumAndNormalize(cShortNorm.getGradient(), cShortNorm.getPrevOutput(),
                       cShortNorm.getGradient(), cShortNorm.GetPeriod(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cUnSeasonTransp.CalcHiddenGradients(cShortNorm.AsObject()))
      ReturnFalse;
   if(!cSeasonNorm.CalcHiddenGradients(cUnSeasonTransp.AsObject()) ||
      !SumAndNormalize(cSeasonNorm.getGradient(), cSeasonNorm.getPrevOutput(),
                       cSeasonNorm.getGradient(), cSeasonNorm.GetPeriod(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cSeasonTransp.CalcHiddenGradients(cSeasonNorm.AsObject()))
      ReturnFalse;
   if(!cLongNorm.CalcHiddenGradients(cSeasonTransp.AsObject()) ||
      !SumAndNormalize(cLongNorm.getGradient(), cLongNorm.getPrevOutput(),
                       cLongNorm.getGradient(), cLongNorm.GetPeriod(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cLongNorm.AsObject()) ||
      !SumAndNormalize(NeuronOCL.getGradient(), NeuronOCL.getPrevOutput(),
                       NeuronOCL.getGradient(), cLongNorm.GetPeriod(), false, 0, 0, 0, 1))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cAdaptSpatNorm.UpdateInputWeights(cShortNorm.AsObject()))
      ReturnFalse;
   if(!cProjection.UpdateInputWeights(cConcatenated.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < caFusion.Size(); i++)
      if(!caFusion[i].UpdateInputWeights(cTranspose.AsObject()))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::Save(const int file_handle)
  {
   if(!CNeuronTransposeOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cLongNorm.Save(file_handle))
      ReturnFalse;
   if(!cSeasonTransp.Save(file_handle))
      ReturnFalse;
   if(!cSeasonNorm.Save(file_handle))
      ReturnFalse;
   if(!cUnSeasonTransp.Save(file_handle))
      ReturnFalse;
   if(!cShortNorm.Save(file_handle))
      ReturnFalse;
   if(!cAdaptSpatNorm.Save(file_handle))
      ReturnFalse;
   if(!cConcatenated.Save(file_handle))
      ReturnFalse;
   if(!cProjection.Save(file_handle))
      ReturnFalse;
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   for(uint i = 0; i < caFusion.Size(); i++)
      if(!caFusion[i].Save(file_handle))
         ReturnFalse;
   if(!cFusionOut.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::Load(const int file_handle)
  {
   if(!CNeuronTransposeOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cLongNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSeasonTransp.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSeasonNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cShortNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cAdaptSpatNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cConcatenated.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cProjection.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   for(uint i = 0; i < caFusion.Size(); i++)
      if(!LoadInsideLayer(file_handle, caFusion[i].AsObject()))
         ReturnFalse;
   if(!LoadInsideLayer(file_handle, cFusionOut.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSCNNEncoder::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronTransposeOCL::SetOpenCL(obj);
//---
   cLongNorm.SetOpenCL(OpenCL);
   cSeasonTransp.SetOpenCL(OpenCL);
   cSeasonNorm.SetOpenCL(OpenCL);
   cUnSeasonTransp.SetOpenCL(OpenCL);
   cShortNorm.SetOpenCL(OpenCL);
   cAdaptSpatNorm.SetOpenCL(OpenCL);
   cConcatenated.SetOpenCL(OpenCL);
   cProjection.SetOpenCL(OpenCL);
   cTranspose.SetOpenCL(OpenCL);
   for(uint i = 0; i < caFusion.Size(); i++)
      caFusion[i].SetOpenCL(OpenCL);
   cFusionOut.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSCNNEncoder::TrainMode(bool flag)
  {
   CNeuronTransposeOCL::TrainMode(flag);
//---
   cLongNorm.TrainMode(bTrain);
   cSeasonTransp.TrainMode(bTrain);
   cSeasonNorm.TrainMode(bTrain);
   cUnSeasonTransp.TrainMode(bTrain);
   cShortNorm.TrainMode(bTrain);
   cAdaptSpatNorm.TrainMode(bTrain);
   cConcatenated.TrainMode(bTrain);
   cProjection.TrainMode(bTrain);
   cTranspose.TrainMode(bTrain);
   for(uint i = 0; i < caFusion.Size(); i++)
      caFusion[i].TrainMode(bTrain);
   cFusionOut.TrainMode(bTrain);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNNEncoder::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronTransposeOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronSCNNEncoder* Source = source;
   if(!cAdaptSpatNorm.WeightsUpdate(Source.cAdaptSpatNorm.AsObject(), tau))
      ReturnFalse;
   if(!cProjection.WeightsUpdate(Source.cProjection.AsObject(), tau))
      ReturnFalse;
   for(uint i = 0; i < caFusion.Size(); i++)
      if(!caFusion[i].WeightsUpdate(Source.caFusion[i].AsObject(), tau))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                       uint units_count, uint variables, uint forecast,
                       uint season_period, uint short_period, uint layers,
                       ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, (units_count + forecast)*variables, optimization_type, batch))
      ReturnFalse;
   SetActivationFunction(None);
//---
   cLayers.Clear();
   cLayers.SetOpenCL(OpenCL);
   CNeuronSCNNEncoder* encoder = NULL;
   CNeuronBaseOCL* residual = NULL;
   for(uint l = 0; l < layers; l++)
     {
      encoder = new CNeuronSCNNEncoder();
      if(!encoder)
         ReturnFalse;
      if(!encoder.Init(0, l, OpenCL, units_count, variables, forecast, season_period, short_period, optimization, iBatch) ||
         !cLayers.Add(encoder))
         ReturnFalse;
      encoder.SetActivationFunction(None);
      if((l + 1) == layers)
         break;
      //---
      residual = new CNeuronBaseOCL();
      if(!residual)
         ReturnFalse;
      if(!residual.Init(0, l, OpenCL, units_count * variables, optimization, iBatch) ||
         !cLayers.Add(residual))
         ReturnFalse;
      residual.SetActivationFunction(None);
     }
   if(!SetGradient(encoder.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!Output.Fill(0))
      ReturnFalse;
   CNeuronBaseOCL* inputs = NeuronOCL;
   CNeuronSCNNEncoder* current = NULL;
   CNeuronBaseOCL* residual = NULL;
   CNeuronBaseOCL* temp = NULL;
   int layers = cLayers.Total();
//---
   for(int l = 0; l < layers; l += 2)
     {
      current = cLayers[l];
      if(!current ||
         !current.FeedForward(inputs) ||
         !SumAndNormalize(Output, current.getOutput(), Output, current.GetCount(), false, 0, 0, 0, 1))
         ReturnFalse;
      if((l + 1) == layers)
         break;
      uint variables = current.GetWindow();
      uint dimension = inputs.Neurons() / variables;
      uint forecast = current.GetCount() - dimension;
      residual = cLayers[l + 1];
      if(!residual)
         ReturnFalse;
      if(!DeConcat(residual.getOutput(), current.getPrevOutput(), current.getOutput(),
                   dimension, forecast, variables) ||
         !SumAndNormalize(residual.getOutput(), inputs.getOutput(), residual.getOutput(),
                          dimension, true, 0, 0, 0, 1))
         ReturnFalse;
      inputs = residual;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!PrevOutput.Fill(0))
      ReturnFalse;
//---
   CNeuronBaseOCL* inputs = NULL;
   CNeuronSCNNEncoder* current = cLayers[-1];
   CNeuronBaseOCL* residual = NULL;
   int layers = cLayers.Total() - 2;
//---
   for(int l = layers; l >= 0; l--)
      switch(cLayers[l].Type())
        {
         case defNeuronBaseOCL:
            inputs = cLayers[l];
            if(!inputs ||
               !inputs.CalcHiddenGradients(current))
               ReturnFalse;
            if(!!residual)
               if(!SumAndNormalize(inputs.getGradient(), residual.getGradient(), inputs.getGradient(),
                                   current.GetWindow(), false, 0, 0, 0, 1))
                  ReturnFalse;
            residual = inputs;
            break;
         case defNeuronSCNNEncoder:
            current = cLayers[l];
            if(!residual)
               break;
            inputs = cLayers[l + 2];
            if(!inputs)
               ReturnFalse;
            if(!Concat(residual.getGradient(), PrevOutput, current.getGradient(),
                       residual.Neurons() / current.GetWindow(),
                       current.GetCount() - residual.Neurons() / current.GetWindow(),
                       current.GetWindow()) ||
               !SumAndNormalize(current.getGradient(), inputs.getGradient(), current.getGradient(),
                                current.GetWindow(), false, 0, 0, 0, 1))
               ReturnFalse;
            break;
         default:
            ReturnFalse;
            break;
        }
//---
   if(!NeuronOCL.CalcHiddenGradients(current))
      ReturnFalse;
   if(!!residual)
      if(!SumAndNormalize(NeuronOCL.getGradient(), residual.getGradient(), NeuronOCL.getGradient(),
                          current.GetWindow(), false, 0, 0, 0, 1))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   CNeuronBaseOCL* inputs = NeuronOCL;
   CNeuronBaseOCL* current = NULL;
//---
   for(int l = 0; l < cLayers.Total(); l++)
     {
      current = cLayers[l];
      if(!current)
         ReturnFalse;
      if(current.Type() == defNeuronSCNNEncoder ||
         current.Type() == defNeuronSSCNNEncoder)
         if(!current.UpdateInputWeights(inputs))
            ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronSCNN* Source = source;
   if(!cLayers.WeightsUpdate(Source.cLayers.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(!cLayers.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSCNN::Load(const int file_handle)
  {
   cLayers.Clear();
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   cLayers.SetOpenCL(OpenCL);
   if(!cLayers.Load(file_handle))
      ReturnFalse;
//---
   CNeuronBaseOCL* last = cLayers[-1];
   if(!SetGradient(last.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSCNN::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cLayers.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSCNN::TrainMode(bool flag)
  {
   CNeuronBaseOCL::TrainMode(flag);
   cLayers.TrainMode(flag);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                       uint units_count, uint window, uint window_out,
                                       ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units_count * window_out, optimization_type, batch))
      ReturnFalse;
   activation = None;
//---
   if(!cProjection.Init(0, 0, OpenCL, window, window, window_out, units_count, 1, optimization, iBatch))
      ReturnFalse;
   if(!cConvolution.Init(0, 1, OpenCL, window_out, window_out, window_out, units_count, 1, optimization, iBatch))
      ReturnFalse;
   cConvolution.SetActivationFunction(None);
   if(!cResidual.Init(0, 2, OpenCL, window, window, window_out, units_count, 1, optimization, iBatch))
      ReturnFalse;
   cResidual.SetActivationFunction(None);
//---
   if(!SetGradient(cConvolution.getGradient(), true))
      ReturnFalse;
   if(!cResidual.SetGradient(getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cProjection.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cConvolution.FeedForward(cProjection.AsObject()))
      ReturnFalse;
   if(!cResidual.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!SumAndNormalize(cConvolution.getOutput(), cResidual.getOutput(),
                       Output, GetWindowOut(), true, 0, 0, 0, 1))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!cProjection.CalcHiddenGradients(cConvolution.AsObject()))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cProjection.AsObject()))
      ReturnFalse;
   CBufferFloat* temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(NeuronOCL.getPrevOutput(), false) ||
      !NeuronOCL.CalcHiddenGradients(cResidual.AsObject()) ||
      !SumAndNormalize(temp, NeuronOCL.getGradient(), temp,
                       GetWindowIn(), false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cProjection.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cConvolution.UpdateInputWeights(cProjection.AsObject()))
      ReturnFalse;
   if(!cResidual.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronPolynomialRegression* Source = source;
   if(!cProjection.WeightsUpdate(Source.cProjection.AsObject(), tau))
      ReturnFalse;
   if(!cConvolution.WeightsUpdate(Source.cConvolution.AsObject(), tau))
      ReturnFalse;
   if(!cResidual.WeightsUpdate(Source.cResidual.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cProjection.Save(file_handle))
      ReturnFalse;
   if(!cConvolution.Save(file_handle))
      ReturnFalse;
   if(!cResidual.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronPolynomialRegression::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cProjection.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cConvolution.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cResidual.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronPolynomialRegression::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
//---
   cProjection.SetOpenCL(OpenCL);
   cConvolution.SetOpenCL(OpenCL);
   cResidual.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                               uint units_count, uint variables, uint forecast,
                               uint season_period, uint short_period,
                               ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronTransposeOCL::Init(numOutputs, myIndex, open_cl, units_count + forecast,
                                 variables, optimization_type, batch))
      ReturnFalse;
   activation = None;
//---
   int index = 0;
   if(!cLongNorm.Init(0, index, OpenCL, 1, units_count, iWindow, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cLongExtrapolate.Init(0, index, OpenCL, units_count, units_count, iCount, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cLongExtrapolate.SetActivationFunction(None);
   index++;
   if(!cLongMeanSTDevTransp.Init(0, index, OpenCL, iWindow, 2, optimization, iBatch))
      ReturnFalse;
   if(!cLongMeanSTDevTransp.getGradient().Fill(0))
      ReturnFalse;
   index++;
   if(!cLongMeanExtrapolate.Init(0, index, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cLongExtrapolate.SetActivationFunction(None);
   if(!cLongMeanExtrapolate.getPrevOutput().Fill(1))
      ReturnFalse;
   index++;
   if(!cSeasonTransp.Init(0, index, OpenCL, iWindow, units_count / season_period, season_period, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cSeasonNorm.Init(0, index, OpenCL, season_period, cSeasonTransp.GetCount(), iWindow, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cUnSeasonTransp.Init(0, index, OpenCL, iWindow, season_period, cSeasonTransp.GetCount(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cSeasonExtrapolate.Init(0, index, OpenCL, units_count, units_count, iCount, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cSeasonExtrapolate.SetActivationFunction(None);
   index++;
   if(!cSeasonMeanExtrapolate.Init(0, index, OpenCL, season_period, season_period, iCount, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cSeasonMeanExtrapolate.SetActivationFunction(None);
   index++;
   if(!cShortNorm.Init(0, index, OpenCL, units_count / short_period, short_period, iWindow, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cShortExtrapolate.Init(0, index, OpenCL, units_count, units_count, iCount, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cShortExtrapolate.SetActivationFunction(None);
   index++;
   if(!cShortMeanExtrapolate.Init(0, index, OpenCL, cShortNorm.GetUnits(), cShortNorm.GetUnits(), iCount, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cShortMeanExtrapolate.SetActivationFunction(None);
   index++;
   if(!cSpatialNorm.Init(0, index, OpenCL, units_count, variables, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cSpatialExtrapolate.Init(0, index, OpenCL, units_count, units_count, iCount, iWindow, 1, optimization, iBatch))
      ReturnFalse;
   cSpatialExtrapolate.SetActivationFunction(None);
   index++;
   if(!cSpatialMeanExtrapolate.Init(0, index, OpenCL, units_count, units_count, iCount, 1, iWindow, optimization, iBatch))
      ReturnFalse;
   cSpatialMeanExtrapolate.SetActivationFunction(None);
   index++;
   if(!cConcatenated.Init(0, index, OpenCL, 8 * Neurons(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cTranspose.Init(0, index, OpenCL, 8 * iWindow, iCount, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cFusion.Init(0, index, OpenCL, iCount, 8 * iWindow, iWindow, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
//--- Long
   if(!cLongNorm.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cLongExtrapolate.FeedForward(cLongNorm.AsObject()))
      ReturnFalse;
   if(!cLongMeanSTDevTransp.FeedForward(cLongNorm.GetMeanSTDevs()))
      ReturnFalse;
   if(!MatMul(cLongMeanSTDevTransp.getOutput(), cLongMeanExtrapolate.getPrevOutput(),
              cLongMeanExtrapolate.getOutput(), 1, 1, iCount, iWindow, true))
      ReturnFalse;
//--- Season
   if(!cSeasonTransp.FeedForward(cLongNorm.AsObject()))
      ReturnFalse;
   if(!cSeasonNorm.FeedForward(cSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cUnSeasonTransp.FeedForward(cSeasonNorm.AsObject()))
      ReturnFalse;
   if(!cSeasonExtrapolate.FeedForward(cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cSeasonMeanExtrapolate.FeedForward(cSeasonNorm.GetMeans()))
      ReturnFalse;
//--- Short
   if(!cShortNorm.FeedForward(cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cShortExtrapolate.FeedForward(cShortNorm.AsObject()))
      ReturnFalse;
   if(!cShortMeanExtrapolate.FeedForward(cShortNorm.GetMeans()))
      ReturnFalse;
//--- Spatial
   if(!cSpatialNorm.FeedForward(cShortNorm.AsObject()))
      ReturnFalse;
   if(!cSpatialExtrapolate.FeedForward(cSpatialNorm.AsObject()))
      ReturnFalse;
   if(!cSpatialMeanExtrapolate.FeedForward(cSpatialNorm.GetMeans()))
      ReturnFalse;
//--- Concat
   if(!Concat(cLongExtrapolate.getOutput(), cLongMeanExtrapolate.getOutput(),
              cSeasonExtrapolate.getOutput(), cSeasonMeanExtrapolate.getOutput(),
              cConcatenated.getOutput(), iCount, iCount, iCount, iCount, iWindow))
      ReturnFalse;
   if(!Concat(cConcatenated.getOutput(), cShortExtrapolate.getOutput(),
              cShortMeanExtrapolate.getOutput(), cConcatenated.getPrevOutput(),
              4 * iCount, iCount, iCount, iWindow))
      ReturnFalse;
   if(!Concat(cConcatenated.getPrevOutput(), cSpatialExtrapolate.getOutput(),
              cSpatialMeanExtrapolate.getOutput(), cConcatenated.getOutput(),
              6 * iCount, iCount, iCount, iWindow))
      ReturnFalse;
//--- Fusion
   if(!cTranspose.FeedForward(cConcatenated.AsObject()))
      ReturnFalse;
   if(!cFusion.FeedForward(cTranspose.AsObject()))
      ReturnFalse;
//---
   return CNeuronTransposeOCL::feedForward(cFusion.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//--- Fusion
   if(!CNeuronTransposeOCL::calcInputGradients(cFusion.AsObject()))
      ReturnFalse;
   if(!cTranspose.CalcHiddenGradients(cFusion.AsObject()))
      ReturnFalse;
   if(!cConcatenated.CalcHiddenGradients(cTranspose.AsObject()))
      ReturnFalse;
//--- DeConcat
   if(!DeConcat(cConcatenated.getPrevOutput(), cSpatialExtrapolate.getGradient(),
                cSpatialMeanExtrapolate.getGradient(), cConcatenated.getGradient(),
                6 * iCount, iCount, iCount, iWindow))
      ReturnFalse;
   if(!DeConcat(cConcatenated.getGradient(), cShortExtrapolate.getGradient(),
                cShortMeanExtrapolate.getGradient(), cConcatenated.getPrevOutput(),
                4 * iCount, iCount, iCount, iWindow))
      ReturnFalse;
   if(!DeConcat(cLongExtrapolate.getGradient(), cLongMeanExtrapolate.getGradient(),
                cSeasonExtrapolate.getGradient(), cSeasonMeanExtrapolate.getGradient(),
                cConcatenated.getGradient(), iCount, iCount, iCount, iCount, iWindow))
      ReturnFalse;
//--- Spatial
   if(!cSpatialNorm.GetMeans().CalcHiddenGradients(cSpatialMeanExtrapolate.AsObject()))
      ReturnFalse;
   if(!cSpatialNorm.CalcHiddenGradients(cSpatialExtrapolate.AsObject()))
      ReturnFalse;
//--- Short
   if(!cShortNorm.CalcHiddenGradients(cSpatialNorm.AsObject()))
      ReturnFalse;
   CBufferFloat* temp = cShortNorm.getGradient();
   if(!cShortNorm.SetGradient(cShortNorm.getPrevOutput(), false) ||
      !cShortNorm.CalcHiddenGradients(cShortExtrapolate.AsObject()) ||
      !SumAndNormalize(temp, cShortNorm.getGradient(), temp, iWindow, false, 0, 0, 0, 1) ||
      !cShortNorm.SetGradient(temp, false))
      ReturnFalse;
   if(!cShortNorm.GetMeans().CalcHiddenGradients(cShortMeanExtrapolate.AsObject()))
      ReturnFalse;
//--- Season
   if(!cUnSeasonTransp.CalcHiddenGradients(cShortNorm.AsObject()))
      ReturnFalse;
   temp = cUnSeasonTransp.getGradient();
   if(!cUnSeasonTransp.SetGradient(cUnSeasonTransp.getPrevOutput(), false) ||
      !cUnSeasonTransp.CalcHiddenGradients(cSeasonExtrapolate.AsObject()) ||
      !SumAndNormalize(temp, cUnSeasonTransp.getGradient(), temp, iWindow, false, 0, 0, 0, 1) ||
      !cUnSeasonTransp.SetGradient(temp, false))
      ReturnFalse;
   if(!cSeasonNorm.GetMeans().CalcHiddenGradients(cSeasonMeanExtrapolate.AsObject()))
      ReturnFalse;
   if(!cSeasonNorm.CalcHiddenGradients(cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cSeasonTransp.CalcHiddenGradients(cSeasonNorm.AsObject()))
      ReturnFalse;
//--- Long
   if(!MatMulGrad(cLongMeanSTDevTransp.getOutput(), cLongMeanSTDevTransp.getGradient(),
                  cLongMeanExtrapolate.getPrevOutput(), cLongMeanExtrapolate.getGradient(),
                  cLongMeanExtrapolate.getGradient(), 1, 1, iCount, iWindow, true))
      ReturnFalse;
   if(!cLongNorm.GetMeanSTDevs().CalcHiddenGradients(cLongMeanSTDevTransp.AsObject()))
      ReturnFalse;
   if(!cLongNorm.CalcHiddenGradients(cSeasonTransp.AsObject()))
      ReturnFalse;
   temp = cLongNorm.getGradient();
   if(!cLongNorm.SetGradient(cLongNorm.getPrevOutput(), false) ||
      !cLongNorm.CalcHiddenGradients(cLongExtrapolate.AsObject()) ||
      !SumAndNormalize(temp, cLongNorm.getGradient(), temp, iWindow, false, 0, 0, 0, 1) ||
      !cLongNorm.SetGradient(temp, false))
      ReturnFalse;
//---
   if(!NeuronOCL.CalcHiddenGradients(cLongNorm.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
//--- Long
   if(!cLongNorm.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cLongExtrapolate.UpdateInputWeights(cLongNorm.AsObject()))
      ReturnFalse;
//--- Season
   if(!cSeasonNorm.UpdateInputWeights(cSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cSeasonExtrapolate.UpdateInputWeights(cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cSeasonMeanExtrapolate.UpdateInputWeights(cSeasonNorm.GetMeans()))
      ReturnFalse;
//--- Short
   if(!cShortNorm.UpdateInputWeights(cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!cShortExtrapolate.UpdateInputWeights(cShortNorm.AsObject()))
      ReturnFalse;
   if(!cShortMeanExtrapolate.UpdateInputWeights(cShortNorm.GetMeans()))
      ReturnFalse;
//--- Spatial
   if(!cSpatialNorm.UpdateInputWeights(cShortNorm.AsObject()))
      ReturnFalse;
   if(!cSpatialExtrapolate.UpdateInputWeights(cSpatialNorm.AsObject()))
      ReturnFalse;
   if(!cSpatialMeanExtrapolate.UpdateInputWeights(cSpatialNorm.GetMeans()))
      ReturnFalse;
//--- Fusion
   if(!cFusion.UpdateInputWeights(cTranspose.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronTransposeOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronSSCNNEncoder* Source = source;
//--- Long
   if(!cLongNorm.WeightsUpdate(Source.cLongNorm.AsObject(), tau))
      ReturnFalse;
   if(!cLongExtrapolate.WeightsUpdate(Source.cLongExtrapolate.AsObject(), tau))
      ReturnFalse;
//--- Season
   if(!cSeasonNorm.WeightsUpdate(Source.cSeasonNorm.AsObject(), tau))
      ReturnFalse;
   if(!cSeasonExtrapolate.WeightsUpdate(Source.cSeasonExtrapolate.AsObject(), tau))
      ReturnFalse;
   if(!cSeasonMeanExtrapolate.WeightsUpdate(Source.cSeasonMeanExtrapolate.AsObject(), tau))
      ReturnFalse;
//--- Short
   if(!cShortNorm.WeightsUpdate(Source.cShortNorm.AsObject(), tau))
      ReturnFalse;
   if(!cShortExtrapolate.WeightsUpdate(Source.cShortExtrapolate.AsObject(), tau))
      ReturnFalse;
   if(!cShortMeanExtrapolate.WeightsUpdate(Source.cShortMeanExtrapolate.AsObject(), tau))
      ReturnFalse;
//--- Spatial
   if(!cSpatialNorm.WeightsUpdate(Source.cSpatialNorm.AsObject(), tau))
      ReturnFalse;
   if(!cSpatialExtrapolate.WeightsUpdate(Source.cSpatialExtrapolate.AsObject(), tau))
      ReturnFalse;
   if(!cSpatialMeanExtrapolate.WeightsUpdate(Source.cSpatialMeanExtrapolate.AsObject(), tau))
      ReturnFalse;
//--- Fusion
   if(!cFusion.WeightsUpdate(Source.cFusion.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::Save(const int file_handle)
  {
   if(!CNeuronTransposeOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cLongNorm.Save(file_handle))
      ReturnFalse;
   if(!cLongExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cLongMeanSTDevTransp.Save(file_handle))
      ReturnFalse;
   if(!cLongMeanExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cSeasonTransp.Save(file_handle))
      ReturnFalse;
   if(!cSeasonNorm.Save(file_handle))
      ReturnFalse;
   if(!cUnSeasonTransp.Save(file_handle))
      ReturnFalse;
   if(!cSeasonExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cSeasonMeanExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cShortNorm.Save(file_handle))
      ReturnFalse;
   if(!cShortExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cShortMeanExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cSpatialNorm.Save(file_handle))
      ReturnFalse;
   if(!cSpatialExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cSpatialMeanExtrapolate.Save(file_handle))
      ReturnFalse;
   if(!cConcatenated.Save(file_handle))
      ReturnFalse;
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
   if(!cFusion.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNNEncoder::Load(const int file_handle)
  {
   if(!CNeuronTransposeOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cLongNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cLongExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cLongMeanSTDevTransp.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cLongMeanExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSeasonTransp.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSeasonNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cUnSeasonTransp.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSeasonExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSeasonMeanExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cShortNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cShortExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cShortMeanExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSpatialNorm.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSpatialExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSpatialMeanExtrapolate.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cConcatenated.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cFusion.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSSCNNEncoder::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronTransposeOCL::SetOpenCL(obj);
//---
   cLongNorm.SetOpenCL(OpenCL);
   cLongExtrapolate.SetOpenCL(OpenCL);
   cLongMeanSTDevTransp.SetOpenCL(OpenCL);
   cLongMeanExtrapolate.SetOpenCL(OpenCL);
   cSeasonTransp.SetOpenCL(OpenCL);
   cSeasonNorm.SetOpenCL(OpenCL);
   cUnSeasonTransp.SetOpenCL(OpenCL);
   cSeasonExtrapolate.SetOpenCL(OpenCL);
   cSeasonMeanExtrapolate.SetOpenCL(OpenCL);
   cShortNorm.SetOpenCL(OpenCL);
   cShortExtrapolate.SetOpenCL(OpenCL);
   cShortMeanExtrapolate.SetOpenCL(OpenCL);
   cSpatialNorm.SetOpenCL(OpenCL);
   cSpatialExtrapolate.SetOpenCL(OpenCL);
   cSpatialMeanExtrapolate.SetOpenCL(OpenCL);
   cConcatenated.SetOpenCL(OpenCL);
   cTranspose.SetOpenCL(OpenCL);
   cFusion.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSSCNNEncoder::TrainMode(bool flag)
  {
   CNeuronTransposeOCL::TrainMode(flag);
//---
   cLongNorm.TrainMode(bTrain);
   cLongExtrapolate.TrainMode(bTrain);
   cLongMeanSTDevTransp.TrainMode(bTrain);
   cLongMeanExtrapolate.TrainMode(bTrain);
   cSeasonTransp.TrainMode(bTrain);
   cSeasonNorm.TrainMode(bTrain);
   cUnSeasonTransp.TrainMode(bTrain);
   cSeasonExtrapolate.TrainMode(bTrain);
   cSeasonMeanExtrapolate.TrainMode(bTrain);
   cShortNorm.TrainMode(bTrain);
   cShortExtrapolate.TrainMode(bTrain);
   cShortMeanExtrapolate.TrainMode(bTrain);
   cSpatialNorm.TrainMode(bTrain);
   cSpatialExtrapolate.TrainMode(bTrain);
   cSpatialMeanExtrapolate.TrainMode(bTrain);
   cConcatenated.TrainMode(bTrain);
   cTranspose.TrainMode(bTrain);
   cFusion.TrainMode(bTrain);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNN::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                        uint units_count, uint variables, uint forecast,
                        uint season_period, uint short_period, uint layers,
                        ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, (units_count + forecast)*variables,
                            optimization_type, batch))
      ReturnFalse;
   SetActivationFunction(None);
//---
   cLayers.Clear();
   cLayers.SetOpenCL(OpenCL);
   CNeuronSSCNNEncoder* encoder = NULL;
   CNeuronBaseOCL* residual = NULL;
   for(uint l = 0; l < layers; l++)
     {
      encoder = new CNeuronSSCNNEncoder();
      if(!encoder)
         ReturnFalse;
      if(!encoder.Init(0, 2 * l, OpenCL, units_count, variables, forecast, season_period,
                       short_period, optimization, iBatch) ||
         !cLayers.Add(encoder))
         ReturnFalse;
      encoder.SetActivationFunction(None);
      if((l + 1) == layers)
         break;
      //---
      residual = new CNeuronBaseOCL();
      if(!residual)
         ReturnFalse;
      if(!residual.Init(0, 2 * l + 1, OpenCL, units_count * variables, optimization, iBatch) ||
         !cLayers.Add(residual))
         ReturnFalse;
      residual.SetActivationFunction(None);
     }
   if(!SetGradient(encoder.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNN::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!Output.Fill(0))
      ReturnFalse;
   CNeuronBaseOCL* inputs = NeuronOCL;
   CNeuronSSCNNEncoder* current = NULL;
   CNeuronBaseOCL* residual = NULL;
   CNeuronBaseOCL* temp = NULL;
   int layers = cLayers.Total();
//---
   for(int l = 0; l < layers; l += 2)
     {
      current = cLayers[l];
      if(!current ||
         !current.FeedForward(inputs) ||
         !SumAndNormalize(Output, current.getOutput(), Output, current.GetCount(), false, 0, 0, 0, 1))
         ReturnFalse;
      if((l + 1) == layers)
         break;
      uint variables = current.GetWindow();
      uint dimension = inputs.Neurons() / variables;
      uint forecast = current.GetCount() - dimension;
      residual = cLayers[l + 1];
      if(!residual)
         ReturnFalse;
      if(!DeConcat(residual.getOutput(), current.getPrevOutput(), current.getOutput(),
                   dimension, forecast, variables) ||
         !SumAndNormalize(residual.getOutput(), inputs.getOutput(), residual.getOutput(),
                          dimension, true, 0, 0, 0, 1))
         ReturnFalse;
      inputs = residual;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSSCNN::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!PrevOutput.Fill(0))
      ReturnFalse;
//---
   CNeuronBaseOCL* inputs = NULL;
   CNeuronSSCNNEncoder* current = cLayers[-1];
   CNeuronBaseOCL* residual = NULL;
   int layers = cLayers.Total() - 2;
//---
   for(int l = layers; l >= 0; l--)
      switch(cLayers[l].Type())
        {
         case defNeuronBaseOCL:
            inputs = cLayers[l];
            if(!inputs ||
               !inputs.CalcHiddenGradients(current))
               ReturnFalse;
            if(!!residual)
               if(!SumAndNormalize(inputs.getGradient(), residual.getGradient(), inputs.getGradient(),
                                   current.GetWindow(), false, 0, 0, 0, 1))
                  ReturnFalse;
            residual = inputs;
            break;
         case defNeuronSSCNNEncoder:
            current = cLayers[l];
            if(!residual)
               break;
            inputs = cLayers[l + 2];
            if(!inputs)
               ReturnFalse;
            if(!Concat(residual.getGradient(), PrevOutput, current.getGradient(),
                       residual.Neurons() / current.GetWindow(),
                       current.GetCount() - residual.Neurons() / current.GetWindow(),
                       current.GetWindow()) ||
               !SumAndNormalize(current.getGradient(), inputs.getGradient(), current.getGradient(),
                                current.GetWindow(), false, 0, 0, 0, 1))
               ReturnFalse;
            break;
         default:
            ReturnFalse;
            break;
        }
//---
   if(!NeuronOCL.CalcHiddenGradients(current))
      ReturnFalse;
   if(!!residual)
      if(!SumAndNormalize(NeuronOCL.getGradient(), residual.getGradient(), NeuronOCL.getGradient(),
                          current.GetWindow(), false, 0, 0, 0, 1))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CChebPolinom::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint dimension,
                        uint steps, ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, dimension * dimension * steps, optimization_type, batch))
      ReturnFalse;
//---
   activation = None;
   iDimension = dimension;
   iSteps = steps;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CChebPolinom::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL || NeuronOCL.Neurons() != (iDimension * iDimension))
      ReturnFalse;
//---
   uint global_work_offset[3] = { 0 };
   uint global_work_size[3] = { MathMin(iDimension, uint(OpenCL.GetMaxLocalSize(0))),
                                iDimension, iDimension
                              };
   uint local_work_size[3] = { global_work_size[0], 1, 1 };
//---
   uint kernel = def_k_ChebStep;
   setBuffer(kernel, def_k_cheb_support, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_cheb_outputs, getOutputIndex())
   for(int step = MathMin(2, MathMax(int(iSteps) - 1, 0)); step < int(iSteps); step++)
     {
      setArgument(kernel, def_k_cheb_step, step + 1)
      kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
      if(!Output.BufferRead())
         ReturnFalse;
#endif
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CChebPolinom::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL || NeuronOCL.Neurons() != (iDimension * iDimension))
      ReturnFalse;
//---
   uint global_work_offset[3] = { 0 };
   uint global_work_size[3] = { MathMin(iDimension, uint(OpenCL.GetMaxLocalSize(0))),
                                iDimension, iDimension
                              };
   uint local_work_size[3] = { global_work_size[0], 1, 1 };
//---
   uint kernel = def_k_ChebStepGrad;
   setBuffer(kernel, def_k_chebgr_support, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_chebgr_support_g, NeuronOCL.getGradientIndex())
   setBuffer(kernel, def_k_chebgr_outputs, getOutputIndex())
   setBuffer(kernel, def_k_chebgr_outputs_g, getGradientIndex())
   for(int step = (int(iSteps) - 1); step >= MathMax(MathMin(2, int(iSteps) - 1), 1); step--)
     {
      setArgument(kernel, def_k_chebgr_step, step)
      kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
      if(!NeuronOCL.getGradient().BufferRead())
         ReturnFalse;
#endif
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CChebPolinom::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
   if(FileWriteInteger(file_handle, int(iDimension)) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, int(iSteps)) < INT_VALUE)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CChebPolinom::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iDimension = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iSteps = (uint)FileReadInteger(file_handle);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGrapConv::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                 uint count, uint window, uint cheb_k,
                                 ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronTransposeRCDOCL::Init(numOutputs, myIndex, open_cl, cheb_k,
                                    count, window, optimization_type, batch))
      ReturnFalse;
   activation = None;
   if(!cX_G.Init(0, 0, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
   cX_G.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGrapConv::FeedForward(CNeuronBaseOCL *NeuronOCL,
                                        CChebPolinom *Support)
  {
   if(!NeuronOCL || !Support)
      ReturnFalse;
   if(Support.GetDimension() != iWindow ||
      Support.GetSteps() != iCount)
      ReturnFalse;
   if(NeuronOCL.Neurons() != (Neurons() / iCount))
      ReturnFalse;
//---
   if(!MatMul(Support.getOutput(), NeuronOCL.getOutput(), cX_G.getOutput(),
              iWindow, iWindow, GetDimension(), iCount, false))
      ReturnFalse;
//---
   return CNeuronTransposeRCDOCL::feedForward(cX_G.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGrapConv::calcInputGradients(CNeuronBaseOCL *NeuronOCL,
      CChebPolinom *Support)
  {
   if(!NeuronOCL || !Support)
      ReturnFalse;
   if(Support.GetDimension() != iWindow ||
      Support.GetSteps() != iCount)
      ReturnFalse;
   if(NeuronOCL.Neurons() != (Neurons() / iCount))
      ReturnFalse;
//---
   if(!CNeuronTransposeRCDOCL::calcInputGradients(cX_G.AsObject()))
      ReturnFalse;
   if(!MatMulGrad(Support.getOutput(), Support.getGradient(),
                  NeuronOCL.getOutput(), NeuronOCL.getGradient(),
                  cX_G.getGradient(), iWindow, iWindow,
                  GetDimension(), iCount, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGrapConv::Load(const int file_handle)
  {
   if(!CNeuronTransposeRCDOCL::Load(file_handle))
      ReturnFalse;
   if(!cX_G.Init(0, 0, OpenCL, Neurons(), optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetGrapConv::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronTransposeRCDOCL::SetOpenCL(obj);
   cX_G.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                             uint units, uint window, uint window_out,
                             uint cheb_k, uint embed_dim,
                             ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units * window_out, optimization_type, batch))
      ReturnFalse;
   activation = None;
//---
   int index = 0;
   if(!cInpAndHidden.Init(0, index, OpenCL, (window + window_out)*units, optimization, iBatch))
      ReturnFalse;
   cInpAndHidden.SetActivationFunction(None);
   index++;
   if(!cZ_R.Init(0, index, OpenCL, units, window + window_out, cheb_k, optimization, iBatch))
      ReturnFalse;
   cZ_R.SetActivationFunction(None);
   index++;
   if(!cZ_R_emb.Init(0, index, OpenCL, embed_dim, embed_dim,
                     2 * cheb_k * (window + window_out) * window_out,
                     units, 1, optimization, iBatch))
      ReturnFalse;
   cZ_R_emb.SetActivationFunction(None);
   index++;
   if(!cZe_Re.Init(0, index, OpenCL, 2 * window_out * units, optimization, iBatch))
      ReturnFalse;
   cZe_Re.SetActivationFunction(None);
   index++;
   if(!cZ.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   cZ.SetActivationFunction(None);
   index++;
   if(!cR.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   cR.SetActivationFunction(None);
   index++;
   if(!cCandidate.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   cCandidate.SetActivationFunction(None);
   index++;
   if(!cHC.Init(0, index, OpenCL, units, window + window_out, cheb_k, optimization, iBatch))
      ReturnFalse;
   cHC.SetActivationFunction(None);
   index++;
   if(!cHC_emb.Init(0, index, OpenCL, embed_dim, embed_dim,
                    (window + window_out) * window_out * cheb_k,
                    units, 1, optimization, iBatch))
      ReturnFalse;
   cHC_emb.SetActivationFunction(None);
   index++;
   if(!cHCe.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   cHCe.SetActivationFunction(None);
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   if(!bSupportAccum.BufferInit(units * units * cheb_k, 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   if(!Output.Fill(0))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::feedForward(CNeuronBaseOCL *NeuronOCL,
                                    CChebPolinom *Support,
                                    CNeuronBaseOCL *Embedding)
  {
   if(!NeuronOCL || !Support || !Embedding)
      ReturnFalse;
   if(!SwapOutputs())
      ReturnFalse;
//---
   uint cheb_k = cZ_R.GetChebK();
   uint units = cZ_R.GetCount();
   uint window_out = Neurons() / units;
   uint window = cZ_R.GetWindow() - window_out;
//---
   if(!Concat(NeuronOCL.getOutput(), PrevOutput, cInpAndHidden.getOutput(), window, window_out, units))
      ReturnFalse;
   if(!cZ_R.FeedForward(cInpAndHidden.AsObject(), Support))
      ReturnFalse;
   if(!cZ_R_emb.FeedForward(Embedding))
      ReturnFalse;
   if(!MatMul(cZ_R.getOutput(), cZ_R_emb.getOutput(), cZe_Re.getOutput(), 1, (window + window_out)*cheb_k,
              2 * window_out, units, true))
      ReturnFalse;
   if(!Activation(cZe_Re.getOutput(), cZe_Re.getOutput(), SIGMOID))
      ReturnFalse;
   if(!DeConcat(cZ.getOutput(), cR.getOutput(), cZe_Re.getOutput(), window_out, window_out, units))
      ReturnFalse;
   if(!ElementMult(cZ.getOutput(), PrevOutput, cZ.getPrevOutput()))
      ReturnFalse;
   if(!Concat(NeuronOCL.getOutput(), cZ.getPrevOutput(), cCandidate.getOutput(), window, window_out, units))
      ReturnFalse;
   if(!cHC.FeedForward(cCandidate.AsObject(), Support))
      ReturnFalse;
   if(!cHC_emb.FeedForward(Embedding))
      ReturnFalse;
   if(!MatMul(cHC.getOutput(), cHC_emb.getOutput(), cHCe.getOutput(), 1, (window + window_out)*cheb_k,
              window_out, units, true))
      ReturnFalse;
   if(!Activation(cHCe.getOutput(), cHCe.getOutput(), TANH))
      ReturnFalse;
   if(!GateElementMult(PrevOutput, cHCe.getOutput(), cR.getOutput(), Output))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::calcInputGradients(CNeuronBaseOCL *NeuronOCL,
      CChebPolinom *Support,
      CNeuronBaseOCL *Embedding)
  {
   if(!NeuronOCL || !Support || !Embedding)
      ReturnFalse;
//---
   uint cheb_k = cZ_R.GetChebK();
   uint units = cZ_R.GetCount();
   uint window_out = Neurons() / units;
   uint window = cZ_R.GetWindow() - window_out;
//---
   if(!GateElementMultGrad(PrevOutput, cR.getPrevOutput(),
                           cHCe.getOutput(), cHCe.getGradient(),
                           cR.getOutput(), cR.getGradient(),
                           Gradient, None, TANH, cR.Activation()))
      ReturnFalse;
   if(!MatMulGrad(cHC.getOutput(), cHC.getGradient(),
                  cHC_emb.getOutput(), cHC_emb.getGradient(),
                  cHCe.getGradient(), 1, (window + window_out)*cheb_k,
                  window_out, units, true))
      ReturnFalse;
   if(!cHC.calcInputGradients(cCandidate.AsObject(), Support))
      ReturnFalse;
   if(!DeConcat(NeuronOCL.getGradient(), cZ.getPrevOutput(), cCandidate.getGradient(),
                window, window_out, units))
      ReturnFalse;
   if(!ElementMultGrad(cZ.getOutput(), cZ.getGradient(),
                       PrevOutput, cCandidate.getPrevOutput(),
                       cZ.getPrevOutput(), None, None))
      ReturnFalse;
   if(!Concat(cZ.getGradient(), cR.getGradient(), cZe_Re.getGradient(),
              window_out, window_out, units))
      ReturnFalse;
   if(!DeActivation(cZe_Re.getOutput(), cZe_Re.getGradient(), cZe_Re.getGradient(), SIGMOID))
      ReturnFalse;
   if(!MatMulGrad(cZ_R.getOutput(), cZ_R.getGradient(),
                  cZ_R_emb.getOutput(), cZ_R_emb.getGradient(),
                  cZe_Re.getGradient(), 1, (window + window_out)*cheb_k,
                  2 * window_out, units, true))
      ReturnFalse;
   CBufferFloat* temp = Support.getGradient();
   if(!Support.SetGradient(GetPointer(bSupportAccum), false) ||
      !cZ_R.calcInputGradients(cInpAndHidden.AsObject(), Support) ||
      !SumAndNormalize(temp, Support.getGradient(), temp, Support.GetDimension(), false, 0, 0, 0, 1) ||
      !Support.SetGradient(temp, false))
      ReturnFalse;
   if(!DeConcat(cInpAndHidden.getPrevOutput(), cCandidate.getPrevOutput(), cInpAndHidden.getGradient(), window, window_out, units))
      ReturnFalse;
   if(!SumAndNormalize(NeuronOCL.getGradient(), cInpAndHidden.getPrevOutput(), NeuronOCL.getGradient(), window, false, 0, 0, 0, 1))
      ReturnFalse;
   if(NeuronOCL.Activation() != None)
      if(!DeActivation(NeuronOCL.getOutput(), NeuronOCL.getGradient(), NeuronOCL.getGradient(), NeuronOCL.Activation()))
         ReturnFalse;
//---
   if(!Embedding.CalcHiddenGradients(cHC_emb.AsObject()))
      ReturnFalse;
   temp = Embedding.getGradient();
   if(!Embedding.SetGradient(Embedding.getPrevOutput(), false) ||
      !Embedding.CalcHiddenGradients(cZ_R_emb.AsObject()) ||
      !SumAndNormalize(temp, Embedding.getGradient(), temp, cZ_R_emb.GetWindow(), false, 0, 0, 0, 1) ||
      !Embedding.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::updateInputWeights(CNeuronBaseOCL *NeuronOCL,
      CChebPolinom *Support,
      CNeuronBaseOCL *Embedding)
  {
   if(!Embedding)
      ReturnFalse;
//---
   if(!cZ_R_emb.UpdateInputWeights(Embedding))
      ReturnFalse;
   if(!cHC_emb.UpdateInputWeights(Embedding))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronHimNetGCRU* Source = source;
   if(!cZ_R_emb.WeightsUpdate(Source.cZ_R_emb.AsObject(), tau))
      ReturnFalse;
   if(!cHC_emb.WeightsUpdate(Source.cHC_emb.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cZ_R.Save(file_handle))
      ReturnFalse;
   if(!cZ_R_emb.Save(file_handle))
      ReturnFalse;
   if(!cHC.Save(file_handle))
      ReturnFalse;
   if(!cHC_emb.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetGCRU::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cZ_R.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cZ_R_emb.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHC.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHC_emb.AsObject()))
      ReturnFalse;
//---
   uint cheb_k = cZ_R.GetChebK();
   uint units = cZ_R.GetCount();
   uint window_out = Neurons() / units;
   uint window = cZ_R.GetWindow() - window_out;
//---
   int index = 0;
   if(!cInpAndHidden.Init(0, index, OpenCL, (window + window_out)*units, optimization, iBatch))
      ReturnFalse;
   cInpAndHidden.SetActivationFunction(None);
   index += 3;
   if(!cZe_Re.Init(0, index, OpenCL, 2 * window_out * units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cZ.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   cZ.SetActivationFunction(None);
   index++;
   if(!cR.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   cR.SetActivationFunction(None);
   index++;
   if(!cCandidate.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   cCandidate.SetActivationFunction(None);
   index += 3;
   if(!cHCe.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   cHCe.SetActivationFunction(None);
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   if(!bSupportAccum.BufferInit(units * units * cheb_k, 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   if(!Output.Fill(0))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetGCRU::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
//---
   cZ_R.SetOpenCL(OpenCL);
   cZ_R_emb.SetOpenCL(OpenCL);
   cHC.SetOpenCL(OpenCL);
   cHC_emb.SetOpenCL(OpenCL);
   cInpAndHidden.SetOpenCL(OpenCL);
   cZe_Re.SetOpenCL(OpenCL);
   cZ.SetOpenCL(OpenCL);
   cR.SetOpenCL(OpenCL);
   cCandidate.SetOpenCL(OpenCL);
   cHCe.SetOpenCL(OpenCL);
//---
   bSupportAccum.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                    uint units, uint window, uint window_out, uint cheb_k,
                                    uint layers, uint embed_dim, uint period1,
                                    uint timeframe1, uint period2, uint timeframe2,
                                    ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(layers <= 0)
      ReturnFalse;
//---
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_out * units, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!caEmbeddings[0].Init(0, index, OpenCL, embed_dim, period1, optimization, iBatch))
      ReturnFalse;
   aTimeframes[0] = MathMax(1, timeframe1);
   index++;
   if(!caEmbeddings[1].Init(0, index, OpenCL, embed_dim, period2, optimization, iBatch))
      ReturnFalse;
   aTimeframes[1] = MathMax(1, timeframe2);
   if(!cConcatEmbeddings.Init(numOutputs, myIndex, open_cl, 2 * embed_dim, optimization_type, batch))
      ReturnFalse;
//---
   cGRCUs.Clear();
   cGRCUs.SetOpenCL(OpenCL);
   index++;
   CNeuronHimNetGCRU *temp = new CNeuronHimNetGCRU();
   if(!temp ||
      !temp.Init(0, index, OpenCL, units, window, window_out, cheb_k, 2 * embed_dim, optimization, iBatch) ||
      !cGRCUs.Add(temp))
      ReturnFalse;
   for(uint i = 1; i < layers; i++)
     {
      index++;
      temp = new CNeuronHimNetGCRU();
      if(!temp ||
         !temp.Init(0, index, OpenCL, units, window_out, window_out, cheb_k, 2 * embed_dim, optimization, iBatch) ||
         !cGRCUs.Add(temp))
         ReturnFalse;
     }
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   if(!bSupportAccum.BufferInit(units * units * cheb_k, 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   SetActivationFunction((ENUM_ACTIVATION)temp.Activation());
   if(!SetOutput(temp.getOutput(), true) ||
      !SetGradient(temp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::feedForward(CNeuronBaseOCL *NeuronOCL, CChebPolinom *Support, int label)
  {
   for(uint i = 0; i < caEmbeddings.Size(); i++)
     {
      int position = (label / int(aTimeframes[i])) % caEmbeddings[i].GetPeriod();
      if(!caEmbeddings[i].SetPosition(position) ||
         !caEmbeddings[i].FeedForward())
         ReturnFalse;
     }
   if(!Concat(caEmbeddings[0].getOutput(), caEmbeddings[1].getOutput(),
              cConcatEmbeddings.getOutput(), 1, 1, caEmbeddings[0].Neurons()))
      ReturnFalse;
//---
   CNeuronBaseOCL *inputs = NeuronOCL;
   CNeuronHimNetGCRU *current = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      current = cGRCUs[i];
      if(!current ||
         !current.feedForward(inputs, Support, cConcatEmbeddings.AsObject()))
         ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::calcInputGradients(CNeuronBaseOCL *NeuronOCL, CChebPolinom *Support)
  {
   if(!NeuronOCL || !Support)
      ReturnFalse;
//---
   CNeuronBaseOCL *inputs = (cGRCUs.Total() > 1 ? cGRCUs[-2] : NeuronOCL);
   CNeuronHimNetGCRU* current = cGRCUs[-1];
   if(!current ||
      !current.calcInputGradients(inputs, Support, cConcatEmbeddings.AsObject()))
      ReturnFalse;
   if(!DeConcat(caEmbeddings[0].getGradient(), caEmbeddings[1].getGradient(),
                cConcatEmbeddings.getGradient(), 1, 1, caEmbeddings[0].Neurons()))
      ReturnFalse;
   if(cGRCUs.Total() > 1)
     {
      CBufferFloat *temp = Support.getGradient();
      if(!Support.SetGradient(GetPointer(bSupportAccum), false))
         ReturnFalse;
      for(int i = cGRCUs.Total() - 2; i >= 0; i--)
        {
         current = cGRCUs[i];
         inputs = (i > 0 ? cGRCUs[i - 1] : NeuronOCL);
         if(!current ||
            !current.calcInputGradients(inputs, Support, cConcatEmbeddings.AsObject()))
            ReturnFalse;
         if(!SumAndNormalize(temp, Support.getGradient(), temp, 1, false, 0, 0, 0, 1))
            ReturnFalse;
         if(!DeConcat(caEmbeddings[0].getPrevOutput(), caEmbeddings[1].getPrevOutput(),
                      cConcatEmbeddings.getGradient(), 1, 1, caEmbeddings[0].Neurons()))
            ReturnFalse;
         for(uint i = 0; i < caEmbeddings.Size(); i++)
            if(!SumAndNormalize(caEmbeddings[i].getGradient(), caEmbeddings[i].getPrevOutput(),
                                caEmbeddings[i].getGradient(), 1, false, 0, 0, 0, 1))
               ReturnFalse;
        }
      if(!Support.SetGradient(temp, false))
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL, CChebPolinom *Support)
  {
   for(uint i = 0; i < caEmbeddings.Size(); i++)
      if(!caEmbeddings[i].UpdateInputWeights())
         ReturnFalse;
//---
   CNeuronBaseOCL* inputs = NeuronOCL;
   CNeuronHimNetGCRU* current = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      current = cGRCUs[i];
      if(!current.updateInputWeights(inputs, Support, cConcatEmbeddings.AsObject()))
         ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronHimNetTempEncoder* Source = source;
   for(uint i = 0; i < caEmbeddings.Size(); i++)
      if(!caEmbeddings[i].WeightsUpdate(Source.caEmbeddings[i].AsObject(), tau))
         ReturnFalse;
   if(!cGRCUs.WeightsUpdate(Source.cGRCUs.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   for(uint i = 0; i < caEmbeddings.Size(); i++)
     {
      if(!caEmbeddings[i].Save(file_handle))
         ReturnFalse;
      if(FileWriteInteger(file_handle, int(aTimeframes[i])) < INT_VALUE)
         ReturnFalse;
     }
   if(!cGRCUs.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   for(uint i = 0; i < caEmbeddings.Size(); i++)
     {
      if(!LoadInsideLayer(file_handle, caEmbeddings[i].AsObject()))
         ReturnFalse;
      if(FileIsEnding(file_handle))
         ReturnFalse;
      aTimeframes[i] = (uint)FileReadInteger(file_handle);
     }
   if(!cGRCUs.Load(file_handle))
      ReturnFalse;
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   CNeuronHimNetGCRU* temp = cGRCUs[-1];
   uint units = temp.GetCount();
   uint cheb_k = temp.GetChebK();
   if(!bSupportAccum.BufferInit(units * units * cheb_k, 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   SetActivationFunction((ENUM_ACTIVATION)temp.Activation());
   if(!SetOutput(temp.getOutput(), true) ||
      !SetGradient(temp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetTempEncoder::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   for(uint i = 0; i < caEmbeddings.Size(); i++)
      caEmbeddings[i].SetOpenCL(OpenCL);
   cGRCUs.SetOpenCL(OpenCL);
//---
   bSupportAccum.BufferFree();
   bSupportAccum.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::Clear(void)
  {
   CNeuronBaseOCL* temp = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      temp = cGRCUs[i];
      if(!temp ||
         !temp.Clear())
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetTempEncoder::GetCount(void) const
  {
   if(cGRCUs.Total() <= 0)
      return 0;
   CNeuronHimNetGCRU* temp = cGRCUs[0];
   if(!temp)
      return 0;
//---
   return temp.GetCount();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetTempEncoder::GetChebK(void) const
  {
   if(cGRCUs.Total() <= 0)
      return 0;
   CNeuronHimNetGCRU* temp = cGRCUs[0];
   if(!temp)
      return 0;
//---
   return temp.GetChebK();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetTempEncoder::GetWindowIn(void) const
  {
   if(cGRCUs.Total() <= 0)
      return 0;
   CNeuronHimNetGCRU* temp = cGRCUs[0];
   if(!temp)
      return 0;
//---
   return temp.GetWindowIn();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetTempEncoder::GetWindowOut(void) const
  {
   uint count = GetCount();
   if(count <= 0)
      return 0;
//---
   return Neurons() / count;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                    uint units, uint window, uint window_out, uint cheb_k,
                                    uint layers, uint embed_dim,
                                    ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(layers <= 0)
      ReturnFalse;
//---
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_out * units, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cEmbedding.Init(0, index, OpenCL, embed_dim, optimization, iBatch))
      ReturnFalse;
//---
   cGRCUs.Clear();
   cGRCUs.SetOpenCL(OpenCL);
   index++;
   CNeuronHimNetGCRU *temp = new CNeuronHimNetGCRU();
   if(!temp ||
      !temp.Init(0, index, OpenCL, units, window, window_out, cheb_k, embed_dim, optimization, iBatch) ||
      !cGRCUs.Add(temp))
     {
      DeleteObj(temp);
      ReturnFalse;
     }
   for(uint i = 1; i < layers; i++)
     {
      index++;
      temp = new CNeuronHimNetGCRU();
      if(!temp ||
         !temp.Init(0, index, OpenCL, units, window_out, window_out, cheb_k, 2 * embed_dim, optimization, iBatch) ||
         !cGRCUs.Add(temp))
        {
         DeleteObj(temp);
         ReturnFalse;
        }
     }
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   if(!bSupportAccum.BufferInit(units * units * cheb_k, 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
   bEmbeddingAccum.BufferFree();
   bEmbeddingAccum.Clear();
   if(!bEmbeddingAccum.BufferInit(cEmbedding.Neurons(), 0) ||
      !bEmbeddingAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   SetActivationFunction((ENUM_ACTIVATION)temp.Activation());
   if(!SetOutput(temp.getOutput(), true) ||
      !SetGradient(temp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::feedForward(CNeuronBaseOCL *NeuronOCL, CChebPolinom *Support)
  {
   if(bTrain)
      if(!cEmbedding.FeedForward())
         ReturnFalse;
//---
   CNeuronBaseOCL *inputs = NeuronOCL;
   CNeuronHimNetGCRU *current = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      current = cGRCUs[i];
      if(!current ||
         !current.feedForward(inputs, Support, cEmbedding.AsObject()))
         ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::calcInputGradients(CNeuronBaseOCL *NeuronOCL, CChebPolinom *Support)
  {
   if(!NeuronOCL || !Support)
      ReturnFalse;
//---
   CNeuronBaseOCL *inputs = (cGRCUs.Total() > 1 ? cGRCUs[-2] : NeuronOCL);
   CNeuronHimNetGCRU* current = cGRCUs[-1];
   if(!current ||
      !current.calcInputGradients(inputs, Support, cEmbedding.AsObject()))
      ReturnFalse;
   if(cGRCUs.Total() > 1)
     {
      CBufferFloat *sup = Support.getGradient();
      if(!Support.SetGradient(GetPointer(bSupportAccum), false))
         ReturnFalse;
      CBufferFloat *emb = cEmbedding.getGradient();
      if(!cEmbedding.SetGradient(GetPointer(bEmbeddingAccum), false))
         ReturnFalse;
      for(int i = cGRCUs.Total() - 2; i >= 0; i--)
        {
         current = cGRCUs[i];
         inputs = (i > 0 ? cGRCUs[i - 1] : NeuronOCL);
         if(!current ||
            !current.calcInputGradients(inputs, Support, cEmbedding.AsObject()))
            ReturnFalse;
         if(!SumAndNormalize(sup, Support.getGradient(), sup, 1, false, 0, 0, 0, 1))
            ReturnFalse;
         if(!SumAndNormalize(cEmbedding.getGradient(), emb, emb, 1, false, 0, 0, 0, 1))
            ReturnFalse;
        }
      if(!Support.SetGradient(sup, false))
         ReturnFalse;
      if(!cEmbedding.SetGradient(emb, false))
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL, CChebPolinom *Support)
  {
   if(!cEmbedding.UpdateInputWeights())
      ReturnFalse;
//---
   CNeuronBaseOCL* inputs = NeuronOCL;
   CNeuronHimNetGCRU* current = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      current = cGRCUs[i];
      if(!current.updateInputWeights(inputs, Support, cEmbedding.AsObject()))
         ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronHimNetSpatEncoder* Source = source;
   if(!cEmbedding.WeightsUpdate(Source.cEmbedding.AsObject(), tau))
      ReturnFalse;
   if(!cGRCUs.WeightsUpdate(Source.cGRCUs.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cEmbedding.Save(file_handle))
      ReturnFalse;
   if(!cGRCUs.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cEmbedding.AsObject()))
      ReturnFalse;
   if(!cGRCUs.Load(file_handle))
      ReturnFalse;
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   CNeuronHimNetGCRU* temp = cGRCUs[-1];
   uint units = temp.GetCount();
   uint cheb_k = temp.GetChebK();
   if(!bSupportAccum.BufferInit(units * units * cheb_k, 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
   bEmbeddingAccum.BufferFree();
   bEmbeddingAccum.Clear();
   if(!bEmbeddingAccum.BufferInit(cEmbedding.Neurons(), 0) ||
      !bEmbeddingAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   SetActivationFunction((ENUM_ACTIVATION)temp.Activation());
   if(!SetOutput(temp.getOutput(), true) ||
      !SetGradient(temp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetSpatEncoder::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cEmbedding.SetOpenCL(OpenCL);
   cGRCUs.SetOpenCL(OpenCL);
//---
   bSupportAccum.BufferFree();
   bSupportAccum.BufferCreate(OpenCL);
   bEmbeddingAccum.BufferFree();
   bEmbeddingAccum.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::Clear(void)
  {
   CNeuronBaseOCL* temp = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      temp = cGRCUs[i];
      if(!temp ||
         !temp.Clear())
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetSpatEncoder::GetCount(void) const
  {
   if(cGRCUs.Total() <= 0)
      return 0;
   CNeuronHimNetGCRU* temp = cGRCUs[0];
   if(!temp)
      return 0;
//---
   return temp.GetCount();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetSpatEncoder::GetChebK(void) const
  {
   if(cGRCUs.Total() <= 0)
      return 0;
   CNeuronHimNetGCRU* temp = cGRCUs[0];
   if(!temp)
      return 0;
//---
   return temp.GetChebK();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetSpatEncoder::GetWindowIn(void) const
  {
   if(cGRCUs.Total() <= 0)
      return 0;
   CNeuronHimNetGCRU* temp = cGRCUs[0];
   if(!temp)
      return 0;
//---
   return temp.GetWindowIn();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
uint CNeuronHimNetSpatEncoder::GetWindowOut(void) const
  {
   uint count = GetCount();
   if(count <= 0)
      return 0;
//---
   return Neurons() / count;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetSpatEncoder::SetGradient(CBufferFloat *buffer, bool delete_prev = true)
  {
   if(!CNeuronBaseOCL::SetGradient(buffer, delete_prev))
      ReturnFalse;
//---
   CNeuronBaseOCL* temp = cGRCUs[-1];
   if(!temp ||
      !temp.SetGradient(Gradient, delete_prev))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetTempEncoder::SetGradient(CBufferFloat *buffer, bool delete_prev = true)
  {
   if(!CNeuronBaseOCL::SetGradient(buffer, delete_prev))
      ReturnFalse;
//---
   CNeuronBaseOCL* temp = cGRCUs[-1];
   if(!temp ||
      !temp.SetGradient(Gradient, delete_prev))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                uint units, uint window, uint window_out, uint cheb_k,
                                uint layers, uint embed_dim, uint period1,
                                uint timeframe1, uint period2, uint timeframe2,
                                ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_out * units, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cEmbedding.Init(0, index, OpenCL, units * embed_dim, optimization, iBatch))
      ReturnFalse;
   SetActivationFunction(TANH);
   index++;
   if(!cEmbeddingT.Init(0, index, OpenCL, units, embed_dim, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cSupport.Init(0, index, OpenCL, units * units, optimization, iBatch))
      ReturnFalse;
   cSupport.SetActivationFunction(None);
   index++;
   if(!cNormSupport.Init(0, index, OpenCL, cSupport.Neurons(), optimization, iBatch))
      ReturnFalse;
   cNormSupport.SetHeads(units);
   cNormSupport.SetActivationFunction(None);
   index++;
   if(!cPolinomSupport.Init(0, index, OpenCL, units, cheb_k, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cTempEncoder.Init(0, index, OpenCL, units, window, window_out, cheb_k, layers, (embed_dim + 1) / 2, period1, timeframe1, period2, timeframe2, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cSpatEncoder.Init(0, index, OpenCL, units, window, window_out, cheb_k, layers, embed_dim, optimization, iBatch))
      ReturnFalse;
//---
   if(!SetGradient(cTempEncoder.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput)
  {
   if(!SecondInput)
      ReturnFalse;
//---
   if(bTrain)
     {
      if(!cEmbedding.FeedForward())
         ReturnFalse;
      if(!cEmbeddingT.FeedForward(cEmbedding.AsObject()))
         ReturnFalse;
      if(!MatMul(cEmbedding.getOutput(), cEmbeddingT.getOutput(), cSupport.getOutput(),
                 cEmbeddingT.GetCount(), cEmbeddingT.GetWindow(), cEmbeddingT.GetCount(), 1, false))
         ReturnFalse;
      if(!cNormSupport.FeedForward(cSupport.AsObject()))
         ReturnFalse;
      if(!cPolinomSupport.FeedForward(cNormSupport.AsObject()))
         ReturnFalse;
     }
//---
   if(!cTempEncoder.feedForward(NeuronOCL, cPolinomSupport.AsObject(), int(SecondInput[0])))
      ReturnFalse;
   if(!cSpatEncoder.feedForward(NeuronOCL, cPolinomSupport.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(cTempEncoder.getOutput(), cSpatEncoder.getOutput(), Output,
                       cTempEncoder.GetWindowOut(), false, 0, 0, 0, 1))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::calcInputGradients(CNeuronBaseOCL *prevLayer)
  {
   if(!cTempEncoder.calcInputGradients(prevLayer, cPolinomSupport.AsObject()))
      ReturnFalse;
   CBufferFloat* temp = cPolinomSupport.getGradient();
   CBufferFloat* prev = prevLayer.getGradient();
   if(!cPolinomSupport.SetGradient(cPolinomSupport.getPrevOutput(), false) ||
      !prevLayer.SetGradient(prevLayer.getPrevOutput(), false) ||
      !cSpatEncoder.calcInputGradients(prevLayer, cPolinomSupport.AsObject()) ||
      !SumAndNormalize(temp, cPolinomSupport.getGradient(), temp,
                       cSpatEncoder.GetCount(), false, 0, 0, 0, 1) ||
      !SumAndNormalize(prev, prevLayer.getGradient(), prev,
                       cSpatEncoder.GetCount(), false, 0, 0, 0, 1) ||
      !cPolinomSupport.SetGradient(temp, false) ||
      !prevLayer.SetGradient(prev, false))
      ReturnFalse;
   if(prevLayer.Activation() != None)
      if(!DeActivation(prevLayer.getOutput(), prev, prev, prevLayer.Activation()))
         ReturnFalse;
//---
   if(!cNormSupport.CalcHiddenGradients(cPolinomSupport.AsObject()))
      ReturnFalse;
   if(!cSupport.CalcHiddenGradients(cNormSupport.AsObject()))
      ReturnFalse;
   if(!MatMulGrad(cEmbedding.getOutput(), cEmbedding.getPrevOutput(),
                  cEmbeddingT.getOutput(), cEmbeddingT.getGradient(),
                  cSupport.getGradient(),
                  cEmbeddingT.GetCount(), cEmbeddingT.GetWindow(), cEmbeddingT.GetCount(), 1, false))
      ReturnFalse;
   if(!cEmbedding.CalcHiddenGradients(cEmbeddingT.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(cEmbedding.getGradient(), cEmbedding.getPrevOutput(), cEmbedding.getGradient(),
                       cEmbeddingT.GetWindow(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(cEmbedding.Activation() != None)
      if(!DeActivation(cEmbedding.getOutput(), cEmbedding.getGradient(),
                       cEmbedding.getGradient(), cEmbedding.Activation()))
         ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cEmbedding.UpdateInputWeights())
      ReturnFalse;
   if(!cTempEncoder.updateInputWeights(NeuronOCL, cPolinomSupport.AsObject()))
      ReturnFalse;
   if(!cSpatEncoder.updateInputWeights(NeuronOCL, cPolinomSupport.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronHimNetEncoder* Source = source;
   if(!cEmbedding.WeightsUpdate(Source.cEmbedding.AsObject(), tau))
      ReturnFalse;
   if(!cTempEncoder.WeightsUpdate(Source.cTempEncoder.AsObject(), tau))
      ReturnFalse;
   if(!cSpatEncoder.WeightsUpdate(Source.cSpatEncoder.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cEmbedding.Save(file_handle))
      ReturnFalse;
   if(!cEmbeddingT.Save(file_handle))
      ReturnFalse;
   if(!cSupport.Save(file_handle))
      ReturnFalse;
   if(!cNormSupport.Save(file_handle))
      ReturnFalse;
   if(!cPolinomSupport.Save(file_handle))
      ReturnFalse;
   if(!cTempEncoder.Save(file_handle))
      ReturnFalse;
   if(!cSpatEncoder.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cEmbedding.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEmbeddingT.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSupport.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cNormSupport.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cPolinomSupport.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cTempEncoder.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSpatEncoder.AsObject()))
      ReturnFalse;
//---
   if(!SetGradient(cTempEncoder.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetEncoder::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
//---
   cEmbedding.SetOpenCL(OpenCL);
   cEmbeddingT.SetOpenCL(OpenCL);
   cSupport.SetOpenCL(OpenCL);
   cNormSupport.SetOpenCL(OpenCL);
   cPolinomSupport.SetOpenCL(OpenCL);
   cTempEncoder.SetOpenCL(OpenCL);
   cSpatEncoder.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetEncoder::TrainMode(bool flag)
  {
   CNeuronBaseOCL::TrainMode(flag);
//---
   cEmbedding.TrainMode(bTrain);
   cEmbeddingT.TrainMode(bTrain);
   cSupport.TrainMode(bTrain);
   cNormSupport.TrainMode(bTrain);
   cPolinomSupport.TrainMode(bTrain);
   cTempEncoder.TrainMode(bTrain);
   cSpatEncoder.TrainMode(bTrain);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::Clear(void)
  {
   if(!CNeuronBaseOCL::Clear())
      ReturnFalse;
//---
   if(!cEmbedding.Clear())
      ReturnFalse;
   if(!cEmbeddingT.Clear())
      ReturnFalse;
   if(!cSupport.Clear())
      ReturnFalse;
   if(!cNormSupport.Clear())
      ReturnFalse;
   if(!cPolinomSupport.Clear())
      ReturnFalse;
   if(!cTempEncoder.Clear())
      ReturnFalse;
   if(!cSpatEncoder.Clear())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetEncoder::SetGradient(CBufferFloat *buffer, bool delete_prev = true)
  {
   if(!CNeuronBaseOCL::SetGradient(buffer, delete_prev))
      ReturnFalse;
//---
   if(!cTempEncoder.SetGradient(Gradient, delete_prev))
      ReturnFalse;
   if(!cSpatEncoder.SetGradient(Gradient, delete_prev))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                uint units, uint window, uint window_out,
                                uint cheb_k, uint layers, uint embed_dim,
                                ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_out * units, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cProjection.Init(0, index, OpenCL, window, window, embed_dim, units, 1, optimization, iBatch))
      ReturnFalse;
   SetActivationFunction(TANH);
   index++;
   if(!cProjectionT.Init(0, index, OpenCL, units, embed_dim, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cEmbedding.Init(0, index, OpenCL, units, units, 1, 1, embed_dim, optimization, iBatch))
      ReturnFalse;
   SetActivationFunction(SIGMOID);
   index++;
   if(!cSupport.Init(0, index, OpenCL, units * units, optimization, iBatch))
      ReturnFalse;
   cSupport.SetActivationFunction(None);
   index++;
   if(!cNormSupport.Init(0, index, OpenCL, cSupport.Neurons(), optimization, iBatch))
      ReturnFalse;
   cNormSupport.SetHeads(units);
   cNormSupport.SetActivationFunction(None);
   index++;
   if(!cPolinomSupport.Init(0, index, OpenCL, units, cheb_k, optimization, iBatch))
      ReturnFalse;
//---
   cGRCUs.Clear();
   cGRCUs.SetOpenCL(OpenCL);
   index++;
   CNeuronHimNetGCRU *temp = new CNeuronHimNetGCRU();
   if(!temp ||
      !temp.Init(0, index, OpenCL, units, window, window_out, cheb_k, embed_dim, optimization, iBatch) ||
      !cGRCUs.Add(temp))
     {
      DeleteObj(temp);
      ReturnFalse;
     }
   for(uint i = 1; i < layers; i++)
     {
      index++;
      temp = new CNeuronHimNetGCRU();
      if(!temp ||
         !temp.Init(0, index, OpenCL, units, window_out, window_out, cheb_k, 2 * embed_dim, optimization, iBatch) ||
         !cGRCUs.Add(temp))
        {
         DeleteObj(temp);
         ReturnFalse;
        }
     }
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   if(!bSupportAccum.BufferInit(cPolinomSupport.Neurons(), 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
   bEmbeddingAccum.BufferFree();
   bEmbeddingAccum.Clear();
   if(!bEmbeddingAccum.BufferInit(cEmbedding.Neurons(), 0) ||
      !bEmbeddingAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   SetActivationFunction((ENUM_ACTIVATION)temp.Activation());
   if(!SetOutput(temp.getOutput(), true) ||
      !SetGradient(temp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cProjection.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cProjectionT.FeedForward(cProjection.AsObject()))
      ReturnFalse;
   if(!MatMul(cProjection.getOutput(), cProjectionT.getOutput(), cSupport.getOutput(),
              cProjectionT.GetCount(), cProjectionT.GetWindow(), cProjectionT.GetCount(), 1, false))
      ReturnFalse;
   if(!cNormSupport.FeedForward(cSupport.AsObject()))
      ReturnFalse;
   if(!cPolinomSupport.FeedForward(cNormSupport.AsObject()))
      ReturnFalse;
//---
   if(!cEmbedding.FeedForward(cProjectionT.AsObject()))
      ReturnFalse;
//---
   CNeuronHimNetGCRU* current = NULL;
   CNeuronBaseOCL* inputs = NeuronOCL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      current = cGRCUs[i];
      if(!current ||
         !current.feedForward(NeuronOCL, cPolinomSupport.AsObject(), cEmbedding.AsObject()))
         ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::calcInputGradients(CNeuronBaseOCL *prevLayer)
  {
   if(!prevLayer)
      ReturnFalse;
//---
   CNeuronBaseOCL *inputs = (cGRCUs.Total() > 1 ? cGRCUs[-2] : prevLayer);
   CNeuronHimNetGCRU* current = cGRCUs[-1];
   if(!current ||
      !current.calcInputGradients(inputs, cPolinomSupport.AsObject(), cEmbedding.AsObject()))
      ReturnFalse;
   if(cGRCUs.Total() > 1)
     {
      CBufferFloat *sup = cPolinomSupport.getGradient();
      if(!cPolinomSupport.SetGradient(GetPointer(bSupportAccum), false))
         ReturnFalse;
      CBufferFloat *emb = cEmbedding.getGradient();
      if(!cEmbedding.SetGradient(GetPointer(bEmbeddingAccum), false))
         ReturnFalse;
      for(int i = cGRCUs.Total() - 2; i >= 0; i--)
        {
         current = cGRCUs[i];
         inputs = (i > 0 ? cGRCUs[i - 1] : prevLayer);
         if(!current ||
            !current.calcInputGradients(inputs, cPolinomSupport.AsObject(), cEmbedding.AsObject()))
            ReturnFalse;
         if(!SumAndNormalize(cPolinomSupport.getGradient(), sup, sup, 1, false, 0, 0, 0, 1))
            ReturnFalse;
         if(!SumAndNormalize(cEmbedding.getGradient(), emb, emb, 1, false, 0, 0, 0, 1))
            ReturnFalse;
        }
      if(!cPolinomSupport.SetGradient(sup, false))
         ReturnFalse;
      if(!cEmbedding.SetGradient(emb, false))
         ReturnFalse;
     }
   if(!cNormSupport.CalcHiddenGradients(cPolinomSupport.AsObject()))
      ReturnFalse;
   if(!cSupport.CalcHiddenGradients(cNormSupport.AsObject()))
      ReturnFalse;
   if(!MatMulGrad(cProjection.getOutput(), cProjection.getPrevOutput(),
                  cProjectionT.getOutput(), cProjectionT.getPrevOutput(),
                  cSupport.getGradient(), cProjectionT.GetCount(),
                  cProjectionT.GetWindow(), cProjectionT.GetCount(), 1, false))
      ReturnFalse;
   if(!cProjectionT.CalcHiddenGradients(cEmbedding.AsObject()))
      ReturnFalse;
   if(cProjectionT.Activation() != None)
      if(!DeActivation(cProjectionT.getOutput(), cProjectionT.getPrevOutput(),
                       cProjectionT.getPrevOutput(), cProjectionT.Activation()))
         ReturnFalse;
   if(!SumAndNormalize(cProjectionT.getGradient(), cProjectionT.getPrevOutput(),
                       cProjectionT.getGradient(), cProjectionT.GetCount(), false, 0, 0, 0, 1))
      ReturnFalse;
   if(!cProjection.CalcHiddenGradients(cProjectionT.AsObject()))
      ReturnFalse;
   if(cProjection.Activation() != None)
      if(!DeActivation(cProjection.getOutput(), cProjection.getPrevOutput(),
                       cProjection.getPrevOutput(), cProjection.Activation()))
         ReturnFalse;
   if(!SumAndNormalize(cProjection.getGradient(), cProjection.getPrevOutput(),
                       cProjection.getGradient(), cProjectionT.GetWindow(), false, 0, 0, 0, 1))
      ReturnFalse;
//---
   CBufferFloat* temp = prevLayer.getGradient();
   if(!prevLayer.SetGradient(prevLayer.getPrevOutput(), false) ||
      !prevLayer.CalcHiddenGradients(cProjection.AsObject()) ||
      !SumAndNormalize(temp, prevLayer.getGradient(), temp, 1, false, 0, 0, 0, 1) ||
      !prevLayer.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cProjection.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cEmbedding.UpdateInputWeights(cProjectionT.AsObject()))
      ReturnFalse;
//---
   CNeuronBaseOCL* inputs = NeuronOCL;
   CNeuronHimNetGCRU* current = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      current = cGRCUs[i];
      if(!current.updateInputWeights(inputs, cPolinomSupport.AsObject(), cEmbedding.AsObject()))
         ReturnFalse;
      inputs = current;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronHimNetDecoder* Source = source;
   if(!cProjection.WeightsUpdate(Source.cProjection.AsObject(), tau))
      ReturnFalse;
   if(!cEmbedding.WeightsUpdate(Source.cEmbedding.AsObject(), tau))
      ReturnFalse;
   if(!cGRCUs.WeightsUpdate(Source.cGRCUs.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cProjection.Save(file_handle))
      ReturnFalse;
   if(!cProjectionT.Save(file_handle))
      ReturnFalse;
   if(!cEmbedding.Save(file_handle))
      ReturnFalse;
   if(!cSupport.Save(file_handle))
      ReturnFalse;
   if(!cNormSupport.Save(file_handle))
      ReturnFalse;
   if(!cPolinomSupport.Save(file_handle))
      ReturnFalse;
   if(!cGRCUs.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cProjection.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cProjectionT.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cEmbedding.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cSupport.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cNormSupport.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cPolinomSupport.AsObject()))
      ReturnFalse;
   if(!cGRCUs.Load(file_handle))
      ReturnFalse;
//---
   bSupportAccum.BufferFree();
   bSupportAccum.Clear();
   CNeuronHimNetGCRU* temp = cGRCUs[-1];
   if(!bSupportAccum.BufferInit(cPolinomSupport.Neurons(), 0) ||
      !bSupportAccum.BufferCreate(OpenCL))
      ReturnFalse;
   bEmbeddingAccum.BufferFree();
   bEmbeddingAccum.Clear();
   if(!bEmbeddingAccum.BufferInit(cEmbedding.Neurons(), 0) ||
      !bEmbeddingAccum.BufferCreate(OpenCL))
      ReturnFalse;
//---
   SetActivationFunction((ENUM_ACTIVATION)temp.Activation());
   if(!SetOutput(temp.getOutput(), true) ||
      !SetGradient(temp.getGradient(), true))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronHimNetDecoder::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
//---
   cProjection.SetOpenCL(OpenCL);
   cProjectionT.SetOpenCL(OpenCL);
   cEmbedding.SetOpenCL(OpenCL);
   cSupport.SetOpenCL(OpenCL);
   cNormSupport.SetOpenCL(OpenCL);
   cPolinomSupport.SetOpenCL(OpenCL);
   cGRCUs.SetOpenCL(OpenCL);
//---
   bSupportAccum.BufferFree();
   bSupportAccum.BufferCreate(OpenCL);
   bEmbeddingAccum.BufferFree();
   bEmbeddingAccum.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronHimNetDecoder::Clear(void)
  {
   if(!CNeuronBaseOCL::Clear())
      ReturnFalse;
//---
   if(!cProjection.Clear())
      ReturnFalse;
   if(!cProjectionT.Clear())
      ReturnFalse;
   if(!cEmbedding.Clear())
      ReturnFalse;
   if(!cSupport.Clear())
      ReturnFalse;
   if(!cNormSupport.Clear())
      ReturnFalse;
   if(!cPolinomSupport.Clear())
      ReturnFalse;
   CNeuronBaseOCL* temp = NULL;
   for(int i = 0; i < cGRCUs.Total(); i++)
     {
      temp = cGRCUs[i];
      if(!temp || !temp.Clear())
         ReturnFalse;
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                            uint units, uint window, uint sparse_dimension,
                            ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units * window, optimization_type, batch))
      ReturnFalse;
   activation = None;
//---
   int index = 0;
   if(!cInpAndHidden.Init(0, index, OpenCL, 2 * units * window, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cNormAttention.Init(0, index, OpenCL, units * sparse_dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cInvDiag.Init(0, index, OpenCL, units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cAX.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cAXplusX.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cNormAXplusX.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cZ_R.Init(0, index, OpenCL, 2 * window, 2 * window, 2 * window, units, 1, optimization, iBatch))
      ReturnFalse;
   cZ_R.SetActivationFunction(SIGMOID);
   index++;
   if(!cZ.Init(0, index, OpenCL, window * units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cR.Init(0, index, OpenCL, window * units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cCandidate.Init(0, index, OpenCL, 2 * window * units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cHC.Init(0, index, OpenCL, 2 * window, 2 * window, window, units, 1, optimization, iBatch))
      ReturnFalse;
   cHC.SetActivationFunction(TANH);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::RandomWalk(CBufferFloat *data,
                                  CBufferFloat *normal,
                                  CBufferFloat *inv_diag,
                                  const int rows,
                                  const int cols)
  {
   if(!OpenCL)
      ReturnFalse;
   uint global_work_offset[2] = {0, 0};
   uint global_work_size[2] = {(uint)rows, MathMin((uint)cols, (uint)OpenCL.GetMaxLocalSize(1))};
   uint local_work_size[2] = {1, global_work_size[1]};
   uint kernel = def_k_RandomWalk;
   setBuffer(kernel, def_k_rw_data, data.GetIndex())
   setBuffer(kernel, def_k_rw_norm, normal.GetIndex())
   setBuffer(kernel, def_k_rw_inv_diag, inv_diag.GetIndex())
   setArgument(kernel, def_k_rw_total_cols, cols)
//---
   kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!inv_diag.BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::FeedForward(CNeuronBaseOCL *SourceData, CNeuronSNSMHAttention *SparseAttent)
  {
   if(!SourceData || !SparseAttent)
      ReturnFalse;
//---
   const uint units = GetCount();
   const uint window = GetWindow();
   const uint sparse = GetSparseDimension();
//---
   if(!RandomWalk(SparseAttent.getOutput(), cNormAttention.getOutput(), cInvDiag.getOutput(), window, sparse))
      ReturnFalse;
   if(!SwapOutputs())
      ReturnFalse;
   if(!Concat(SourceData.getOutput(), PrevOutput, cInpAndHidden.getOutput(), window, window, units))
      ReturnFalse;
//---
   if(!SparseMatMul(SparseAttent.GetIndexes(), cNormAttention.getOutput(), cInpAndHidden.getOutput(),
                    cAX.getOutput(), units, sparse, units, 2 * window))
      ReturnFalse;
   if(!SumAndNormalize(cAX.getOutput(), cInpAndHidden.getOutput(), cAXplusX.getOutput(), 2 * window, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!DiagMatMul(cInvDiag.getOutput(), cAXplusX.getOutput(), cNormAXplusX.getOutput(), units, 2 * window, 1, None))
      ReturnFalse;
   if(!cZ_R.FeedForward(cNormAXplusX.AsObject()))
      ReturnFalse;
   if(!DeConcat(cZ.getOutput(), cR.getOutput(), cZ_R.getOutput(), window, window, units))
      ReturnFalse;
   if(!ElementMult(cR.getOutput(), PrevOutput, cR.getPrevOutput()))
      ReturnFalse;
   if(!Concat(SourceData.getOutput(), cR.getPrevOutput(), cCandidate.getOutput(), window, window, units))
      ReturnFalse;
   if(!cHC.FeedForward(cCandidate.AsObject()))
      ReturnFalse;
   if(!GateElementMult(PrevOutput, cHC.getOutput(), cZ.getOutput(), Output))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::CalcInputGradients(CNeuronBaseOCL *SourceData, CNeuronSNSMHAttention *SparseAttent)
  {
   if(!SourceData || !SparseAttent)
      ReturnFalse;
//---
   const uint units = GetCount();
   const uint window = GetWindow();
   const uint sparse = GetSparseDimension();
//---
   if(!GateElementMultGrad(PrevOutput, cInpAndHidden.getGradient(),
                           cHC.getOutput(), cHC.getGradient(),
                           cZ.getOutput(), cZ.getGradient(),
                           Gradient, None, cHC.Activation(), cZ_R.Activation()))
      ReturnFalse;
   if(!cCandidate.CalcHiddenGradients(cHC.AsObject()))
      ReturnFalse;
   if(!DeConcat(SourceData.getGradient(), cR.getPrevOutput(),
                cCandidate.getGradient(), window, window, units))
      ReturnFalse;
   if(!ElementMultGrad(cR.getOutput(), cR.getGradient(),
                       PrevOutput, cInpAndHidden.getGradient(),
                       cR.getPrevOutput(), cZ_R.Activation(), None))
      ReturnFalse;
   if(!Concat(cZ.getGradient(), cR.getGradient(),
              cZ_R.getGradient(), window, window, units))
      ReturnFalse;
   if(!cNormAXplusX.CalcHiddenGradients(cZ_R.AsObject()))
      ReturnFalse;
   if(!DiagMatMulGrad(cInvDiag.getOutput(), cInvDiag.getGradient(),
                      cAXplusX.getOutput(), cAX.getGradient(),
                      cNormAXplusX.getGradient(), units, 2 * window, 1))
      ReturnFalse;
   if(!SparseMatMulGrad(SparseAttent.GetIndexes(), cNormAttention.getOutput(),
                        cNormAttention.getGradient(), cInpAndHidden.getOutput(),
                        cInpAndHidden.getGradient(), cAX.getGradient(),
                        units, sparse, units, 2 * window))
      ReturnFalse;
   if(!SumAndNormalize(cAX.getGradient(), cInpAndHidden.getGradient(),
                       cInpAndHidden.getGradient(), 2 * window, false, 0, 0, 0, 1))
      ReturnFalse;
   if(!DeConcat(cAXplusX.getGradient(), cAXplusX.getPrevOutput(),
                cInpAndHidden.getGradient(), window, window, units))
      ReturnFalse;
   if(!SumAndNormalize(SourceData.getGradient(), cAXplusX.getGradient(),
                       SourceData.getGradient(), window, false, 0, 0, 0, 1))
      ReturnFalse;
   if(SourceData.Activation() != None)
      if(!DeActivation(SourceData.getOutput(), SourceData.getGradient(),
                       SourceData.getGradient(), SourceData.Activation()))
         ReturnFalse;
   if(!DiagMatMul(cInvDiag.getOutput(), cNormAttention.getGradient(),
                  SparseAttent.getGradient(), units, sparse, 1, None))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
//---
   if(!cZ_R.UpdateInputWeights(cNormAXplusX.AsObject()))
      ReturnFalse;
   if(!cHC.UpdateInputWeights(cCandidate.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronFastGConv *Source = source;
   if(!cZ_R.WeightsUpdate(Source.cZ_R.AsObject(), tau))
      ReturnFalse;
   if(!cHC.WeightsUpdate(Source.cHC.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cZ_R.Save(file_handle))
      ReturnFalse;
   if(!cHC.Save(file_handle))
      ReturnFalse;
   if(FileWriteInteger(file_handle, (int)GetSparseDimension()) < INT_VALUE)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronFastGConv::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cZ_R.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cHC.AsObject()))
      ReturnFalse;
//---
   if(FileIsEnding(file_handle))
      ReturnFalse;
   uint sparse_dimension = FileReadInteger(file_handle);
   uint units = cZ_R.GetUnits();
   uint window_out = Neurons() / units;
   uint window = cZ_R.GetWindow() - window_out;
//---
   int index = 0;
   if(!cInpAndHidden.Init(0, index, OpenCL, units * (window + window_out), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cNormAttention.Init(0, index, OpenCL, units * sparse_dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cInvDiag.Init(0, index, OpenCL, units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cAX.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cAXplusX.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cNormAXplusX.Init(0, index, OpenCL, cInpAndHidden.Neurons(), optimization, iBatch))
      ReturnFalse;
   index += 2;
   if(!cZ.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cR.Init(0, index, OpenCL, window_out * units, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cCandidate.Init(0, index, OpenCL, (window + window_out) * units, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronFastGConv::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
//---
   cZ_R.SetOpenCL(OpenCL);
   cHC.SetOpenCL(OpenCL);
   cInpAndHidden.SetOpenCL(OpenCL);
   cNormAttention.SetOpenCL(OpenCL);
   cInvDiag.SetOpenCL(OpenCL);
   cAX.SetOpenCL(OpenCL);
   cAXplusX.SetOpenCL(OpenCL);
   cNormAXplusX.SetOpenCL(OpenCL);
   cZ.SetOpenCL(OpenCL);
   cR.SetOpenCL(OpenCL);
   cCandidate.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                         uint time_steps, uint variables, uint embedding_dim,
                         uint emb_layers, uint sparse_dimension, uint heads,
                         float sparse, uint gcru_layers,
                         uint forecast, uint forec_layers,
                         ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(emb_layers <= 0 || gcru_layers <= 0 || forec_layers <= 0)
      ReturnFalse;
//---
   if(!CNeuronTransposeOCL::Init(numOutputs, myIndex, open_cl, variables, forecast, optimization_type, batch))
      ReturnFalse;
//---
   uint index = 0;
   if(!cTranspose.Init(0, index, OpenCL, time_steps, variables, optimization, iBatch))
      ReturnFalse;
//--- Embedding
   cEmbedding.Clear();
   cEmbedding.SetOpenCL(OpenCL);
   index++;
   CNeuronConvOCL *conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(0, index, OpenCL, time_steps, time_steps, embedding_dim, variables, 1, optimization, iBatch) ||
      !cEmbedding.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   conv.SetActivationFunction(SoftPlus);
   for(uint i = 1; i < emb_layers; i++)
     {
      index++;
      conv = new CNeuronConvOCL();
      if(!conv ||
         !conv.Init(0, index, OpenCL, embedding_dim, embedding_dim,
                    embedding_dim, variables, 1, optimization, iBatch) ||
         !cEmbedding.Add(conv))
        {
         DeleteObj(conv);
         ReturnFalse;
        }
      conv.SetActivationFunction(SoftPlus);
     }
   CNeuronBatchNormOCL *norm = new CNeuronBatchNormOCL();
   index++;
   if(!norm ||
      !norm.Init(0, index, OpenCL, conv.Neurons(), iBatch, optimization) ||
      !cEmbedding.Add(norm))
     {
      DeleteObj(norm);
      ReturnFalse;
     }
   norm.SetActivationFunction(None);
//--- GCRUs
   cGCRU.Clear();
   cGCRU.SetOpenCL(OpenCL);
   index++;
   if(!cAttention.Init(0, index, OpenCL, variables, embedding_dim, heads,
                       sparse_dimension, sparse, optimization, iBatch))
      ReturnFalse;
   CNeuronFastGConv *gcru = NULL;
   for(uint i = 0; i < gcru_layers; i++)
     {
      index++;
      gcru = new CNeuronFastGConv();
      if(!gcru ||
         !gcru.Init(0, index, OpenCL, variables, embedding_dim, sparse_dimension, optimization, iBatch) ||
         !cGCRU.Add(gcru))
        {
         DeleteObj(gcru);
         ReturnFalse;
        }
     }
//--- Forecast
   cProjection.Clear();
   cProjection.SetOpenCL(OpenCL);
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(0, index, OpenCL, embedding_dim, embedding_dim,
                 forecast, variables, 1, optimization, iBatch) ||
      !cProjection.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   conv.SetActivationFunction(SoftPlus);
   for(uint i = 1; i < forec_layers; i++)
     {
      index++;
      conv = new CNeuronConvOCL();
      if(!conv ||
         !conv.Init(0, index, OpenCL, forecast, forecast,
                    forecast, variables, 1, optimization, iBatch) ||
         !cProjection.Add(conv))
        {
         DeleteObj(conv);
         ReturnFalse;
        }
      conv.SetActivationFunction(SoftPlus);
     }
   norm = new CNeuronBatchNormOCL();
   index++;
   if(!norm ||
      !norm.Init(0, index, OpenCL, conv.Neurons(), iBatch, optimization) ||
      !cProjection.Add(norm))
     {
      DeleteObj(norm);
      ReturnFalse;
     }
   norm.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cTranspose.FeedForward(NeuronOCL))
      ReturnFalse;
   CNeuronBaseOCL *inputs = cTranspose.AsObject();
   CNeuronBaseOCL *current = NULL;
//--- Embedding
   for(int i = 0; i < cEmbedding.Total(); i++)
     {
      current = cEmbedding[i];
      if(!current ||
         !current.FeedForward(inputs))
         ReturnFalse;
      inputs = current;
     }
//--- GCRUs
   if(!cAttention.FeedForward(inputs))
      ReturnFalse;
   CNeuronFastGConv *gcru = NULL;
   for(int i = 0; i < cGCRU.Total(); i++)
     {
      gcru = cGCRU[i];
      if(!gcru ||
         !gcru.FeedForward(inputs, cAttention.AsObject()))
         ReturnFalse;
      inputs = gcru;
     }
//--- Forecast
   for(int i = 0; i < cProjection.Total(); i++)
     {
      current = cProjection[i];
      if(!current ||
         !current.FeedForward(inputs))
         ReturnFalse;
      inputs = current;
     }
//--- result
   return CNeuronTransposeOCL::feedForward(inputs);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronTransposeOCL::calcInputGradients(cProjection[-1]))
      ReturnFalse;
//--- Forecast
   CNeuronBaseOCL *inputs = NULL;
   for(int i = cProjection.Total() - 1; i >= 0; i--)
     {
      inputs = (i > 0 ? cProjection[i - 1] : cGCRU[-1]);
      if(!inputs ||
         !inputs.CalcHiddenGradients(cProjection[i]))
         ReturnFalse;
     }
//--- GCRU
   CNeuronFastGConv *gcru = cGCRU[-1];
   int layers = cGCRU.Total();
   inputs = (layers > 1 ? cGCRU[-2] : cEmbedding[-1]);
   if(!gcru ||
      !gcru.CalcInputGradients(inputs, cAttention.AsObject()))
      ReturnFalse;
   if(layers > 1)
     {
      CBufferFloat *temp = cAttention.getGradient();
      if(!cAttention.SetGradient(cAttention.getPrevOutput(), false))
         ReturnFalse;
      for(int i = layers - 2; i >= 0; i--)
        {
         inputs = (i > 0 ? cGCRU[i - 1] : cEmbedding[-1]);
         gcru = cGCRU[i];
         if(!gcru ||
            !gcru.CalcInputGradients(inputs, cAttention.AsObject()) ||
            !SumAndNormalize(temp, cAttention.getGradient(), temp, 1, false, 0, 0, 0, 1))
            ReturnFalse;
        }
      if(!cAttention.SetGradient(temp, false))
         ReturnFalse;
     }
   CBufferFloat *temp = inputs.getGradient();
   if(!inputs.SetGradient(inputs.getPrevOutput(), false) ||
      !inputs.CalcHiddenGradients(cAttention.AsObject()) ||
      !SumAndNormalize(temp, inputs.getGradient(), temp, 1, false, 0, 0, 0, 1) ||
      !inputs.SetGradient(temp, false))
      ReturnFalse;
//--- Embedding
   for(int i = cEmbedding.Total() - 2; i >= 0; i--)
     {
      inputs = cEmbedding[i];
      if(!inputs ||
         !inputs.CalcHiddenGradients(cEmbedding[i + 1]))
         ReturnFalse;
     }
//---
   if(!cTranspose.CalcHiddenGradients(inputs))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cTranspose.AsObject()))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   CNeuronBaseOCL *inputs = cTranspose.AsObject();
   CNeuronBaseOCL *current = NULL;
//--- Embedding
   for(int i = 0; i < cEmbedding.Total(); i++)
     {
      current = cEmbedding[i];
      if(!current ||
         !current.UpdateInputWeights(inputs))
         ReturnFalse;
      inputs = current;
     }
//--- GCRUs
   if(!cAttention.UpdateInputWeights(inputs))
      ReturnFalse;
   for(int i = 0; i < cGCRU.Total(); i++)
     {
      current = cGCRU[i];
      if(!current ||
         !current.UpdateInputWeights(inputs))
         ReturnFalse;
      inputs = current;
     }
//--- Forecast
   for(int i = 0; i < cProjection.Total(); i++)
     {
      current = cProjection[i];
      if(!current ||
         !current.UpdateInputWeights(inputs))
         ReturnFalse;
      inputs = current;
     }
//--- result
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronTransposeOCL::WeightsUpdate(source, tau))
      ReturnFalse;
   CNeuronSAGDFN *Source = source;
//--- Embedding
   if(!cEmbedding.WeightsUpdate(Source.cEmbedding.AsObject(), tau))
      ReturnFalse;
//--- GCRUs
   if(!cAttention.WeightsUpdate(Source.cAttention.AsObject(), tau))
      ReturnFalse;
   if(!cGCRU.WeightsUpdate(Source.cGCRU.AsObject(), tau))
      ReturnFalse;
//--- Forecast
   if(!cProjection.WeightsUpdate(Source.cProjection.AsObject(), tau))
      ReturnFalse;
//--- result
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::Save(const int file_handle)
  {
   if(!CNeuronTransposeOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cTranspose.Save(file_handle))
      ReturnFalse;
//--- Embedding
   if(!cEmbedding.Save(file_handle))
      ReturnFalse;
//--- GCRUs
   if(!cGCRU.Save(file_handle) ||
      !cAttention.Save(file_handle))
      ReturnFalse;
//--- Forecast
   if(!cProjection.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSAGDFN::Load(const int file_handle)
  {
   if(!CNeuronTransposeOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cTranspose.AsObject()))
      ReturnFalse;
//--- Embedding
   if(!cEmbedding.Load(file_handle))
      ReturnFalse;
//--- GCRUs
   if(!cGCRU.Load(file_handle) ||
      !LoadInsideLayer(file_handle, cAttention.AsObject()))
      ReturnFalse;
//--- Forecast
   if(!cProjection.Load(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSAGDFN::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronTransposeOCL::SetOpenCL(obj);
//---
   cTranspose.SetOpenCL(OpenCL);
   cEmbedding.SetOpenCL(OpenCL);
   cGCRU.SetOpenCL(OpenCL);
   cAttention.SetOpenCL(OpenCL);
   cProjection.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                   uint units, uint window, uint embed_dim,
                                   ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, (window + embed_dim)*units, optimization_type, batch))
      ReturnFalse;
   activation = None;
   if(!cEmbedding.Init(0, 0, OpenCL, embed_dim * units, optimization, iBatch))
      ReturnFalse;
   cEmbedding.SetActivationFunction(TANH);
//---
   iUnits = units;
   iWindow = window;
   iEmbeddingDim = embed_dim;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(bTrain)
      if(!cEmbedding.FeedForward())
         ReturnFalse;
//---
   if(!Concat(NeuronOCL.getOutput(), cEmbedding.getOutput(), Output, iWindow, iEmbeddingDim, iUnits))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
   if(!DeConcat(NeuronOCL.getGradient(), cEmbedding.getGradient(), Gradient, iWindow, iEmbeddingDim, iUnits))
      ReturnFalse;
   Deactivation(NeuronOCL)
   Deactivation(cEmbedding)
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cEmbedding.UpdateInputWeights())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronSpatialEmbedding *Source = source;
   if(!cEmbedding.WeightsUpdate(Source.cEmbedding.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!cEmbedding.Save(file_handle))
      ReturnFalse;
//---
   if(FileWriteInteger(file_handle, (int)iUnits) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, (int)iWindow) < INT_VALUE)
      ReturnFalse;
   if(FileWriteInteger(file_handle, (int)iEmbeddingDim) < INT_VALUE)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSpatialEmbedding::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cEmbedding.AsObject()))
      ReturnFalse;
//---
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iUnits = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iWindow = (uint)FileReadInteger(file_handle);
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iEmbeddingDim = (uint)FileReadInteger(file_handle);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSpatialEmbedding::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   cEmbedding.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                           uint units, uint window, uint emb_dimension,
                           uint experts, float dropout,
                           ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, units * units, optimization_type, batch))
      ReturnFalse;
   activation = None;
//---
   int index = 0;
   if(!cGraphs.Init(0, index, OpenCL, Neurons()*experts, optimization, iBatch))
      ReturnFalse;
   cGraphs.SetActivationFunction(SIGMOID);
//---
   CNeuronBaseOCL       *neuron  = NULL;
   CNeuronConvOCL       *conv    = NULL;
   CNeuronTransposeOCL  *transp  = NULL;
   CNeuronDropoutOCL    *dout    = NULL;
   CNeuronSoftMaxOCL    *softmax = NULL;
//--- Probability
   acProbability.Clear();
   acProbability.SetOpenCL(OpenCL);
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(experts, index, OpenCL, window, window, experts, units, 1, optimization, iBatch) ||
      !acProbability.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   conv.SetActivationFunction(SoftPlus);
   index++;
   neuron = new CNeuronBaseOCL();
   if(!neuron ||
      !neuron.Init(0, index, OpenCL, experts, optimization, iBatch) ||
      !acProbability.Add(neuron))
     {
      DeleteObj(neuron);
      ReturnFalse;
     }
   index++;
   dout = new CNeuronDropoutOCL();
   if(!dout ||
      !dout.Init(0, index, OpenCL, experts, dropout, optimization, iBatch) ||
      !acProbability.Add(dout))
     {
      DeleteObj(dout);
      ReturnFalse;
     }
   index++;
   softmax = new CNeuronSoftMaxOCL();
   if(!softmax ||
      !softmax.Init(0, index, OpenCL, experts, optimization, iBatch) ||
      !acProbability.Add(softmax))
     {
      DeleteObj(softmax);
      ReturnFalse;
     }
   softmax.SetHeads(1);
//--- Data Embedding
   acDataEmb.Clear();
   acDataEmb.SetOpenCL(OpenCL);
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(0, index, OpenCL, window, window, 2 * emb_dimension, units, 1, optimization, iBatch) ||
      !acDataEmb.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   conv.SetActivationFunction(SoftPlus);
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(0, index, OpenCL, 2 * emb_dimension, 2 * emb_dimension, emb_dimension, units, 1, optimization, iBatch) ||
      !acDataEmb.Add(conv))
     {
      DeleteObj(conv);
      ReturnFalse;
     }
   conv.SetActivationFunction(None);
   index++;
   transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(0, index, OpenCL, units, emb_dimension, optimization, iBatch) ||
      !acDataEmb.Add(transp))
     {
      DeleteObj(transp);
      ReturnFalse;
     }
   transp.SetActivationFunction((ENUM_ACTIVATION)conv.Activation());
//---
   index++;
   if(!cExpertsEmb.Init(0, index, OpenCL, units * emb_dimension * experts, optimization, iBatch))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   CNeuronBaseOCL* prev = NeuronOCL;
   CNeuronBaseOCL* current = NULL;
//--- Probability
   for(int i = 0; i < acProbability.Total(); i++)
     {
      current = acProbability[i];
      if(!current ||
         !current.FeedForward(prev))
         ReturnFalse;
      prev = current;
     }
//--- Data Embedding
   prev = NeuronOCL;
   for(int i = 0; i < acDataEmb.Total(); i++)
     {
      current = acDataEmb[i];
      if(!current ||
         !current.FeedForward(prev))
         ReturnFalse;
      prev = current;
     }
//--- Experts
   if(bTrain)
      if(!cExpertsEmb.FeedForward())
         ReturnFalse;
//--- Graphs
   uint units = (uint)MathSqrt((double)Neurons());
   uint emb_dim = acDataEmb[-1].Neurons() / units;
   uint experts = cExpertsEmb.Neurons() / acDataEmb[-1].Neurons();
   if(!MatMul(cExpertsEmb.getOutput(), current.getOutput(), cGraphs.getOutput(),
              units, emb_dim, units, experts, false))
      ReturnFalse;
   if(cGraphs.Activation() != None)
      if(!Activation(cGraphs.getOutput(), cGraphs.getOutput(), cGraphs.Activation()))
         ReturnFalse;
//--- Result
   if(!MatMul(acProbability[-1].getOutput(), cGraphs.getOutput(), Output, 1, experts, units * units, 1, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!acDataEmb[-1] || !acProbability[-1])
      ReturnFalse;
//---
   uint units = (uint)MathSqrt((double)Neurons());
   uint emb_dim = acDataEmb[-1].Neurons() / units;
   uint experts = cExpertsEmb.Neurons() / acDataEmb[-1].Neurons();
//---
   if(!MatMulGrad(acProbability[-1].getOutput(), acProbability[-1].getGradient(),
                  cGraphs.getOutput(), cGraphs.getGradient(), Gradient,
                  1, experts, units * units, 1, false))
      ReturnFalse;
//--- Graphs
   if(cGraphs.Activation() != None)
      if(!DeActivation(cGraphs.getOutput(), cGraphs.getGradient(),
                       cGraphs.getGradient(), cGraphs.Activation()))
         ReturnFalse;
   if(!MatMulGrad(cExpertsEmb.getOutput(), cExpertsEmb.getGradient(),
                  acDataEmb[-1].getOutput(), acDataEmb[-1].getGradient(),
                  cGraphs.getGradient(), units, emb_dim, units, experts, false))
      ReturnFalse;
//---
   CNeuronBaseOCL* next = NULL;
   CNeuronBaseOCL* current = NULL;
//--- Probability
   for(int i = acProbability.Total() - 1; i >= 0; i--)
     {
      next = acProbability[i];
      current = (i == 0 ? NeuronOCL : acProbability[i - 1]);
      if(!current ||
         !current.CalcHiddenGradients(next))
         ReturnFalse;
     }
//--- Data Embedding
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(NeuronOCL.getPrevOutput(), false))
      ReturnFalse;
   for(int i = acDataEmb.Total() - 1; i >= 0; i--)
     {
      next = acDataEmb[i];
      current = (i == 0 ? NeuronOCL : acDataEmb[i - 1]);
      if(!current ||
         !current.CalcHiddenGradients(next))
         ReturnFalse;
     }
   if(!SumAndNormalize(temp, NeuronOCL.getGradient(), temp, 1, false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   CNeuronBaseOCL* prev = NeuronOCL;
   CNeuronBaseOCL* current = NULL;
//--- Probability
   for(int i = 0; i < acProbability.Total(); i++)
     {
      current = acProbability[i];
      if(!current ||
         !current.UpdateInputWeights(prev))
         ReturnFalse;
      prev = current;
     }
//--- Data Embedding
   prev = NeuronOCL;
   for(int i = 0; i < acDataEmb.Total(); i++)
     {
      current = acDataEmb[i];
      if(!current ||
         !current.UpdateInputWeights(prev))
         ReturnFalse;
      prev = current;
     }
//--- Experts
   if(!cExpertsEmb.UpdateInputWeights())
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronBaseOCL::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronGraphons *Source = source;
   if(!acProbability.WeightsUpdate(Source.acProbability.AsObject(), tau))
      ReturnFalse;
   if(!acDataEmb.WeightsUpdate(Source.acDataEmb.AsObject(), tau))
      ReturnFalse;
   if(!cExpertsEmb.WeightsUpdate(Source.cExpertsEmb.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::Save(const int file_handle)
  {
   if(!CNeuronBaseOCL::Save(file_handle))
      ReturnFalse;
//---
   if(!acProbability.Save(file_handle))
      ReturnFalse;
   if(!acDataEmb.Save(file_handle))
      ReturnFalse;
   if(!cExpertsEmb.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGraphons::Load(const int file_handle)
  {
   if(!CNeuronBaseOCL::Load(file_handle))
      ReturnFalse;
//---
   if(!acProbability.Load(file_handle))
      ReturnFalse;
   if(!acDataEmb.Load(file_handle))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cExpertsEmb.AsObject()))
      ReturnFalse;
//---
   uint experts = cExpertsEmb.Neurons() / acDataEmb[-1].Neurons();
   if(!cGraphs.Init(0, 0, OpenCL, Neurons()*experts, optimization, iBatch))
      ReturnFalse;
   cGraphs.SetActivationFunction(SIGMOID);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGraphons::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronBaseOCL::SetOpenCL(obj);
   acProbability.SetOpenCL(OpenCL);
   acDataEmb.SetOpenCL(OpenCL);
   cExpertsEmb.SetOpenCL(OpenCL);
   cGraphs.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGraphons::TrainMode(bool flag)
  {
   CNeuronBaseOCL::TrainMode(flag);
   acProbability.TrainMode(bTrain);
   acDataEmb.TrainMode(bTrain);
   cExpertsEmb.TrainMode(bTrain);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSparseSoftMax::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                uint units, uint dimension_in, uint dimension_out,
                                ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(dimension_in < dimension_out)
      ReturnFalse;
   if(!CNeuronSoftMaxOCL::Init(numOutputs, myIndex, open_cl, units * dimension_out, optimization_type, batch))
      ReturnFalse;
//---
   SetHeads(units);
   iDimensionIn = dimension_in;
   if(!cIndexes.BufferInit(Neurons(), -1) ||
      !cIndexes.BufferCreate(OpenCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSparseSoftMax::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL ||
      NeuronOCL.Neurons() < int(iHeads * iDimensionIn))
      ReturnFalse;
   uint global_work_offset[2] = {0, 0};
   uint global_work_size[2] = { iHeads, iDimensionIn };
   uint local_work_size[2] = { 1, global_work_size[1] };
   uint kernel = def_k_SparseSoftMax;
   setBuffer(kernel, def_k_ssoftmax_data, NeuronOCL.getOutputIndex())
   setBuffer(kernel, def_k_ssoftmax_outputs, getOutputIndex())
   setBuffer(kernel, def_k_ssoftmax_indexes, cIndexes.GetIndex())
   setArgument(kernel, def_k_ssoftmax_out_dimension, int(DimensionOut()))
   kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!Output.BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSparseSoftMax::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!OpenCL || !NeuronOCL ||
      NeuronOCL.Neurons() < int(iHeads * iDimensionIn))
      ReturnFalse;
   uint global_work_offset[2] = {0, 0};
   uint global_work_size[2] = { iHeads, iDimensionIn };
   uint local_work_size[2] = { 1, global_work_size[1] };
   uint kernel = def_k_SparseSoftMaxGrad;
   setBuffer(kernel, def_k_ssoftmax_gr_data_gr, NeuronOCL.getGradientIndex())
   setBuffer(kernel, def_k_ssoftmax_gr_outputs, getOutputIndex())
   setBuffer(kernel, def_k_ssoftmax_gr_outputs_gr, getGradientIndex())
   setBuffer(kernel, def_k_ssoftmax_gr_indexes, cIndexes.GetIndex())
   setArgument(kernel, def_k_ssoftmax_gr_out_dimension, int(DimensionOut()))
   kernelExecuteLoc(kernel, global_work_offset, global_work_size, local_work_size)
#ifdef  _DEBUG
   if(!NeuronOCL.getGradient().BufferRead())
      ReturnFalse;
#endif
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSparseSoftMax::Save(const int file_handle)
  {
   if(!CNeuronSoftMaxOCL::Save(file_handle))
      ReturnFalse;
   if(FileWriteInteger(file_handle, iDimensionIn) < INT_VALUE)
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronSparseSoftMax::Load(const int file_handle)
  {
   if(!CNeuronSoftMaxOCL::Load(file_handle))
      ReturnFalse;
   if(FileIsEnding(file_handle))
      ReturnFalse;
   iDimensionIn = (uint)FileReadInteger(file_handle);
//---
   cIndexes.BufferFree();
   if(!cIndexes.BufferInit(Neurons(), -1) ||
      !cIndexes.BufferCreate(OpenCL))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronSparseSoftMax::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronSoftMaxOCL::SetOpenCL(obj);
   cIndexes.BufferCreate(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                                  uint units, uint window, uint experts, float dropout,
                                  uint emb_dimension, uint sparse_dimension,
                                  ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMHAttentionPooling::Init(numOutputs, myIndex, open_cl, window, units,
                                       2, optimization_type, batch))
      ReturnFalse;
//---
   int index = 0;
   if(!cGlobal.Init(0, index, OpenCL, iUnits, iWindow, emb_dimension, experts, dropout,
                    optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cLocal.Init(0, index, OpenCL, iUnits, iWindow, experts, dropout, emb_dimension,
                   sparse_dimension, optimization, iBatch))
      ReturnFalse;
   index++;
   if(!cConcat.Init(0, index, OpenCL, 2 * iWindow * iUnits, optimization, iBatch))
      ReturnFalse;
   cConcat.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cGlobal.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!cLocal.FeedForward(NeuronOCL))
      ReturnFalse;
   if(!Concat(cGlobal.getOutput(), cLocal.getOutput(), cConcat.getOutput(),
              iWindow, iWindow, iUnits))
      ReturnFalse;
//---
   return CNeuronMHAttentionPooling::feedForward(cConcat.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      ReturnFalse;
//---
   if(!CNeuronMHAttentionPooling::calcInputGradients(cConcat.AsObject()))
      ReturnFalse;
   if(!DeConcat(cGlobal.getGradient(), cLocal.getGradient(), cConcat.getGradient(),
                iWindow, iWindow, iUnits))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cGlobal.AsObject()))
      ReturnFalse;
   CBufferFloat *temp = NeuronOCL.getGradient();
   if(!NeuronOCL.SetGradient(PrevOutput, false))
      ReturnFalse;
   if(!NeuronOCL.CalcHiddenGradients(cLocal.AsObject()))
      ReturnFalse;
   if(!SumAndNormalize(temp, PrevOutput, temp, iWindow, false, 0, 0, 0, 1) ||
      !NeuronOCL.SetGradient(temp, false))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::updateInputWeights(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cGlobal.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
   if(!cLocal.UpdateInputWeights(NeuronOCL))
      ReturnFalse;
//---
   return CNeuronMHAttentionPooling::updateInputWeights(cConcat.AsObject());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::WeightsUpdate(CNeuronBaseOCL *source, float tau)
  {
   if(!CNeuronMHAttentionPooling::WeightsUpdate(source, tau))
      ReturnFalse;
//---
   CNeuronGlobLocGraphAtt* Source = source;
   if(!cGlobal.WeightsUpdate(Source.cGlobal.AsObject(), tau))
      ReturnFalse;
   if(!cLocal.WeightsUpdate(Source.cLocal.AsObject(), tau))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::Save(const int file_handle)
  {
   if(!CNeuronMHAttentionPooling::Save(file_handle))
      ReturnFalse;
//---
   if(!cGlobal.Save(file_handle))
      ReturnFalse;
   if(!cLocal.Save(file_handle))
      ReturnFalse;
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CNeuronGlobLocGraphAtt::Load(const int file_handle)
  {
   if(!CNeuronMHAttentionPooling::Load(file_handle))
      ReturnFalse;
//---
   if(!LoadInsideLayer(file_handle, cGlobal.AsObject()))
      ReturnFalse;
   if(!LoadInsideLayer(file_handle, cLocal.AsObject()))
      ReturnFalse;
//---
   if(!cConcat.Init(0, 2, OpenCL, 2 * iWindow * iUnits, optimization, iBatch))
      ReturnFalse;
   cConcat.SetActivationFunction(None);
//---
   return true;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGlobLocGraphAtt::SetOpenCL(COpenCLMy *obj)
  {
   CNeuronMHAttentionPooling::SetOpenCL(obj);
//---
   cGlobal.SetOpenCL(OpenCL);
   cLocal.SetOpenCL(OpenCL);
   cConcat.SetOpenCL(OpenCL);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CNeuronGlobLocGraphAtt::TrainMode(bool flag)
  {
   CNeuronMHAttentionPooling::TrainMode(flag);
//---
   cGlobal.TrainMode(flag);
   cLocal.TrainMode(flag);
   cConcat.TrainMode(flag);
  }
