boost::shared_ptr<FunctionInfo> Minimizer::create_nll_function_info(const Model & model, const boost::shared_ptr<Distribution> & override_parameter_distribution, const ParValues & fixed_parameters){ const Distribution & dist = override_parameter_distribution.get()? *override_parameter_distribution: model.get_parameter_distribution(); ParValues start; dist.mode(start); Ranges ranges(dist); ParIds pids = fixed_parameters.get_parameters(); for(ParIds::const_iterator pit = pids.begin(); pit!=pids.end(); ++pit){ double val = fixed_parameters.get(*pit); start.set(*pit, val); ranges.set(*pit, make_pair(val, val)); } ParValues step = asimov_likelihood_widths(model, override_parameter_distribution); return boost::shared_ptr<FunctionInfo>(new DefFunctionInfo(start, step, ranges, fixed_parameters)); }
theta::ParValues asimov_likelihood_widths(const theta::Model & model, const boost::shared_ptr<Distribution> & override_parameter_distribution){ const Distribution & dist = override_parameter_distribution.get()? *override_parameter_distribution: model.get_parameter_distribution(); ParIds parameters = model.getParameters(); ParValues mode; dist.mode(mode); Data asimov_data; model.get_prediction(asimov_data, mode); std::auto_ptr<NLLikelihood> nll = model.getNLLikelihood(asimov_data); //0 value has same semantics for NLLikelihood: nll->set_override_distribution(override_parameter_distribution); double nll_at_min = (*nll)(mode); ParValues result; int k=0; for(ParIds::const_iterator it=parameters.begin(); it!=parameters.end(); ++it, ++k){ ParId pid = *it; const double pid_mode = mode.get(pid); std::pair<double, double> support = dist.support(pid); assert(support.first <= pid_mode && pid_mode <= support.second); if(support.first == support.second){ result.set(pid, 0.0); continue; } nll_mode_pid f(mode, pid, *nll, nll_at_min + 0.5); //if one end is finite, try to use it. Save whether the interval end is considered // "fl0", i.e. the interval end itself is finite but the function value there is invalid (< 0). bool low_is_fl0 = false, high_is_fl0 = false; if(std::isfinite(support.second)){ double f2 = f(support.second); if(f2==0.0){ result.set(pid, fabs(pid_mode - support.second)); continue; } if(!std::isfinite(f2) || f2 < 0){ low_is_fl0 = true; } else{ result.set(pid, fabs(pid_mode - secant(pid_mode, support.second, 0.0, -0.5, f2, 0.05, f))); continue; } } if(std::isfinite(support.first)){ double f2 = f(support.first); if(f2==0.0){ result.set(pid, fabs(pid_mode - support.first)); continue; } if(!std::isfinite(f2) || f2 < 0){ high_is_fl0 = true; } else{ result.set(pid, fabs(pid_mode - secant(support.first, pid_mode, 0.0, f2, -0.5, 0.05, f))); continue; } } //the support was either infinite or the values at the borders were not sufficiently high. // Treat second case first: if(low_is_fl0 && high_is_fl0){ result.set(pid, support.second - support.first); continue; } //Now, one of the interval ends has to be infinite, otherwise we would not be here. //Scan in that direction: assert(std::isinf(support.first) || std::isinf(support.second)); bool found = false; for(double sign = -1.0; sign <= 1.001; sign+=2.0){ if(!std::isinf(support.first) && sign < 0) continue; if(!std::isinf(support.second) && sign > 0) continue; // as step size, try the parameter value, if it is not zero: double step = fabs(pid_mode); if(step==0) step = 1.0; for(int i=0; i<1000; ++i){ double fval = f(pid_mode + sign * step); if(isinf(fval)){ step /= 1.5; continue; } step *= 2.0; if(fval > 0){ double xlow, xhigh, flow, fhigh; xlow = pid_mode; flow = -0.5; xhigh = pid_mode + sign * step; fhigh = fval; if(sign < 0){ std::swap(xlow, xhigh); std::swap(flow, fhigh); } assert(xlow <= xhigh); result.set(pid, fabs(pid_mode - secant(xlow, xhigh, 0.0, flow, fhigh, 0.05, f))); found = true; break; } } if(found) break; } if(found) continue; stringstream ss; ss << "asimov_likelihood_widths: could not find width for parameter " << pid; throw Exception(ss.str()); } return result; }