Changeset c8a5446 in tol


Ignore:
Timestamp:
Mar 30, 2012, 9:19:20 AM (15 years ago)
Author:
Víctor de Buen <vdebuen@…>
Branches:
1941-docker-image-to-test, devel, master
Children:
6768766
Parents:
7427dd4c
Message:

Refs #1342
Applies enhancements of BVMat::trunc_std_gaussian to BTruncatedNormalDist

git-svn-id: ​https://www.tol-project.org/svn/tolp/trunk@4475 be4846ae-f051-483a-831f-b64e7d08527f

Location:
tol
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • tol/bmath/bprdist/prd.cpp

    r7427dd4c rc8a5446  
    893893
    894894//--------------------------------------------------------------------
     895BDat BTruncatedNormalDist::Random01(
     896  BDat lower_, BDat upper_, BDat borderDistance_)
     897//--------------------------------------------------------------------
     898{
     899  double lower = lower_.Value();
     900  double upper = upper_.Value();
     901  double borderDistance = borderDistance_.Value();
     902  double  negInf = BDat::NegInf();
     903  double  posInf = BDat::PosInf();
     904  double tn;
     905  if((lower>negInf)||(upper<posInf))
     906  {
     907    //Checking different cases
     908    //lower = 66.8902; upper = posInf;
     909    //upper = -66.8902; lower = negInf;
     910
     911    //A continuación se generará una N(0,1) truncada estrictamente en el
     912    //interior del intevalo de dominio abierto (lower, upper) para evitar
     913    //problemas numéricos de frontera.
     914
     915    //Máximo valor absoluto para el que se usará la distribución N(0,1)
     916    double K = 6;
     917    //La probabilidad de estar una N(0,1) fuera del intervalo [-K,K] es
     918    //con K=6 es p=1.973175400848959e-009 ~ 1 entre 500 mil millones, por lo que
     919    //es despreciable cualquier valor fuera del mismo y nos evitamos problemas
     920    //numéricos, pues tanto la cumulativa como su inversa funcionan bien
     921    //en este intervalo pero empiezan a fallar a partir de K=8.
     922    //Quizás podría ponerse K=7 pero se toma K=6 por dar un margen y porque
     923    //la pérdida de precisión es irrelevante tan lejos de la moda.
     924    //Fuera de ahí la distribución se aproximará por la raíz cuadrada de una
     925    //uniforme en el dominio reducido interior al original de tal forma que
     926    //el que el cociente de verosimilitudes entre los extremos sea menor o
     927    //igual a 100, pues en ese rango resulta una buena aproximación de la
     928    //normal truncada y es al mismo tiempo tratable numéricamente.
     929    //
     930    //El cociente de densidades entre dos puntos x,y ~ N(0,1) es
     931    //
     932    //  f(x)/f(y) = Exp(-0.5*(x^2-y^2))
     933    //
     934    //Si queremos un punto y que sea h veces menos probable que x entonces
     935    //
     936    //  Log(h) = -0.5*(x^2-y^2)
     937    //  y^2 = x^2 + 2*Log(h)
     938    //
     939    //Estas operaciones no comportan riesgos numéricos incluso para valores
     940    //muy grandes.
     941    double lowMrg = lower;
     942    double uppMrg = upper;
     943    if(lowMrg>K)
     944    {
     945      if(uppMrg>=2*lowMrg) { uppMrg=2*lowMrg; }
     946      //Si todo el dominio está por encima de K tomamos como nuevos límites
     947      //tentativos los puntos con densidad 1.1 y 110 veces las del límite inferior.
     948      //De esta forma el nuevo límite inferior es por construcción 100 veces más
     949      //probable que el superior
     950      double lowMrg_ = sqrt(pow(lowMrg,2) + 2.0 * log(  1.1) );
     951      double uppMrg_ = sqrt(pow(lowMrg,2) + 2.0 * log(110.0) );
     952      //Si los límites tentativos se salen del dominio hay que meterlos dentro
     953      if(lowMrg_>=upper)
     954      {
     955        lowMrg = 0.99*lowMrg+0.01*uppMrg;
     956        uppMrg = 0.99*uppMrg+0.01*lowMrg;
     957      }
     958      else
     959      {
     960        lowMrg = lowMrg_;
     961        if(uppMrg_>=upper)
     962        {
     963          uppMrg = 0.99*uppMrg+0.01*lowMrg;
     964        }
     965        else
     966        {
     967          uppMrg = uppMrg_;
     968        }
     969      }
     970      //Tomamos un r uniforme
     971      double r = BUniformDist::Random01().Value();
     972      //y calculamos su raíz cuadrada como valor de ponderación de los límites
     973      //para forzar puntos más cercanos al nuevo límite inferior.
     974      double u = sqrt(r);
     975      tn = u*lowMrg + (1.0-u)*uppMrg;
     976      if((tn<=lower) ||(tn>=upper))
     977      {
     978        Warning(BText("(I) TruncStdGaussian [Case C.1] ")<<
     979          " domain:["<<lower<<","<<upper<<"] -> "<<
     980          " interior domain: ["<<lowMrg<<","<<uppMrg<<"] -> "<<tn);
     981        return(-1);
     982      }
     983    }
     984    else if(upper<-K)
     985    {
     986      if(lowMrg<=2*uppMrg) { lowMrg=2*uppMrg; }
     987      //Si todo el dominio está por debajo de -K tomamos como nuevos límites
     988      //tentativos los puntos con densidad 1.1 y 110 veces las del límite superior.
     989      //De esta forma el nuevo límite superior es por construcción 100 veces más
     990      //probable que el inferior
     991      double uppMrg_ = -sqrt(pow(uppMrg,2) + 2.0 * log(  1.1) );
     992      double lowMrg_ = -sqrt(pow(uppMrg,2) + 2.0 * log(110.0) );
     993      //Si los límites tentativos se salen del dominio hay que meterlos dentro
     994      if(uppMrg_<=lower)
     995      {
     996        lowMrg = 0.99*lowMrg+0.01*uppMrg;
     997        uppMrg = 0.99*uppMrg+0.01*lowMrg;
     998      }
     999      else
     1000      {
     1001        uppMrg = uppMrg_;
     1002        if(lowMrg_<=lower)
     1003        {
     1004          lowMrg = 0.99*lowMrg+0.01*uppMrg;
     1005        }
     1006        else
     1007        {
     1008          lowMrg = lowMrg_;
     1009        }
     1010      }
     1011      //Tomamos un r uniforme
     1012      double r = BUniformDist::Random01().Value();
     1013      //y calculamos su raíz cuadrada como valor de ponderación de los límites
     1014      //para forzar puntos más cercanos al nuevo límite inferior.
     1015      double u = sqrt(r);
     1016      tn = u*uppMrg + (1.0-u)*lowMrg;
     1017      if((tn<=lower) ||(tn>=upper))
     1018      {
     1019        Warning(BText("(I) TruncStdGaussian [Case C.2] ") <<
     1020          " domain:["<<lower<<","<<upper<<"] -> "<<
     1021          " interior domain: ["<<lowMrg<<","<<uppMrg<<"] -> "<<tn);
     1022        return(-1);
     1023      }
     1024    }
     1025    else
     1026    {
     1027      //Si alguno de los límites cae fuera de [-K,K] hacemos la intersección
     1028      if(lower<-K) { lowMrg=-K; }
     1029      if(upper>+K) { uppMrg=+K; }
     1030      BNormalDist u01(0,1);
     1031      //Calculamos los cuantiles de la N(0,1) en los extremos
     1032      double lowF01 = u01.Dist(lowMrg).Value();
     1033      double uppF01 = u01.Dist(uppMrg).Value();
     1034      //Calculamos el desplazamiento en la métrica probabilística
     1035      double difF01 = borderDistance*(uppF01-lowF01);
     1036      //Desplazamos los límites hacia el interior
     1037      double lowF01_ = lowF01+difF01;
     1038      double uppF01_ = uppF01-difF01;
     1039      //Tomamos un r uniforme
     1040      double r = BUniformDist::Random01().Value();
     1041      //Tomamos un u normal truncado en el dominio interior
     1042      double u = r*uppF01_ + (1.0-r)*lowF01_;
     1043      //Volvemos a la métrica normalizada   
     1044      tn = u01.Inverse(u).Value();
     1045      if((tn<=lower) ||(tn>=upper))
     1046      {
     1047        Warning(BText("(I) TruncStdGaussian [Case C.1] ")<<
     1048          " domain:["<<lower<<","<<upper<<"] -> "<<
     1049          " Prob domain["<<lowF01<<","<<uppF01<<"] -> "<<
     1050          " Prob interior domain["<<lowF01_<<","<<uppF01_<<"] -> "<<tn);
     1051        return(-1);
     1052      }
     1053    }
     1054  }
     1055  else
     1056  {
     1057    tn = BNormalDist::Random01().Value();
     1058    if((tn<=lower) ||(tn>=upper))
     1059    {
     1060      Warning(BText("(I) TruncStdGaussian [Case D] ")<<
     1061        I2("(empty interval",
     1062        "(intervalo vacio")+" ["+BDat(lower).Format("%.16lg")+","+
     1063                                 BDat(upper).Format("%.16lg")+"])");
     1064    }
     1065  }
     1066  return(tn);
     1067}
     1068
     1069//--------------------------------------------------------------------
     1070  BDat BTruncatedNormalDist::Random()
     1071//--------------------------------------------------------------------
     1072{
     1073  BDat nu = normal_.Nu();
     1074  BDat sigma = normal_.Sigma();
     1075  BDat lower = (A_-nu)/sigma;
     1076  BDat upper = (B_-nu)/sigma;
     1077  BDat borderDistance = DEpsilon();
     1078  BDat tn01 = Random01(lower,upper,borderDistance);
     1079  BDat tn = sigma*tn01+nu;
     1080  return(tn);
     1081}
     1082
     1083//--------------------------------------------------------------------
    8951084  BClosestInsideDist::BClosestInsideDist(BDat A, BDat B, BDat nu)
    8961085//--------------------------------------------------------------------
  • tol/bmath/bprdist/tol_bprdist.h

    r7427dd4c rc8a5446  
    315315  BProbDist& NonTruncated() { return(normal_); }
    316316
     317  static BDat Random01(BDat lower, BDat upper, BDat borderDistance);
     318  BDat Random();
     319
    317320  BDat Average   ();
    318321  BDat Varianze  ();
  • tol/btol/vmatrix_type/vmat_stats.cpp

    r7427dd4c rc8a5446  
    681681    }
    682682# ifdef USE_BTruncatedNormalDist
    683     else if((lower>negInf)||(upper<posInf))
    684     {
    685       //Checking different cases
    686       //lower = 66.8902; upper = posInf;
    687       //upper = -66.8902; lower = negInf;
    688 
    689       //A continuación se generará una N(0,1) truncada estrictamente en el
    690       //interior del intevalo de dominio abierto (lower, upper) para evitar
    691       //problemas numéricos de frontera.
    692 
    693       //Máximo valor absoluto para el que se usará la distribución N(0,1)
    694       double K = 6;
    695       //La probabilidad de estar una N(0,1) fuera del intervalo [-K,K] es
    696       //con K=6 es p=1.973175400848959e-009 ~ 1 entre 500 mil millones, por lo que
    697       //es despreciable cualquier valor fuera del mismo y nos evitamos problemas
    698       //numéricos, pues tanto la cumulativa como su inversa funcionan bien
    699       //en este intervalo pero empiezan a fallar a partir de K=8.
    700       //Quizás podría ponerse K=7 pero se toma K=6 por dar un margen y porque
    701       //la pérdida de precisión es irrelevante tan lejos de la moda.
    702       //Fuera de ahí la distribución se aproximará por la raíz cuadrada de una
    703       //uniforme en el dominio reducido interior al original de tal forma que
    704       //el que el cociente de verosimilitudes entre los extremos sea menor o
    705       //igual a 100, pues en ese rango resulta una buena aproximación de la
    706       //normal truncada y es al mismo tiempo tratable numéricamente.
    707       //
    708       //El cociente de densidades entre dos puntos x,y ~ N(0,1) es
    709       //
    710       //  f(x)/f(y) = Exp(-0.5*(x^2-y^2))
    711       //
    712       //Si queremos un punto y que sea h veces menos probable que x entonces
    713       //
    714       //  Log(h) = -0.5*(x^2-y^2)
    715       //  y^2 = x^2 + 2*Log(h)
    716       //
    717       //Estas operaciones no comportan riesgos numéricos incluso para valores
    718       //muy grandes.
    719       double lowMrg = lower;
    720       double uppMrg = upper;
    721       if(lowMrg>K)
    722       {
    723         if(uppMrg>=2*lowMrg) { uppMrg=2*lowMrg; }
    724         //Si todo el dominio está por encima de K tomamos como nuevos límites
    725         //tentativos los puntos con densidad 1.1 y 110 veces las del límite inferior.
    726         //De esta forma el nuevo límite inferior es por construcción 100 veces más
    727         //probable que el superior
    728         double lowMrg_ = sqrt(pow(lowMrg,2) + 2.0 * log(  1.1) );
    729         double uppMrg_ = sqrt(pow(lowMrg,2) + 2.0 * log(110.0) );
    730         //Si los límites tentativos se salen del dominio hay que meterlos dentro
    731         if(lowMrg_>=upper)
    732         {
    733           lowMrg = 0.99*lowMrg+0.01*uppMrg;
    734           uppMrg = 0.99*uppMrg+0.01*lowMrg;
    735         }
    736         else
    737         {
    738           lowMrg = lowMrg_;
    739           if(uppMrg_>=upper)
    740           {
    741             uppMrg = 0.99*uppMrg+0.01*lowMrg;
    742           }
    743           else
    744           {
    745             uppMrg = uppMrg_;
    746           }
    747         }
    748         //Tomamos un r uniforme
    749         double r = BUniformDist::Random01().Value();
    750         //y calculamos su raíz cuadrada como valor de ponderación de los límites
    751         //para forzar puntos más cercanos al nuevo límite inferior.
    752         double u = sqrt(r);
    753         zj = u*lowMrg + (1.0-u)*uppMrg;
    754         if((zj<=lower) ||(zj>=upper))
    755         {
    756           warn_cannot_apply("(I) TruncStdGaussian [Case C.1] ",
    757             BText(" j=")<<j<<
    758             " domain:["<<lower<<","<<upper<<"] -> "<<
    759             " interior domain: ["<<lowMrg<<","<<uppMrg<<"] -> "<<zj,z);
    760           return(-1);
    761         }
    762       }
    763       else if(upper<-K)
    764       {
    765         if(lowMrg<=2*uppMrg) { lowMrg=2*uppMrg; }
    766         //Si todo el dominio está por debajo de -K tomamos como nuevos límites
    767         //tentativos los puntos con densidad 1.1 y 110 veces las del límite superior.
    768         //De esta forma el nuevo límite superior es por construcción 100 veces más
    769         //probable que el inferior
    770         double uppMrg_ = -sqrt(pow(uppMrg,2) + 2.0 * log(  1.1) );
    771         double lowMrg_ = -sqrt(pow(uppMrg,2) + 2.0 * log(110.0) );
    772         //Si los límites tentativos se salen del dominio hay que meterlos dentro
    773         if(uppMrg_<=lower)
    774         {
    775           lowMrg = 0.99*lowMrg+0.01*uppMrg;
    776           uppMrg = 0.99*uppMrg+0.01*lowMrg;
    777         }
    778         else
    779         {
    780           uppMrg = uppMrg_;
    781           if(lowMrg_<=lower)
    782           {
    783             lowMrg = 0.99*lowMrg+0.01*uppMrg;
    784           }
    785           else
    786           {
    787             lowMrg = lowMrg_;
    788           }
    789         }
    790         //Tomamos un r uniforme
    791         double r = BUniformDist::Random01().Value();
    792         //y calculamos su raíz cuadrada como valor de ponderación de los límites
    793         //para forzar puntos más cercanos al nuevo límite inferior.
    794         double u = sqrt(r);
    795         zj = u*uppMrg + (1.0-u)*lowMrg;
    796         if((zj<=lower) ||(zj>=upper))
    797         {
    798           warn_cannot_apply("(I) TruncStdGaussian [Case C.2] ",
    799             BText(" j=")<<j<<
    800             " domain:["<<lower<<","<<upper<<"] -> "<<
    801             " interior domain: ["<<lowMrg<<","<<uppMrg<<"] -> "<<zj,z);
    802           return(-1);
    803         }
    804       }
    805       else
    806       {
    807         //Si alguno de los límites cae fuera de [-K,K] hacemos la intersección
    808         if(lower<-K) { lowMrg=-K; }
    809         if(upper>+K) { uppMrg=+K; }
    810         BNormalDist u01(0,1);
    811         //Calculamos los cuantiles de la N(0,1) en los extremos
    812         double lowF01 = u01.Dist(lowMrg).Value();
    813         double uppF01 = u01.Dist(uppMrg).Value();
    814         //Calculamos el desplazamiento en la métrica probabilística
    815         double difF01 = borderDistance*(uppF01-lowF01);
    816         //Desplazamos los límites hacia el interior
    817         double lowF01_ = lowF01+difF01;
    818         double uppF01_ = uppF01-difF01;
    819         //Tomamos un r uniforme
    820         double r = BUniformDist::Random01().Value();
    821         //Tomamos un u normal truncado en el dominio interior
    822         double u = r*uppF01_ + (1.0-r)*lowF01_;
    823         //Volvemos a la métrica normalizada   
    824         zj = u01.Inverse(u).Value();
    825         if((zj<=lower) ||(zj>=upper))
    826         {
    827           warn_cannot_apply("(I) TruncStdGaussian [Case C.1] ",
    828             BText(" j=")<<j<<
    829             " domain:["<<lower<<","<<upper<<"] -> "<<
    830             " Prob domain["<<lowF01<<","<<uppF01<<"] -> "<<
    831             " Prob interior domain["<<lowF01_<<","<<uppF01_<<"] -> "<<zj,z);
    832           return(-1);
    833         }
    834       }
    835     }
    836     else
    837     {
    838       zj = BNormalDist::Random01().Value();
    839       if((zj<=lower) ||(zj>=upper))
    840       {
    841         warn_cannot_apply("(I) TruncStdGaussian [Case D] ",
    842           I2("(empty interval",
    843           "(intervalo vacio")+" ["+BDat(lower).Format("%.16lg")+","+
    844                                    BDat(upper).Format("%.16lg")+"])",z);
    845       }
    846     }
    847 
    848 
     683    zj = BTruncatedNormalDist::Random01(lower, upper, borderDistance).Value();
    849684# else
    850685    zj = gsl_rtabnorm_combo(getGslRng(), 0.0, 1.0,
Note: See TracChangeset for help on using the changeset viewer.