unit uoddface_genyfx;

{$mode delphi}
{$DEFINE RECHAZAR_INFACTIBLES}


interface

uses
  Classes, SysUtils,
  uoddface,
  Math,
  xmatdefs,
  matreal,
  uauxiliares,
  uunidades,
//  uactores,
  ufechas,
  uDataSetGenerico,
  urosx,
  uInicioYFinal,
  uConstantesSimSEE
  ;


(*
Resolucion generica del problema min_{cajaX}( f(x) )
==========================================

Generalizacion basada en el oddface PIG.
1-  Crear el Problema
  1.1-  Se crea el Problema_genyfx
    1.1.1-  Se descargan de la base de datos los archivos correspondidente al
        problema segun el nid
        Bajamos un archivo .zip que contiene toda la informacion necesaria para
        correr la evaluacion. Todo esto sera lo que se denotara como la SALA.
        {NombreSala,".zip"} contiene "CajasX.xlt" y un ejecutable NombreSala.
    1.1.2-  Se crea un el problema oddface
    1.1.3-  Se crea la sala               ->(2)
    1.1.4-  Se define el largo de las cadenas de ADN
    1.1.5-

  1.2-  Se crea la sala:
    1.2.1-  Se arma la sala
    1.2.2-  Se cargan las restricciones de caja: min, max, NBits y TipoDato.

2-  Explorar
  2.1-  Crea un Explorador Genetico
  2.2-  Crea un Explorador Estimador de Gradiente  --Este esta comentado--
  2.3-  Crea un Explorador Mejorador

3-  Bajar y Crear individuo.
  3.1-  Lee de la base de datos un ADN de individuo en base a un nid
  3.2-  Genera un individuo decodificado

4-  Evalua


*)


type

  { TCaja }

  TCaja = class
    { Caja correspondiente a un parametro del problema.
      Cada parametro tiene una caja con nombre, tipo, numero de bits (por si
      real), minimo y maximo.
      Con la combinacion de las cajas de todos los parametros se arma el ADN.
      }
    Nombre: string;
    EsEntero: boolean; { 1 : es Entero
                          0 : es Real
                         }
    Nbits: integer;    { Numero de bit con que se representa si es Real }
    min: NReal;        { Si es EsEntero se castea a con un round a entero}
    max: NReal;
    constructor CreateFromStr(linea: string);
    destructor Destroy; override;
  end;


  { TSala }

  TSala = class

    NParametros: integer;
    CajasX: array of TCaja;
    Ejecutable: string;
    XE: array of integer;
    XR: array of NReal;
    // X: ...
    // Y: ...
    //  OtraInfo:..
    constructor CreateFromArchi(aDirTmp, aSala: string);
    destructor Destroy; override;
  end;


  TSimCostos_archi = class
    tasa: NReal; // [p.u.] tasa de descuento en por unidad

    cad_VE: NReal; // [MUSD] valor esperado del CAD
    cad_VaR5pe: NReal; // [MUSD] VaR(5%) del CAD
    cad_CVaR5pe: NReal; // [MUSD] CVaR(5%) del CAD
    cad_VE_aux: NReal; // [MUSD] valor esperado del CAD considerando CF_aux al final
    cad_VaR5pe_aux: NReal; // [MUSD] VaR(5%) del CAD considerando CF_aux al final
    vcad: TDAOfNReal; // [MUSD] vector con los CAD de cada crónica

    // [MUSD] vector de los CAD de cada crónica, considerando CF_aux al final
    vcad_aux: TDAOfNReal;

    // [MUSD] vector con los costos directos del paso (CDP) acumulados de cada crónica.
    vcdp: TDAOfNReal;

    // [MUSD] vector con el Costo Futuro de Fin de Juego Actualizado de cad crónica.
    vcffja: TDAOfNReal;

    // [MUSD] vector con el Costo Futuro (auxiliar) de Fin de Juego Actualizado de cad crónica.
    vcffja_aux: TDAOfNReal;

    // [MUSD] vector con las utilidades directas del paso (UDP) acumuladas de cada crónica.
    vudp: TDAOfNReal;

    constructor CreateRead(archi_res: string; NCronicas: integer);
    procedure Free;

  end;


  { Tgenyfx_Problema }

  Tgenyfx_Problema = class(TProblema)
    CarpetaSala: string;
    NombreSala: string;

    constructor Create(dbconx: TDBrosxCon; recProblema: TDataRecord;
      idEjecutor_: integer; tmp_rundir_: string);
    class function CreateFromDB(dbconx: TDBrosxCon; nid_problema: integer;
      idEjecutor_: integer; tmp_rundir_: string): Tgenyfx_Problema;
    function evaluar_(Individuo: TIndividuo; SemillaAleatoria: integer): boolean;
      override;
    destructor destroy; override;

    function CalcularError_fx: NReal;

    function CargarSala(individuo: TIndividuo): string;

    function crearIndividuo(Sala: TSala): TIndividuo;

    function BajarCrearSala(nid: integer; out aIndividuo: TIndividuo): string;

    procedure WriteIndividuoAXLT(aIndividuo: TIndividuo);

  end;




implementation

{ TCaja }

constructor TCaja.CreateFromStr(linea: string);
var
  entradas: array of string;

begin
  entradas := getPalabras_arr(linea, #9);
  Nombre := entradas[0];
  EsEntero := StrToBool(entradas[1]);
  min := StrToFloat(entradas[2]);
  max := StrToFloat(entradas[3]);
  if EsEntero then
    Nbits := 0
  else
    Nbits := StrToInt(entradas[4]);

end;

destructor TCaja.Destroy;
begin
  inherited Destroy;
end;

{ TSala }

constructor TSala.CreateFromArchi(aDirTmp, aSala: string);

var
  archiCajasX: string;
  textoCajasX: TextFile;
  kParametro: integer;
  linea: string;
  linea_arr: TDAofString;
begin
  //  Ubica el archivo CajasX
  archiCajasX := aDirTmp + DirectorySeparator + 'CajasX.xlt';
  AssignFile(textoCajasX, archiCajasX);
  reset(textoCajasX);
  readln(textoCajasX, linea);
  linea_arr := getPalabras_arr(linea, #9);
  //  Lee el numero de parametros
  NParametros := StrToInt(linea_arr[1]);
  readln(textoCajasX, linea);
  setlength(CajasX, NParametros);
  //  Para cada parametros carga el parametros
  for kParametro := 0 to NParametros - 1 do
  begin
    readLn(textoCajasX, linea);
    CajasX[kParametro] := TCaja.CreateFromStr(linea);
  end;
  //  Cierro los archivos usados
  CloseFile(textoCajasX);
  Ejecutable := getDir_Bin + aSala ;

end;

destructor TSala.Destroy;
var
  kParametro: integer;
begin
  inherited Destroy;
  for kParametro := 1 to NParametros do
    CajasX[kParametro].Destroy;
end;



constructor Tgenyfx_Problema.Create(dbconx: TDBrosxCon;
  recProblema: TDataRecord; idEjecutor_: integer; tmp_rundir_: string);

var
  kParametro, kParam, kEtapa, Etapas: integer;
  sala: TSala;
  aParam: TCaja;
  NombreArchi: string;
  NombreArchiArr: TDAofString;

begin
  inherited Create(dbconx, recProblema, idEjecutor_, tmp_rundir_);

  CarpetaSala := tmp_rundir_;

  NombreArchi := extractFileName(ArchiSala);
  NombreArchiArr := getPalabras_arr(NombreArchi,'.');
  NombreSala := NombreArchiArr[0];

  // acomodamos el directorio de la sala
  // nombre del EJCUTABLE
  ArchiSala := tmp_rundir_ + DirectorySeparator + NombreSala;

  // CARGAR/CREAR Sala desde archivo.
  sala := TSala.CreateFromArchi(CarpetaSala,NombreSala);

  // Bien, a partir de Sala (los datos) dimensionar y describir
  // los parámetros CajasX.txt
  setlength(DescriptoresE, 0);
  setlength(DescriptoresR, sala.NParametros);

  if n_etapas <> 0 then
    Etapas := n_etapas
  else
    Etapas := 1;


  for kEtapa := 0 to Etapas - 1 do
    for kParam := 0 to sala.NParametros - 1 do
    begin
      kParametro := kEtapa * sala.NParametros + kParam;
      aParam := Sala.CajasX[kParametro];

      // capaz que hay enteros
      self.DefinirParametroReal(
        kParametro,
        'r' + IntToStr(kEtapa) + '_' + aParam.Nombre,
        aParam.Min, aParam.Max, aParam.nbits);
    end;

  CalcularLargosADN;
end;

class function Tgenyfx_Problema.CreateFromDB(dbconx: TDBrosxCon;
  nid_problema: integer; idEjecutor_: integer; tmp_rundir_: string): Tgenyfx_Problema;
var
  ds: TResultadoQuery;
  r: TDataRecord;
  ap: Tgenyfx_Problema;
  sql: string;
begin
  sql := 'SELECT * FROM ofe_problemas WHERE nid = ' + IntToStr(
    nid_problema) + ' LIMIT 1 ';
  ds := dbconx.sql_query(sql);
  ds.Next;
  r := ds.getFilaActiva;
  if r <> nil then
    ap := Tgenyfx_Problema.Create(dbconx, r, idEjecutor_, tmp_rundir_)
  else
    ap := nil;
  ds.Free;
  Result := ap;
end;


constructor TSimCostos_archi.CreateRead(archi_res: string; NCronicas: integer);
var
  f: textfile;

begin
  inherited Create;
  setSeparadoresGlobales;

  Assign(f, archi_res);
  {$I-}
  reset(f);
  {$I+}
  if ioresult <> 0 then
    raise Exception.Create('No encontré archivo: ' + archi_res);

  //1.00000000000000E-001  [p.u.]  Tasa de descuento anual.
  system.readln(f, tasa);

  //1.56194772422336E+003  [MUSD]  Costo esperado
  system.readln(f, cad_VE);

  //  1.64347173169052E+003  [MUSD]  Costo con riesgo 5% de ser excedido.
  system.readln(f, cad_VaR5pe);

  //  1.72340935051501E+003  [MUSD]  Costo CVaR(5%).
  system.readln(f, cad_CVaR5pe);

  //0.00000000000000E+000  [MUSD]  Costo auxiliar esperado (cdp+CFaux)
  system.readln(f, cad_VE_aux);

  //0.00000000000000E+000  [MUSD]  Costo auxiliar con riesgo 5% de ser excedido. (cdp+CFaux)
  system.readln(f, cad_VaR5pe_aux);

  closefile(f);
  setSeparadoresLocales;
end;



procedure TSimCostos_archi.Free;
begin
  setlength(vcad, 0);
  setlength(vcad_aux, 0);
  setlength(vcdp, 0);
  setlength(vcffja, 0);
  setlength(vcffja_aux, 0);
  inherited Free;
end;




function Tgenyfx_Problema.CargarSala(individuo: TIndividuo): string;

begin
  Result := individuo.problema.archiSala;

end;


function Tgenyfx_Problema.crearIndividuo(Sala: TSala): TIndividuo;
var
  Individuo: TIndividuo;
begin
  Individuo := TIndividuo.CreateNew(dbcon, self);
  Result := Individuo;
end;


function Tgenyfx_Problema.BajarCrearSala(nid: integer;
  out aIndividuo: TIndividuo): string;

{ GFlieller@2019-05-14 12:53
SI NO SE USA BORRARLO - #QUITARCODIGO
var
  EsFactible: boolean;
  Escenario: string;
  GradoInfactibilidad: NReal;
  }
begin
  aIndividuo := LeerIndividuo(nid);
  decodificar_adn(aIndividuo);
  Result := CargarSala(aIndividuo);
end;

function Tgenyfx_Problema.evaluar_(Individuo: TIndividuo;
  SemillaAleatoria: integer): boolean;
{
================================================================================
            ** Hereda de Tproblema.evaluar de uoddface, por eso tiene la entrada
            ** SemillaAleatoria pero este tipo de problema no tiene semilla.
5-  Evaluar
        5.1-  Escribir x.xlt en funcion del individuo
  5.2-  Correr el evaluador (calcular el error).
    5.2.1-  Armo los parametros
    5.2.2-  Corro el programa evaluador con RunChildAndWait
          5.2.3-  Leo el resultado
          5.2.4-  Devuelvo el resultado
================================================================================
}
var
  res_SimCosto: NReal;
{ GFlieller@2019-05-14 12:53
SI NO SE USA BORRARLO - #QUITARCODIGO
  archiSalaModificada: string;
  MontoInversiones: double;
  EsFactible: boolean;
  Escenario: string;
  GradoInfactibilidad: NReal;
  k: integer;
}
begin
  WriteIndividuoAXLT(Individuo);

  res_SimCosto := CalcularError_fx;

  Individuo.f_histo := TVectR.Create_Init( N_DiscrtetizacionesHistograma );
  Individuo.f_histo.pon_e(1, res_SimCosto);
  Individuo.f_VE := res_SimCosto; // Este es el ERROR.
  Individuo.f_VaR := res_SimCosto;
  Individuo.f_CVaR := res_SimCosto;
  Individuo.f_MIN := res_SimCosto;
  Individuo.f_MAX := res_SimCosto;
  Individuo.f_Objetivo := ro_VE * Individuo.f_VE + ro_VaR *
    Individuo.f_VaR + ro_CVaR * Individuo.f_CVaR;

  Result := True;

end;


destructor Tgenyfx_Problema.destroy;
begin
  inherited destroy;
end;

function Tgenyfx_Problema.CalcularError_fx: NReal;
var
  params: array of string;
  archiSalida, linea, ejecutable: string;
  textoSalida: textFile;
  salida: NReal;
  nombre_arr: TDAofString;
begin
  //  Correr el evaluador.
  chdir(CarpetaSala);
  setlength(params, 1);
  nombre_arr := getPalabras_arr(NombreSala, '.');
  ejecutable := uconstantessimsee.getDir_Bin + nombre_arr[0];
  params[0] := CarpetaSala;
  RunChildAndWAIT(ejecutable, params);

  //  Leer la salida.
  archiSalida := carpetaSala + DirectorySeparator + 'y.xlt';
  AssignFile(textoSalida, archiSalida);
  reset(textoSalida);
  readln(textoSalida, linea);

  // Convertir la salida a NReal.
  salida := StrToFloat(linea);
  Result := salida;

  // Cierro archivos
  CloseFile(textoSalida);
end;

procedure Tgenyfx_Problema.WriteIndividuoAXLT(aIndividuo: TIndividuo);
var
  archiX: string;
  textoX: textFile;
  kParametro: integer;
begin
  archiX := CarpetaSala + DirectorySeparator + 'x.xlt';
  AssignFile(textoX, archiX);
  Rewrite(textoX);

  if (aIndividuo.XE <> nil) then
  begin
    for kParametro := 1 to aIndividuo.XE.n do
      WriteLn(textoX, IntToStr(aIndividuo.XE.e(kParametro)));
  end;

  if (aIndividuo.XR <> nil) then
  begin
    for kParametro := 1 to aIndividuo.XR.n do
      WriteLn(textoX, FloatToStr(aIndividuo.XR.e(kParametro)));
  end;

  CloseFile(textoX);

end;

end.
