unit utractorcito_rch;

{$mode delphi}

{$DEFINE KICKRANDOM}

interface

uses
  Classes, SysUtils, usalasdejuego,
  uInicioYFinal,
  Math,
  uInterpreteDeParametros,
  uauxiliares, xmatdefs, matreal,
  umh_distribuidor_optsim, uConstantesSimSEE,
  uestados,
  ufechas,
  uneuronas,
  uparsimoniat,
  uversiones, uversion_architexto,
  uAdministradorDeEstadosMultiPlano,
  uadministradordeestadoscerebro,
  ulearningcfmultiplano,
  ulearningcfcerebros,
  utrayectorias_xcdpcf;


const
  carpeta_CFsYTrys = 'CFsYTrys'; // guarda los CFs y Trayectorias de la optimización

// interpreta los parámetros de entrada al programa y ejecuta lo que corresponda.
procedure tractorcito_cmd;

(***** Conjunto de órdenes reconocidas *******************)


(*
 Crea un administrador de CF Multiplano, con todos los coeficientes nulos.
 Parámetros:
 sala= (archivo de la sala obligatorio)
 [CF]= (string archivo, si no se especifica se calcula a partir de la sala).
 [nHiperplanos]=( cantidad de hiperplanos por paso de tiempo ). Si se omite
     se considera nHiperplanos = 1.
 [dtIni]=(fecha del primer paso de tiempo, si no se especifica se considera la
       de inicio de simulación de la Sala.
 [dtFin]=(fecha de inicio del paso de tiempo posterior al horizonte de simulación
       deseado. Si no se especifica se considera la de Fin de simulación de la Sala.
*)
procedure xo_cf_mp_nulo(prms: TInterpreteParametros);


(*
 Crea un administrador de estado CF Cerebro
  Parámetros:
 sala= (archivo de la sala obligatorio)
 [CF]= (string archivo, si no se especifica se calcula a partir de la sala).
 [ArrNPCI]=(enteros separados ";" con la cantidad de neuranas de las capas intermedias)
    Ej: 4;3  indicaría la creación de una Red Neuronal por paso de tiempo consistente
    en una capa de 4 neuronas cuyas entradas es el vector de estado X y sus salidas
    son las entradas de una egunda capa de 3 neuronas. Las salidad de la capa de
    3 neuronas son entradas de una neurona con saturación cuya salida
    es a su vez la entrada de una única neurona en la capa de salida sin saturación.
    Si no se especifica, no hay capas intermedias y la RN de cada paso de tiempo
    es formada por una neurona con saturación que tiene por entrada el vector
    de estados X y cuya salida alimenta la neurora de salida que no tienen saturación.
 [dtIni]=(fecha del primer paso de tiempo, si no se especifica se considera la
       de inicio de simulación de la Sala.
 [dtFin]=(fecha de inicio del paso de tiempo posterior al horizonte de simulación
       deseado. Si no se especifica se considera la de Fin de simulación de la Sala
*)
procedure xo_cf_Cerebro_nulo(prms: TInterpreteParametros);

procedure xo_cf_Cerebro_especifico(prms: TInterpreteParametros);


// Realiza una simulación en base a una Sala, un CF,
// un conjunto de Estrellitas de partida y un conjunto de semillas de simulación
// como resultado genera un archivo de trayectorias.
procedure xo_sim(prms: TInterpreteParametros);


(**
 Actualiza los valores de CF de las trayectorias pasadas como parámetro
 en base a los costos directos del paso y a los valores de CF anteriores
 Utiliza un vector de PesosForward y un factor de actualización q.
 Parámetros:
  archi=(archivo de trayectorias de entrada)
  [archi_out]=(archivo de trayectorias de salida) si no se especifica
  [PesosForward]=( pesos para los Pasos hacia adelante) debe ingresarse
      un conjunto de números en punto flotante separados por ";".
      Por ej.: "0.1;0.2;0.7"  indicaría que que se utilicen hasta 3 pasos
      hacia delante. La actualización correspondiente a considerar solo
      el primer paso se considerará con peso 0.1, la actualización correspondiente
      a considerar los costos directos de los dos primeros pasos se considera
      con peso 0.2 y la correspondiente a considerar los costos directos de los
      tres primeros pasos con peso 0.7. Tenga en cuenta que no se impone internamente
      que la suma de los pesos sea 1.
  q=( factor de actualización correspondiente a un paso de tiempo ).
*)
procedure xo_updateCF_npfq(prms: TinterpreteParametros);


(* Lee una Sala desde disco y la libera solo para testear pérdidas de memoria
Parámetros:
  sala = (arhivo de sala a leer );
  *)
procedure xo_testmem(prms: TInterpreteParametros);


procedure xo_ayuda(prms: TInterpreteParametros);
(*============================================================*)
  (*
   Lee el archivo de semillas "archiSemilla". Este archivo tiene que tener el formato que
   se muestra en el comentario anterior.
   Retorna en seillasIniSin el array de semillas y como resultado de la función
   la cantidad de semillas. (length( semillasIniSim) ).
  *)
function CargarSemillas(out semillasIniSim: TDAofNInt; archiSemilla: string): integer;
procedure WriteSemillas(const semillasIniSim: TDAofNInt; archiSemilla: string );



// Genera un array de semillas en base a la SemillaMadre
// iCronicaIni e iCronicaFin van de 1 .. NCronicas (globales de simulación)
// En semillasIniSim se retorna el array de las semillas y en el resultado
// de la función se retorna la cantidad de semillas (el largo del array).
function GenSemillas(out semillasIniSim: TDAOfNInt;
  SemillaMadre, iCronicaIni, iCronicaFin: integer): integer;

function TractorcitoRunSimular(
  // Entradas
  sala: TSalaDeJuego; dtIni, dtFin: TDateTime; EstadosIniciales: TNubeDePuntos_XEstado;
  // Dejar EstadosIniciales como entrada
  SemillasIniciales: TDAOfNInt; var CF, CF_Enganche: TAdminEstados;
  nHilos: integer; // -1 es nHilos = núcleos
  // Salida
  var Trayectorias: TTrayectorias ): NReal;

function CrearCFMultiPlano_NULO(sala: TSalaDeJuego; dtIni, dtFin: TDateTime;
  nHiperplanos: integer): TAdminEstadosMultiPlano;

// Actualiza el campo CF de las trayectorias en base a adicionar los costos
// directo del paso (cdp) mirando diferentes cantidades de pasos hacia delante
// y ponderando cada mirada con los pesos indicados en pesosForward
// y utilizando el factor de actualización del paso q
// Como resultado los valors CF de trys son modificados y la función
// retorna el promedio del cuadrado de la distancia euclídea entre los valores
// de CF antes y después del cálculo.
function UpdateCF_npfq(Trayectorias: TTrayectorias; pesosForward: TVectR;
  q: NReal): NReal;

procedure TractorcitoRunOptimizar(
  // Entradas
  sala: TSalaDeJuego; dtIni, dtFin: TDateTime; EstadosIniciales: TNubeDePuntos_XEstado;
  // Dejar EstadosIniciales como entrada
  SemillasIniciales: TDAOfNInt; var CF, CF_Enganche: TAdminEstados;
  nHilos: integer; // -1 es nHilos = núcleos
  // Salida
  var Trayectorias: TTrayectorias;
  parsimonia_tipo: integer;
  parsimonia_nPotencias: integer;
  parsimonia_nArmonicos: integer);






function archi_CF_of_iter( CarpetaSala: string; cnt: integer ): string;
function archi_try_of_iter( CarpetaSala: string; cnt: integer ): string;

// retorna el contador del último juego de CF y Trayectorias guardado
// en carpeta_CFsYTrys
function ultimo_CFsYTrys( CarpetaSala: string ): integer;


implementation

{ ArchiSemilla:
 NSemillas: 3
 Semillas: 31, 41, 61
}

function CargarSemillas(out semillasIniSim: TDAofNInt; archiSemilla: string): integer;
var
  f: TextFile;
  s: string;
  nSemillas, kSemilla: integer;
begin
  AssignFile(f, archiSemilla);
  Reset(f);

  ReadLn(f, s); //NSemillas: 3
  getNextPalSep(s, #9); //NSemillas
  nSemillas := StrToInt( getNextPalSep(s, #9));

  SetLength(semillasIniSim, nSemillas);

  ReadLn(f, s); //Semillas: 31, 41, 61
  getNextPalSep(s, #9); //Semillas
  for kSemilla := 0 to nSemillas - 1 do
  begin
    semillasIniSim[kSemilla] := StrToInt( getNextPalSep(s, #9));
  end;

  Result := nSemillas;
end;

procedure WriteSemillas(const semillasIniSim: TDAofNInt; archiSemilla: string );
var
  f: TextFile;
  s: string;
  nSemillas, kSemilla: integer;
begin
  AssignFile(f, archiSemilla);
  Rewrite(f);
  writeln(f, 'NSemillas:', #9, length( semillasIniSim ) ); //NSemillas: 3
  Write(f, 'Semillas:');
  for kSemilla := 0 to high( semillasIniSim )  do
    Write(f, #9, semillasIniSim[kSemilla]);
  writeln( f );
  closefile( f );
end;

function GenSemillas(out semillasIniSim: TDAOfNInt;
  SemillaMadre, iCronicaIni, iCronicaFin: integer): integer;
var
  k, NSemillas: integer;
begin
  NSemillas := iCronicaFin - iCronicaIni + 1;
  setlength(semillasIniSim, NSemillas);
  for k := iCronicaIni to iCronicaFin do
    semillasIniSim[k - iCronicaIni] := SemillaMadre + k;
  Result := NSemillas;
end;


function TractorcitoRunSimular(sala: TSalaDeJuego; dtIni, dtFin: TDateTime;
  EstadosIniciales: TNubeDePuntos_XEstado; SemillasIniciales: TDAOfNInt;
  var CF, CF_Enganche: TAdminEstados; nHilos: integer;
  var Trayectorias: TTrayectorias): NReal;
var
  nTareas: integer;
  GestorMT: TGestorSalaMH;

begin
  sala.globs.CF_Enganche := CF_Enganche;

  if dtIni <> 1 then
  begin
    sala.globs.fechaIniSim.dt := dtIni;
    sala.globs.fechaFinSim.dt := dtFin;
    sala.globs.fechaIniOpt.dt := dtIni;
    sala.globs.fechaFinOpt.dt := dtFin;
  end;

  sala.globs.semillasIniSim := SemillasIniciales;
  sala.globs.NCronicasSim_global_ := length(SemillasIniciales);
  sala.globs.nubeDePuntos_XEstado := EstadosIniciales;
  if EstadosIniciales <> nil then
  begin
    sala.globs.NEstadosIniciales_global :=
      sala.globs.nubeDePuntos_XEstado.NEstadosIniciales;
    sala.globs.NTrayectorias_global :=
      sala.globs.NEstadosIniciales_global * sala.globs.NCronicasSim_global_;
  end
  else
  begin
    sala.globs.NEstadosIniciales_global := 1;
    sala.globs.NTrayectorias_global := sala.globs.NCronicasSim_global_;
  end;

  sala.globs.CF := CF;
  sala.globs.CF_Enganche := CF_Enganche;

  // revisar, me parece inecesario
  if EstadosIniciales <> nil then
    sala.globs.nubeDePuntos_XEstado.indexarVariables(sala.globs.CF);

  sala.globs.Trayectorias := Trayectorias;


  {$IFDEF WINDOWS}
  LimpiarCarpeta(sala.dirResultadosCorrida, '*.*');
  {$ELSE}
  LimpiarCarpeta(sala.dirResultadosCorrida, '*');
  {$ENDIF}

  chdir(sala.dirSala);
  if nHilos <> 0 then
  begin
    nTareas := nHilos;
    GestorMT := TGestorSalaMH.Create(sala);
    GestorMT.nHilosForzados := nHilos;
    GestorMT.nTareasForzadas := nTareas;
    GestorMT.sim_SimularMultiCore(nHilos);
    GestorMT.Free;
  end
  else
    sala.Simular(0, True);

  // desconecto los CFs de la sala para que cuando se hga Free de la sala
  // no los mate
  sala.globs.CF := nil;
  sala.globs.CF_Enganche := nil;
  Result := sala.Acumuladores_.VE_CF_MUSD;
end;

function CrearCFMultiPlano_NULO(sala: TSalaDeJuego; dtIni, dtFin: TDateTime;
  nHiperplanos: integer): TAdminEstadosMultiPlano;
var
  CF: TAdminEstadosMultiPlano;
  nPasosT: integer;
  fechaIni, fechaFin: TFecha;
begin
  sala.PrepararMemoriaYListados(True);
  sala.PrepararActualizadorFichasLPD(True);
  sala.optx_nvxs;
  if dtIni <= 0 then
    dtIni := sala.globs.fechaIniSim.dt;
  if dtFin <= 0 then
    dtFin := sala.globs.fechaFinSim.dt;

  fechaIni := TFecha.Create_Dt(dtIni);
  fechaFin := TFecha.Create_Dt(dtFin);
  nPasosT := trunc((dtFin - dtIni + dt_100ms ) * 24.0 / sala.globs.HorasDelPaso );
  CF := TAdminEstadosMultiPlano.Create(sala.ivar_xr, sala.ivar_xd,
    nPasosT, nHiperplanos);
  CF.nHiperplanosPorPasoT := nHiperplanos;
  CF.CrearElEspacioTiempo(fechaIni, FechaFin, sala.globs.HorasDelPaso,
    nil, False, sala.globs.fActPaso);
  fechaIni.Free;
  fechaFin.Free;
  sala.RegistrarVariablesDeEstado(CF);
  Result := CF;
end;


function CrearCFCerebros_NULO(sala: TSalaDeJuego; dtIni, dtFin: TDateTime;
  ArrNeuronasPorCapa: TDAOfNInt; tiposSaturacionCapa: TDAOfTipoSaturacion): TAdminEstadosCerebro;
var
  CF: TAdminEstadosCerebro;
  nPasosT: integer;
  fechaIni, fechaFin: TFecha;
begin
  sala.PrepararMemoriaYListados(True);
  sala.PrepararActualizadorFichasLPD(True);
  sala.optx_nvxs;
  if dtIni <= 0 then
    dtIni := sala.globs.fechaIniSim.dt;
  if dtFin <= 0 then
    dtFin := sala.globs.fechaFinSim.dt;

  fechaIni := TFecha.Create_Dt(dtIni);
  fechaFin := TFecha.Create_Dt(dtFin);
  nPasosT := trunc((dtFin - dtIni + dt_100ms) * 24.0 / sala.globs.HorasDelPaso);

  CF := TAdminEstadosCerebro.Create( sala.ivar_xr, sala.ivar_xd,
    nPasosT, ArrNeuronasPorCapa, tiposSaturacionCapa );

  sala.RegistrarVariablesDeEstado( CF );


  CF.CrearElEspacioTiempo(fechaIni, FechaFin, sala.globs.HorasDelPaso,
    nil, False, sala.globs.fActPaso);

  {$IFDEF KICKRANDOM}
  CF.KickRandom( 1e-3 );
  {$ENDIF}

  CF.EscalarEntradas;



  fechaIni.Free;
  fechaFin.Free;
  sala.RegistrarVariablesDeEstado(CF);
  Result := CF;
end;


function archi_CF_of_iter(CarpetaSala: string; cnt: integer): string;
begin
  Result := CarpetaSala + DirectorySeparator + carpeta_CFsYTrys +
    DirectorySeparator + 'CF_' + IntToStr(cnt) + '.xlt';
end;

function archi_try_of_iter(CarpetaSala: string; cnt: integer): string;
begin
  Result := CarpetaSala + DirectorySeparator + carpeta_CFsYTrys +
    DirectorySeparator + 'trys_' + IntToStr(cnt) + '.xlt';
end;

// retorna el contador del último juego de CF y Trayectorias guardado
// en carpeta_CFsYTrys
function ultimo_CFsYTrys(CarpetaSala: string): integer;
var
  k: integer;
  archi: string;
begin
  k:= 0;
  while FileExists(archi_CF_of_iter(carpetaSala, k)) and FileExists(
      archi_try_of_iter(CarpetaSala, k)) do
    Inc(k);
  Result := k - 1;
end;


procedure xo_ayuda(prms: TInterpreteParametros);
begin
  writeln('tractorcito    SimSEE v' + uversiones.vSimSEESimulador_);
  writeln('Sintaxis: ');
  writeln('    tractorcito xo={orden} {otros parametros}');
end;


procedure xo_testmem(prms: TInterpreteParametros);
var
  archiSala: string;
  sala: TSalaDeJuego;
begin
  writeln('inicio');
  uInicioYFinal.AlInicio;


  archiSala := prms.valStr('sala');

  sala := usalasdejuego.cargarSala(0, archiSala, '', True);
  sala.Free;

  uInicioYFinal.AlFinal;
  writeln('fin');
end;


procedure tractorcito_cmd;
var
  prms: TInterpreteParametros;
  xo: string;
begin
  prms := TInterpreteParametros.Create;
  xo := prms.valStr('xo');
  if (xo = 'sim') or (xo = 'opt') then
    xo_sim(prms)
  else if xo = 'cfmpnulo' then
    xo_cf_mp_nulo(prms)
  else if xo = 'cfcerebronulo' then
    xo_cf_Cerebro_nulo(prms)
  else if xo = 'testmem' then
    xo_testmem(prms)
  else
    xo_ayuda(prms);
  prms.Free;
end;

function UpdateCF_npfq(Trayectorias: TTrayectorias; pesosForward: TVectR;
  q: NReal): NReal;
begin
  Result := Trayectorias.Recalcular(pesosForward, q);
end;

procedure TractorcitoRunOptimizar(sala: TSalaDeJuego; dtIni, dtFin: TDateTime;
  EstadosIniciales: TNubeDePuntos_XEstado; SemillasIniciales: TDAOfNInt;
  var CF, CF_Enganche: TAdminEstados; nHilos: integer;
  var Trayectorias: TTrayectorias;
  parsimonia_tipo: integer;
  parsimonia_nPotencias: integer;
  parsimonia_nArmonicos: integer);

var
  pesosForward: TVectR;
  q: NReal;
  e2: NReal;
  cnt, cntIter: integer;
  tam0, tamCNT: cardinal;
  learningRate: NReal;
  e2Ant: NReal;

  k: integer;
  eMaxAbs: NReal;
  VE_CF_MUSD: NReal;

  parsimonia: TParsimonia;

begin
  sala.CalcularValoresGlobs;
  q := sala.globs.fActPaso;

  pesosForward := TVectR.Create_init(1);
  for k := 1 to pesosForward.n do
    pesosForward.pon_e(k, 1.0 / pesosForward.n);


  tam0 := GetHeapStatus.TotalAllocated;

  cnt := -1;
  cntIter:= 0;

  learningRate:=1; // Con esto implementa un filtro del tipo Ys = Y*a + Xs(1-a)
  e2Ant:= -1;

  if DirectoryExists( carpeta_CFsYTrys ) then
  begin

    // buscamos la ultima iteración y cargamos el CF
    cnt:= ultimo_CFsYTrys( sala.dirSala );
    if cnt >= 0 then
    begin
      // Cargamos el CF y las trayectorias para continuar la iteración
      if CF <> nil then
        CF.Free;
      CF:= TAdminEstados.CreateReadFromArchi( archi_CF_of_iter( sala.DirSala, cnt ));
      if Trayectorias <> nil then
        Trayectorias.Destroy;
      Trayectorias := TTrayectorias.CreateFromXLTArchi(
        archi_try_of_iter(sala.DirSala, cnt));
    end;
  end
  else
    MkDir( carpeta_CFsYTrys );


  if cnt < 0 then
  begin
    cnt:= 0;
    CF.WriteToArchi( archi_CF_of_iter( sala.DirSala, cnt) );
    Trayectorias.WriteToXLTArchi( archi_try_of_iter( sala.DirSala, cnt));
  end;

  repeat
    Inc(cnt);
    Inc(cntIter);
    VE_CF_MUSD := TractorcitoRunSimular(sala, dtIni, dtFin, EstadosIniciales,
      SemillasIniciales, CF, CF_Enganche, nHilos, Trayectorias);

    e2:= Trayectorias.Recalcular( pesosForward, q );


    if CF is TAdminEstadosMultiPlano then
      eMaxAbs := AjusteCFMultiplanos_1PLano_(CF as TAdminEstadosMultiPlano,
        Trayectorias, learningRate)
    else if CF is TAdminEstadosCerebro then
    begin
      {$IFDEF PROBANDO_PARSIMONIA}
      parsimonia:= TParsimonia.Create( 7.0, 365.2425, 0, 0 , (CF as TAdminEstadosCerebro).Cerebros[1].DimParametros );
      {$ELSE}
      parsimonia:= nil;
      {$ENDIF}
      eMaxAbs := AjusteCFCerebro(CF as TAdminEstadosCerebro, Trayectorias, parsimonia, learningRate);
      parsimonia.Free;
    end
    else
      raise Exception.Create('No está implementado el ajuste para la clase CF: ' +
        CF.ClassName);


    writeln('e2: ', e2, ', eMaxAbs: ', eMaxAbs, ', VE_CF_MUSD: ', VE_CF_MUSD );
    tamCNT := GetHeapStatus.TotalAllocated;
    writeln('CNT: ', cnt, ' TotalAllocated: ', tamCNT, ' TAM-0: ', tamCNT - Tam0);

    CF.WriteToArchi( archi_CF_of_iter( sala.DirSala, cnt ) );
    Trayectorias.WriteToXLTArchi( archi_try_of_iter( sala.DirSala, cnt ));

    until (cntIter > 10000) or (eMaxAbs < 1e-5) or ( learningRate < 1e-9 ) ;

  pesosForward.Free;
end;




procedure xo_cf_mp_nulo(prms: TInterpreteParametros);
var
  sala: TSalaDeJuego;
  CF: TAdminEstados;
  archiSala: string;
  archiCF: string;

  dtIni, dtFin: TDateTime;
  nHiperplanos: integer;

begin
  archiSala := prms.valStr('sala');
  archiCF := prms.valStr('CF');

  nHiperplanos := prms.valInt('nHiperplanos', 1);

  uInicioYFinal.AlInicio;
  sala := usalasdejuego.cargarSala(0, archiSala, '', True);
  chdir(sala.dirSala);
  dtIni := prms.valDateTime('dtIni', Sala.globs.fechaIniSim.dt);
  dtFin := prms.valDateTime('dtFin', Sala.globs.fechaFinSim.dt);
  if archiCF = '' then
    archiCF := sala.ArchiCF_bin();

  CF := CrearCFMultiPlano_NULO(sala, dtIni, dtFin, nHiperplanos);

  setSeparadoresGlobales;
  CF.WriteToArchi( archiCF );
  setSeparadoresLocales;

  CF.Free;

  sala.Free;
  uInicioYFinal.AlFinal;
end;


procedure xo_cf_Cerebro_nulo(prms: TInterpreteParametros);
var
  sala: TSalaDeJuego;
  CF: TAdminEstados;
  archiSala: string;
  archiCF: string;

  dtIni, dtFin: TDateTime;
  nHiperplanos, nCapas: integer;

  sArrNPCI: string;
  arrNPorCapa: TDAOfNInt;
  tiposSaturacionCapa: TDAOfTipoSaturacion;
  k: integer;

begin
  archiSala := prms.valStr('sala');
  archiCF := prms.valStr('CF');

  sArrNPCI := prms.valStr('ArrNPCI');
  sArrNPCI := sArrNPCI + ';1;1'; // agregamos dos capas de salida en serie para que
  // la última neurona meneje el offset y la escala de CF para que la red sea eficiente.
  arrNPorCapa := StrToDAOfNInt(sArrNPCI, ';');

  nCapas:=Length(arrNPorCapa);
  SetLength(tiposSaturacionCapa, nCapas);

  for k:= 0 to nCapas-2 do
    tiposSaturacionCapa[k]:= CTS_Tanh;

  tiposSaturacionCapa[nCapas-1]:=CTS_Lineal;

  uInicioYFinal.AlInicio;
  sala := usalasdejuego.cargarSala(0, archiSala, '', False);
  chdir(sala.dirSala);
  if archiCF = '' then
    archiCF := sala.ArchiCF_bin();

  dtIni := prms.valDateTime('dtIni', Sala.globs.fechaIniSim.dt);
  dtFin := prms.valDateTime('dtFin', Sala.globs.fechaFinSim.dt);

  CF := CrearCFCerebros_NULO(sala, dtIni, dtFin, arrNPorCapa, tiposSaturacionCapa);

  setSeparadoresGlobales;
  CF.WriteToArchi(archiCF);
  setSeparadoresLocales;

  sala.Free;
  CF.Free;
  uInicioYFinal.AlFinal;
end;

procedure xo_cf_Cerebro_especifico(prms: TInterpreteParametros);
var
  sala: TSalaDeJuego;
  CF: TAdminEstados;
  archiSala: string;
  archiCF: string;

  dtIni, dtFin: TDateTime;
  nHiperplanos, k: integer;

  sArrNPCI, pal, sdat, capas: string;
  CapasDef: TDAOfNInt;
  TiposSat: TDAOfTipoSaturacion;

begin
  archiSala := prms.valStr('sala');
  archiCF := prms.valStr('CF');

  pal := prms.valStr('ArrNPCI');


  setlength(CapasDef, 2000);
  setlength(TiposSat, 2000);
  k := 0;
  sdat := pal;
  pal := getNextPalSep(sdat, ',');
  while pal <> '' do
  begin
    capas := getNextPalSep(pal, ';');
    CapasDef[k] := StrToInt(capas);
    TiposSat[k] := StrToTipoSaturacion(pal);
    Inc(k);
    pal := getNextPalSep(sdat, ',');
  end;

  setlength(CapasDef, k + 1);
  setlength(TiposSat, k + 1);
  CapasDef[k] := 1; //cantidad de salidas
  TiposSat[k] := CTS_Lineal; //Tipo de salida



  uInicioYFinal.AlInicio;
  sala := usalasdejuego.cargarSala(0, archiSala, '', False);
  chdir(sala.dirSala);
  if archiCF = '' then
    archiCF := sala.ArchiCF_bin();

  dtIni := prms.valDateTime('dtIni', Sala.globs.fechaIniSim.dt);
  dtFin := prms.valDateTime('dtFin', Sala.globs.fechaFinSim.dt);

  CF := CrearCFCerebros_NULO(sala, dtIni, dtFin, CapasDef,TiposSat);



  setSeparadoresGlobales;
  CF.WriteToArchi(archiCF);
  setSeparadoresLocales;

  sala.Free;
  CF.Free;
  uInicioYFinal.AlFinal;

end;

  procedure xo_sim(prms: TInterpreteParametros);
  var
    sala: TSalaDeJuego;
    EstadosIniciales: TNubeDePuntos_XEstado;
    SemillasIniciales: TDAofNInt;
    CF: TAdminEstados;

    Trayectorias: TTrayectorias;
    archiSala: string;
    archiEstrellas: string;
    archiSemillas: string;
    archiCF: string;

    semilla: integer;
    nEstrellitas, nCronicas: integer;
    dtIni, dtFin: TDateTime;
    nhilos, cantHP: integer;
    archiTrayectorias: string;

    CF_Enganche: TAdminEstados;
    fxlt: textfile;
    fechaIni, fechaFin: TFecha;
    xo:string;

    parsimonia: integer;
    parsimonia_nPotencias, parsimonia_nArmonicos: integer;

  begin

    WriteLn('Empieza el programa');

    archiSala := prms.valStr('sala');
    archiEstrellas := prms.valStr('estrellas');
    archiSemillas := prms.valStr('archisemillas');
    archiCF := prms.valStr('CF');
    archiTrayectorias := prms.ValStr('trayectorias');

    semilla := prms.valInt('semilla');
    nCronicas := prms.valInt('nCronicas');
    dtIni := prms.valDateTime('dtIni',0);
    dtFin := prms.valDateTime('dtFin',0);
    nHilos := prms.valInt('nHilos');
    cantHP := prms.valInt('cantHP');
    xo:=prms.valStr('xo');

    parsimonia:= prms.Valint( 'parsimonia' );
    parsimonia_nPotencias:= prms.Valint( 'parsimonia_nPotencias' );
    parsimonia_nArmonicos:= prms.valInt( 'parsimonia_nArmonicos' );

    WriteLn('Terminé de leer parametros');



    uInicioYFinal.AlInicio;
    sala := usalasdejuego.cargarSala(0, archiSala, '', True);

    WriteLn('Cargué la sala');

    WriteLn('Voy a cambiar de directorio a: ' + sala.dirSala);
    chdir(sala.dirSala);


    if not DirectoryExists( sala.dirResultadosCorrida) then
    begin
      WriteLn('Voy a crear la carpeta: ' + sala.dirResultadosCorrida);
      MkDir(sala.dirResultadosCorrida);
    end;


    if dtIni > 0 then
    begin
      sala.globs.fechaIniSim.dt := dtIni;
      sala.globs.fechaFinSim.dt := dtFin;
      sala.globs.fechaIniOpt.dt := dtIni;
      sala.globs.fechaFinOpt.dt := dtFin;
    end
    else
    begin
      dtIni := sala.globs.fechaIniSim.dt;
      dtFin := sala.globs.fechaFinSim.dt;
    end;

    fechaIni := TFecha.Create_Dt(dtIni);
    fechaFin := TFecha.Create_Dt(dtFin);

    setSeparadoresGlobales;

    if archiSemillas = '' then
    begin
      if semilla > 0 then
        sala.globs.semilla_inicial_sim := semilla;
      if nCronicas > 0 then
        sala.globs.NCronicasSim_global_ := nCronicas;
      WriteLn('Voy a generar las Semillas');
      nCronicas := GenSemillas(SemillasIniciales, sala.globs.semilla_inicial_sim,
        1, sala.globs.NCronicasSim_global_);
    end
    else
    begin
      WriteLn('Voy a cargar las Semillas');
      nCronicas := CargarSemillas(SemillasIniciales, archiSemillas);
    end;

    nCronicas := length(SemillasIniciales);

    if archiEstrellas <> '' then
    begin
      WriteLn('Voy a generar la nube de puntos iniciales');
      EstadosIniciales := TNubeDePuntos_XEstado.CreateFromArchiXLT(archiEstrellas);
      nEstrellitas := EstadosIniciales.NEstadosIniciales;
    end
    else
    begin
      EstadosIniciales := nil;
      nEstrellitas := 1;
    end;

    WriteLn('Voy a crear el CF');
    if archiCF = '' then
    begin
      archiCF := sala.ArchiCF_bin('__principal__');
    end;
    CF := TAdminEstados.CreateReadFromArchi(archiCF);

    if CF = nil then
    begin
      CF := CrearCFMultiPlano_NULO(sala, dtIni, dtFin, cantHP);
      TAdminEstadosMultiPlano( CF ).archiCFMP := archiCF;
      CF.WriteToArchi( archiCF );
      TAdminEstadosMultiPlano(CF).archiCFMP:=archiCF;
    end;
  {  else
      CF.CrearElEspacioTiempo(fechaIni, fechaFin, sala.globs.HorasDelPaso, nil, False, sala.globs.fActPaso );
       }
  

    WriteLn('Terminé de crear el CF');

    WriteLn('Voy a crear las trayectorias');

    Trayectorias := TTrayectorias.Create(dtIni, dtFin, sala.globs.HorasDelPaso /
      24.0, nEstrellitas * nCronicas, CF.nVarsContinuas, CF.nVarsDiscretas);

    if ( (sala.archivoCF_ParaEnganches <> nil) and
      (sala.archivoCF_ParaEnganches.ArchiRef.get_archi <> '') and
      (sala.usarArchivoParaInicializarFrameInicial = 1) ) then
        CF_Enganche := sala.globs.CF.CrearCF_AUX_para_enganaches_CFbin(
          sala.archivoCF_ParaEnganches.ArchiRef.get_archi, sala.dirSala)
    else
      CF_Enganche := nil;

    if xo = 'sim' then
      TractorcitoRunSimular(sala, dtIni, dtFin, EstadosIniciales,
        SemillasIniciales, CF, CF_Enganche, nHilos, Trayectorias);

    if xo = 'opt' then
    begin
      WriteLn('Voy a optimizar');  //Iteración de simulaciones
      TractorcitoRunOptimizar(sala, dtIni, dtFin, EstadosIniciales,
        SemillasIniciales, CF, CF_Enganche, nHilos, Trayectorias,
        parsimonia, parsimonia_nPotencias, parsimonia_nArmonicos );
    end;

    Trayectorias.WriteToXLTArchi('trayectorias.xlt');

    if archiTrayectorias = '' then
      archiTrayectorias := sala.ArchiTrayectorias_bin();

    Trayectorias.WriteToBinArchi(archiTrayectorias);
    CF.WriteToArchi( archiCF );

    setLength(SemillasIniciales, 0);
    EstadosIniciales.Free;
    Trayectorias.Free;
    sala.Free;
    if CF_Enganche <> nil then
      CF_Enganche.Free;
    CF.Free;
  setSeparadoresLocales;
    uInicioYFinal.AlFinal;
  end;

procedure xo_updateCF_npfq(prms: TinterpreteParametros);
var
  archi, archi_out: string;
  q: NReal;
  PesosForward: TVectR;
  Trayectorias: TTrayectorias;
  e2: NReal;

begin
  archi := prms.ValStr('trys');
  archi_out := prms.ValStr('trys_out', archi);

  writeln('Leyendo: ', archi);
  Trayectorias := TTrayectorias.CreateFromArchi(archi);

  PesosForward := prms.ValVectR('pesos');
  q := prms.valFlt('q');

  writeln('ejecutando UpdateCF_npfq ...');
  e2 := UpdateCF_npfq(Trayectorias, PesosForward, q);

  writeln('UpdateCF_npfq ejecutado, e2: ', e2);
  writeln(' Trayectorias actualizadas guardadas en: ', archi_out);

  Trayectorias.WriteToArchi(archi_out);
  PesosForward.Free;
  Trayectorias.Free;

end;


end.
