unit uoddface_redcegh;

{rch@201311050726
  Defininición del tiepo de probelma oddface RedCEGH
La intención de este problema obtener el reductor de estaods de un
CEGH fijada la dimensión del estado reducido deseado que minimiza el
costo esperado de oepración del sietema.
  Este trabajo se inicia con SimSEE v421_BOGA_ y en esta versión la
matriz de reducción es UNA sola en los CEGHs. Al plantera este problema
de optimización surge naturalmente plantearse dejar que las matrices
reductoras puedan ser variables durante el períododo estacional al igual
que las matrices del filtro lineal trabajo que se deja para el futuro.

  Por ahora vamos a suponer que la reducción es a única dimensión por
simplicidad. Luego aumentaremos la posibilidad de mayores dimensiones
del espacio de llegada. Posiblmente la solución se en base a declarar
un problema "en cascada" en que se van agregando dimensiones de forma
tal que cada una va agregando información ??? PARA PENSAR
 }


{$mode delphi}

interface

uses
  Classes, SysUtils,
  uoddface,
  Math,
  xmatdefs,
  matreal,
  uauxiliares,
  uactores,
  ufuentesintetizador,
  ufechas,
  usalasdejuego,
  uDataSetGenerico,
  urosx,
  uInicioYFinal,
  umodelosintcegh,
  uconstantessimsee;

type
  TRedCEGH_infoProblema = class
    archi_modelo_cegh: string; // Nombre de la fuenete en SimSEE
    Modelo_CEGH: TModeloCEGH;
    class function ReadFromRec(var r: TDataRecord;
      tmp_rundir_: string): TRedCEGH_infoProblema;
    class function ReadFromDB(dbconx: TDBrosxCon; nid_problema: integer;
      tmp_rundir_: string): TRedCEGH_infoProblema;
    procedure Free;
  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_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
    vcad_aux: TDAOfNReal;
    // [MUSD] vector de los CAD de cada crónica, considerando CF_aux al final
    vcdp: TDAOfNReal;
    // [MUSD] vector con los costos directos del paso (CDP) acumulados de cada crónica.
    vcffja: TDAOfNReal;
    // [MUSD] vector con el Costo Futuro de Fin de Juego Actualizado de cad crónica.
    vcffja_aux: TDAOfNReal;
    // [MUSD] vector con el Costo Futuro (auxiliar) de Fin de Juego Actualizado de cad crónica.

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

  end;


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

    InfoProblema: TRedCEGH_infoProblema;

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

    function RunOptSim(sala: string;
      NCronicas, semillaAleatoria_: integer): TSimCostos_archi;

    // Modifica las matrices y retorna TRUE si el individuo es factible y FALSE en caso
    // contrario.
    function Modificar_MatricesRedAmp(individuo: TIndividuo): boolean;

    function IndividuoToSalaSimSEE(nid: integer; var MontoInversiones: NReal): string;

  end;




implementation




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

var
  kPar: integer;
  nf, nc: integer;
  npars: integer;

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

  // calculamos carpeta de la sala y nombre
  //  CarpetaSala := extractFilePath( ArchiSala );
  CarpetaSala := tmp_rundir_;
  NombreSala := extractFileName(ArchiSala);
  // acomodamos el directorio de la sala
  ArchiSala := tmp_rundir_ + DirectorySeparator + NombreSala;
  if (pos('.ese', NombreSala) > 0) then
    Delete(NombreSala, pos('.ese', NombreSala), length(NombreSala) -
      pos('.ese', NombreSala) + 1);


  // cargamos la informa
  InfoProblema := TRedCEGH_infoProblema.ReadFromDB(dbconx, nid_Problema, tmp_rundir);


  // Bien, ahora que tenemos la información del problema  creamos
  // los descriptores de ADN

  setlength(DescriptoresE, 0);

  nc:= InfoProblema.Modelo_CEGH.Dim_X; // dimension del espacio sin reducir
  nf:= InfoProblema.Modelo_CEGH.Dim_XRed; // dimensión del espacio reducido
  npars:= nf*nc - ((nf+1)*nf) div 2;
  (*
    Este cálculo tiene en cuenta que para la primer fila de la
    matriz de reducción de tiene nc-1 grados de libertad para la segunda
    un grado menos y así sucesivamente.
  *)

  setlength(DescriptoresR, npars );
  for kPar := 0 to high(DescriptoresR) do
    DefinirParametroReal(kPar, 'r_' + IntToStr(kPar), -1, 1, 16);
  CalcularLargosADN;
end;

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


function nextpal_sep(var r: string; sep: string): string;
var
  res: string;
  i: integer;
begin
  r := trim(r);
  i := pos(sep, r);
  if i > 0 then
  begin
    res := trim(copy(r, 1, i - 1));
    Delete(r, 1, i);
  end
  else
  begin
    res := r;
    r := '';
  end;
  Result := res;
end;


function nextpal_tab(var r: string): string;
begin
  Result := nextpal_sep(r, #9);
end;


procedure readln_vect(var f: textfile; var v: TDAOfNreal; N: integer);
var
  r: string;
  pal: string;
  k: integer;
begin
  setlength(v, N);
  system.readln(f, r);
  pal := nextpal_tab(r); // caption
  for k := 0 to high(v) do
  begin
    pal := nextpal_tab(r);
    v[k] := StrToFloat(pal);
  end;
end;



class function TRedCEGH_infoProblema.ReadFromRec(var r: TDataRecord;
  tmp_rundir_: string): TRedCEGH_infoProblema;
var
  res: TRedCEGH_infoProblema;
begin
  res := TRedCEGH_infoProblema.Create;
  res.archi_modelo_cegh := r.GetByNameAsString('data');
  res.Modelo_CEGH := TModeloCEGH.CreateFromArchi(
    tmp_rundir_ + DirectorySeparator + res.archi_modelo_cegh);
  Result := res;
end;



procedure TRedCEGH_infoProblema.Free;
begin
  Modelo_CEGH.Free;
  inherited Free;
end;



class function TRedCEGH_infoProblema.ReadFromDB(
  dbconx: TDBrosxCon;
  nid_problema: integer;
  tmp_rundir_: string): TRedCEGH_infoProblema;
var
  res: TRedCEGH_infoProblema;
  sql: string;
  rec: TDataRecord;

begin
  res := nil;
  sql := 'SELECT * FROM ofe_RedCEGH_data WHERE nid_problema = ' +
    IntToStr(NID_Problema) + ' LIMIT 1 ';
  rec := dbconx.sql_ficha(sql);
  if rec <> nil then
  begin
    res := TRedCEGH_infoProblema.ReadFromRec( rec, tmp_rundir_ );
    rec.Free;
  end
  else
    raise Exception.Create( 'Error no pude encontrar el registro del problema: '+IntToStr( nid_problema )+' en la tabla: ofe_RedCEGH_data. Debe editar la información del problema y completar la información adicional.' );
  Result := res;
end;




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

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

  system.readln(f, tasa);
  system.readln(f, cad_VE);
  system.readln(f, cad_VaR5pe);
  system.readln(f, cad_VE_aux);
  system.readln(f, cad_VaR5pe_aux);

  readln_vect(f, vcad, NCronicas);
  readln_vect(f, vcad_aux, NCronicas);
  readln_vect(f, vcdp, NCronicas);
  readln_vect(f, vcffja, NCronicas);
  readln_vect(f, vcffja_aux, NCronicas);

  closefile(f);
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;




// retorna NIL si falla algo
function TRedCEGH_Problema.RunOptSim(sala: string;
  NCronicas, semillaAleatoria_: integer): TSimCostos_archi;
var
  cmd: string;
  params: array of string;
  archi_simcosto: string;

begin

  if tmp_rundir = '' then
    archi_simcosto := getDir_run + NombreSala + DirectorySeparator
  else
    archi_simcosto := getDir_run;

  limpiarCarpeta(archi_simcosto, 'simres_*');
  limpiarCarpeta(archi_simcosto, 'simcosto_*');
  limpiarCarpeta(archi_simcosto, 'estado_fin_cron_*');


  setlength(params, 3);
  params[0] := 'sala=' + sala;
  params[1] := 'ejecutor=' + IntToStr(idEjecutor);
  params[2] := 'semilla=' + IntToStr(semillaAleatoria_);

{$IFDEF LINUX}
  cmd := getDir_bin + 'cmdopt';
{$ELSE}
  cmd := getDir_bin + 'cmdopt.exe';
{$ENDIF}
  if not RunChildAndWAIT(cmd, params) then
    raise Exception.Create('no puede correr la cmdopt');


{$IFDEF LINUX}
  cmd := getDir_bin + 'cmdsim';
{$ELSE}
  cmd := getDir_bin + 'cmdsim.exe';
{$ENDIF}
  setlength(params, 4);
  params[0] := 'sala=' + sala;
  params[1] := 'ejecutor=' + IntToStr(idEjecutor);
  params[2] := 'semilla=' + IntToStr(semillaAleatoria_);
  params[3] := 'NCronicasSim=' + IntToStr(NCronicas);

  if not RunChildAndWAIT(cmd, params) then
    raise Exception.Create('no puede correr la cmdsim');
  setlength(params, 0);


  try
    if tmp_rundir = '' then
      archi_simcosto := getDir_run + NombreSala + DirectorySeparator
    else
      archi_simcosto := getDir_run;

    archi_simcosto := archi_simcosto + DirectorySeparator + 'simcosto_' + IntToStr(
      semillaAleatoria_) + 'x' + IntToStr(NCronicas) + '_base.xlt';
    Result := TSimCostos_archi.CreateRead(archi_simcosto, NCronicas);
  except
    Result := nil;
    raise Exception.Create('Error al leer archivo de resultados uplanes.425: ' +
      archi_simcosto);
  end;

end;



// calcula los x y si es necesario canoniza el individuo (cambiando el signo)
// El resultado es false si no fue necesaria canonizar y true si fue necesario canonizar.
function AlfaToXCanonizado(var alfa, x: TVectR): boolean;
var
  j, j_max: integer;
  a_max, abs_x: Nreal;
  r2: nreal;
  b: integer;
  res: boolean;
begin

  b := x.n;
  x.pv[1] := alfa.pv[1];
  r2 := 1 - sqr(x.pv[1]);

  for j := 2 to b - 1 do
  begin
    if r2 > 0 then
    begin
      x.pv[j] := sqrt(r2) * alfa.pv[j];
      r2 := r2 - sqr(x.pv[j]);
    end
    else
      x.pv[j] := 0.0;
  end;

  if r2 > 0 then
  begin
    x.pv[b] := sqrt(r2);
    res:= false;
  end
  else
  begin
    j_max := 1;
    a_max := abs(x.pv[1]);
    for j := 1 to b - 1 do
    begin
      abs_x := abs(x.pv[1]);
      if abs_x > a_max then
      begin
        a_max := abs_x;
        j_max := j;
      end;
    end;

    if x.pv[j_max] < 0 then
    begin
      alfa.PorReal( -1.0 );
      x.PorReal( -1.0 );
      res:= true;
    end;
  end;
  result:= res;
end;



function TRedCEGH_Problema.Modificar_MatricesRedAmp(individuo: TIndividuo): boolean;
var
  sala: TSalaDejuego;
  kParametro: integer;
  fcegh: TFuenteSintetizadorCEGH;
  Red_Info: TRedCEGH_infoProblema;

  archi_modelo_bin: string;
  MaxUnidadesGeneradorasActivas: integer;
  x: TVectR;
  canonizado: boolean;
  fsal: textfile;

begin
  // cargamos la sala de juegos_base

  chdir(carpetaSala);
  Red_info := TRedCEGH_Problema(individuo.problema).InfoProblema;

  (**
    // Aquí habría que fijarse si la matriz de reducción tiene más de una fila en cuyo
    caso hay que calcular una base del subespacio ortogonal a las primeras filas de
    la matriz M_a obtenida de MRed quitando la ultima fila.
    La base deel subespacio ortogonal a M_a ordenado en filas nos da la matriz M_b
    Si solo estamos buscando una dirección del espacio (o sea la reducción es a una dimensión)
    M_a = O y M_b = I.
    // Por ahora supongo que estamos en el caso M_a = O y M_b = I que es lo que aplicaría
    en el problema que nos preocupa de reducir el modelo estocástico BPS50 a una variable
    de H de estado hidrológico.
  *)
  if Red_info.Modelo_CEGH.MRed_cte <> nil then
    x:= TVectR.Create_Init( Red_info.Modelo_CEGH.MRed_cte.nc -  (Red_info.Modelo_CEGH.MRed_cte.nf - 1 ))
  else
    x:= TVectR.Create_Init( Red_info.Modelo_CEGH.MAmp_mc[0].nc -  (Red_info.Modelo_CEGH.MAmp_mc[0].nf - 1 ));

  canonizado := AlfaToXCanonizado( Individuo.XR, x );

  (* ahora hay que componer la ultima fila de MRed como combinación lineal de las filas de M_b
  con los coeficientes x resultantes
  // Como por ahora M_b = I se simplifica el calculo
  *)

  Red_info.Modelo_CEGH.MRed_cte.Fila( Red_info.Modelo_CEGH.MRed_cte.nf ).Igual( x );

  assignfile( fsal, 'MRed_UltimaFila.xlt' );
  rewrite( fsal );
  x.StoreInFile( fsal );
  closefile( fsal );
  x.Free;

  Red_info.Modelo_CEGH.Calcular_Matrices_Ampliacion;

  assignfile( fsal, 'MRed.xlt' );
  rewrite( fsal );
  Red_info.Modelo_CEGH.MRed_cte.WriteXLT( fsal );
  closefile( fsal );

  assignfile( fsal, 'MAmp_cte.xlt' );
  rewrite( fsal );
  Red_info.Modelo_CEGH.MAmp_cte.WriteXLT( fsal );
  closefile( fsal );

  assignfile( fsal, 'MBAmp_cte.xlt' );
  rewrite( fsal );
  Red_info.Modelo_CEGH.BAmp_cte.WriteXLT( fsal );
  closefile( fsal );


  archi_modelo_bin := ChangeFileExt(Red_info.archi_modelo_cegh, '.bin');
  Red_info.Modelo_CEGH.WriteToArchi_bin(archi_modelo_bin);

  Result := True;
end;


function TRedCEGH_Problema.IndividuoToSalaSimSEE(nid: integer;
  var MontoInversiones: NReal): string;
var
  a: TIndividuo;
  EsFactible: boolean;
begin
  a := LeerIndividuo(nid);
  decodificar_adn(a);
  EsFactible := Modificar_MatricesRedAmp(a);
  Result := self.archiSala;
end;

function TRedCEGH_Problema.evaluar_(Individuo: TIndividuo;
  SemillaAleatoria: integer): boolean;
var
  res_SimCostos: TSimCostos_archi;
  MontoInversiones: double;
  EsFactible: boolean;
begin
  EsFactible := Modificar_MatricesRedAmp(Individuo);
  if EsFactible then
  begin
    res_SimCostos := RunOptSim(archiSala, NCronicasCronicasPorVez,
      SemillaAleatoria);
    if res_SimCostos <> nil then
    begin
      Individuo.f_histo := TVectR.Create_FromDAofR(res_SimCostos.vcad);
      Individuo.f_histo.MasReal(MontoInversiones);
      Individuo.f_VE := res_SimCostos.cad_VE + MontoInversiones;
      Individuo.f_VaR := Individuo.f_histo.pe_VaR(pe_var);
      Individuo.f_CVaR := Individuo.f_histo.pe_CVaR(pe_var);
      Individuo.f_MIN := individuo.f_histo.e(1);
      Individuo.f_MAX := individuo.f_histo.e(individuo.f_histo.n);
      Individuo.f_Objetivo := ro_VE * Individuo.f_VE + ro_VaR *
        Individuo.f_VaR + ro_CVaR * Individuo.f_CVaR;
      res_SimCostos.Free;
      Result := True;
    end
    else
      raise Exception.Create('Falló la simulación.');
  end
  else
    Result := False;
end;


procedure TRedCEGH_Problema.Free;
begin
  infoProblema.Free;
  inherited Free;
end;

end.
