27using namespace Kernel;
35const size_t MAX_APPROX_SIZE = 129;
36const double MIN_APPROX_TOLERANCE = 1e-4;
39double getConstraints(
const std::vector<std::unique_ptr<IConstraint>> &constraints) {
40 return std::accumulate(constraints.begin(), constraints.end(), 0.0,
41 [](
double s,
const std::unique_ptr<IConstraint> &c) { return s + c->check(); });
47 double operator()(
double p) {
63void fixBadParameters(CostFunctions::CostFuncFitting &costFunction,
64 const std::vector<std::pair<double, double>> &ranges) {
65 std::vector<size_t> indicesOfFixed;
66 std::vector<double> P, A, D;
67 auto &fun = *costFunction.getFittingFunction();
68 for (
size_t i = 0, j = 0; i < fun.nParams(); ++i) {
69 if (!fun.isActive(i)) {
72 auto lBound = ranges[j].first;
73 auto rBound = ranges[j].second;
74 auto storedParam = fun.getParameter(i);
75 Slice slice{costFunction, j};
76 auto base = Functions::ChebfunBase::bestFitAnyTolerance(lBound, rBound, slice, P, A, 1.0, MIN_APPROX_TOLERANCE,
80 fun.setParameter(i, storedParam);
81 base = std::make_shared<Functions::ChebfunBase>(MAX_APPROX_SIZE, lBound, rBound, MIN_APPROX_TOLERANCE);
85 base->derivative(A, D);
86 auto roots = base->roots(D);
88 if (roots.size() * 2 >= base->size()) {
94 fun.setParameter(i, storedParam);
99 indicesOfFixed.emplace_back(i);
103 for (
auto i : indicesOfFixed) {
104 fun.tie(fun.parameterName(i),
std::to_string(fun.getParameter(i)));
109class BestParameters {
117 explicit BestParameters(
size_t size) :
m_size(size) {}
121 void insertParams(
double value,
const EigenVector ¶ms) {
129 std::vector<EigenVector> getParams()
const {
130 std::vector<EigenVector> res;
132 std::transform(
m_params.begin(),
m_params.end(), std::back_inserter(res), [](
const auto &it) { return it.second; });
146std::vector<EigenVector> runMonteCarlo(CostFunctions::CostFuncFitting &costFunction,
147 const std::vector<std::pair<double, double>> &ranges,
148 const std::vector<std::unique_ptr<IConstraint>> &constraints,
149 const size_t nSamples,
const size_t nOutput,
const unsigned int seed) {
154 double value = costFunction.val() + getConstraints(constraints);
155 auto nParams = costFunction.nParams();
157 costFunction.getParameters(params);
158 BestParameters bestParams(nOutput);
159 bestParams.insertParams(
value, params);
162 for (
size_t it = 0; it < nSamples; ++it) {
163 for (
size_t i = 0; i < nParams; ++i) {
164 const auto &range = ranges[i];
165 costFunction.setParameter(i, std::uniform_real_distribution<>(range.first, range.second)(rng));
167 costFunction.applyTies();
168 if (getConstraints(constraints) > 0.0) {
171 value = costFunction.val();
172 if (costFunction.nParams() != nParams) {
173 throw std::runtime_error(
"Cost function changed number of parameters " +
std::to_string(nParams) +
" -> " +
176 if (bestParams.isOneOfBest(
value)) {
177 costFunction.getParameters(params);
178 bestParams.insertParams(
value, params);
181 auto outputParams = bestParams.getParams();
182 costFunction.setParameters(outputParams.front());
208void runCrossEntropy(CostFunctions::CostFuncFitting &costFunction,
const std::vector<std::pair<double, double>> &ranges,
209 const std::vector<std::unique_ptr<IConstraint>> &constraints,
size_t nSamples,
size_t nSelection,
210 size_t nIterations,
unsigned int seed) {
213 std::vector<std::pair<double, double>> distributionParams;
214 for (
auto &range : ranges) {
215 auto mean = (range.first + range.second) / 2;
216 auto sigma = std::fabs(range.first - range.second) / 2;
217 distributionParams.emplace_back(mean,
sigma);
220 auto nParams = costFunction.nParams();
227 using ParameterSet = std::pair<double, EigenVector>;
228 std::vector<ParameterSet> sampleSets(nSamples);
230 auto compareSets = [](
const ParameterSet &p1,
const ParameterSet &p2) {
return p1.first < p2.first; };
233 for (
size_t it = 0; it < nIterations; ++it) {
234 for (
size_t isam = 0; isam < nSamples; ++isam) {
236 auto ¶mSet = sampleSets[isam];
237 if (paramSet.second.size() != nParams) {
238 paramSet.second.resize(nParams);
240 for (
size_t i = 0; i < nParams; ++i) {
242 Kernel::normal_distribution<>(distributionParams[i].first, distributionParams[i].second)(rng);
245 costFunction.setParameters(paramSet.second);
246 paramSet.first = costFunction.val() + getConstraints(constraints);
247 if (costFunction.nParams() != nParams) {
248 throw std::runtime_error(
"Cost function changed number of parameters " +
std::to_string(nParams) +
" -> " +
255 std::partial_sort(sampleSets.begin(), sampleSets.begin() + nSelection, sampleSets.end(), compareSets);
257 EigenVector means(nParams);
258 EigenVector variances(nParams);
259 for (
size_t isam = 0; isam < nSelection; ++isam) {
260 auto ¶mSet = sampleSets[isam];
261 for (
size_t i = 0; i < nParams; ++i) {
262 auto p = paramSet.second[i];
264 variances[i] += p * p;
267 means *= 1.0 / double(nSelection);
268 variances *= 1.0 / double(nSelection);
269 for (
size_t i = 0; i < nParams; ++i) {
270 auto mean = means[i];
271 auto sigma = sqrt(variances[i] - mean * mean);
272 distributionParams[i].first = mean;
273 distributionParams[i].second =
sigma;
277 costFunction.setParameters(sampleSets.front().second);
285const std::string EstimateFitParameters::name()
const {
return "EstimateFitParameters"; }
288int EstimateFitParameters::version()
const {
return 1; }
291const std::string EstimateFitParameters::summary()
const {
292 return "Estimate parameters of a fitting function using a Monte Carlo "
298void EstimateFitParameters::initConcrete() {
299 std::vector<std::string> types{
"Monte Carlo",
"Cross Entropy"};
304 declareProperty(
"Constraints",
"",
"Additional constraints on tied parameters.");
305 declareProperty(
"Type",
"Monte Carlo", R
"(Type of the algorithm: "Monte Carlo" or "Cross Entropy")");
307 "Number of parameter sets to output to "
308 "OutputWorkspace. Unused if OutputWorkspace "
309 "isn't set. (Monte Carlo only)");
312 "Optional: A table workspace with parameter sets producing "
313 "the smallest values of cost function. (Monte Carlo only)");
314 declareProperty(
"NIterations", 10,
"Number of iterations of the Cross Entropy algorithm.");
316 "Size of the selection in the Cross Entropy "
317 "algorithm from which to estimate new "
318 "distribution parameters for the next "
321 "If true try to estimate which "
322 "parameters may cause problems "
323 "for fitting and fix them.");
325 "A seed value for the random number generator. "
326 "The default value (0) makes the generator use a "
332void EstimateFitParameters::execConcrete() {
334 auto func = costFunction->getFittingFunction();
338 std::vector<std::unique_ptr<IConstraint>> constraints;
339 std::string constraintStr =
getProperty(
"Constraints");
340 if (!constraintStr.empty()) {
342 expr.
parse(constraintStr);
344 for (
auto &term : expr.
terms()) {
346 constraints.emplace_back(std::unique_ptr<IConstraint>(c));
351 std::vector<std::pair<double, double>> ranges;
352 ranges.reserve(costFunction->nParams());
353 for (
size_t i = 0; i < func->nParams(); ++i) {
354 if (!func->isActive(i)) {
357 auto constraint = func->getConstraint(i);
364 throw std::runtime_error(
"Parameter " + func->parameterName(i) +
" must have a boundary constraint. ");
366 if (!boundary->hasLower()) {
367 throw std::runtime_error(
"Constraint of " + func->parameterName(i) +
" must have a lower bound.");
369 if (!boundary->hasUpper()) {
370 throw std::runtime_error(
"Constraint of " + func->parameterName(i) +
" must have an upper bound.");
374 ranges.emplace_back(boundary->lower(), boundary->upper());
377 costFunction->reset();
378 if (costFunction->nParams() == 0) {
379 throw std::runtime_error(
"No parameters are given for which to estimate "
380 "initial values. Set boundary constraints to "
381 "parameters that need to be estimated.");
384 size_t nSamples =
static_cast<int>(
getProperty(
"NSamples"));
385 unsigned int seed =
static_cast<int>(
getProperty(
"Seed"));
390 if (outputWorkspaceProp->isDefault() || nOutput <= 0) {
393 auto output = runMonteCarlo(*costFunction, ranges, constraints, nSamples,
static_cast<size_t>(nOutput), seed);
395 if (!outputWorkspaceProp->isDefault()) {
397 auto column = table->addColumn(
"str",
"Name");
398 column->setPlotType(6);
399 for (
size_t i = 0; i < output.size(); ++i) {
401 column->setPlotType(2);
404 for (
size_t i = 0, ia = 0; i <
m_function->nParams(); ++i) {
408 for (
auto &j : output) {
417 size_t nSelection =
static_cast<int>(
getProperty(
"Selection"));
418 size_t nIterations =
static_cast<int>(
getProperty(
"NIterations"));
419 runCrossEntropy(*costFunction, ranges, constraints, nSamples, nSelection, nIterations, seed);
423 fixBadParameters(*costFunction, ranges);
#define DECLARE_ALGORITHM(classname)
size_t m_size
Maximum size of the store.
size_t m_paramIndex
Index of the running parameter.
std::map< double, EigenVector > m_params
Actual storage.
CostFunctions::CostFuncFitting & m_costFunction
The cost function.
double value
The value of the point.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
This class represents an expression made up of names, binary operators and brackets.
const std::vector< Expression > & terms() const
Returns the top level terms of the expression (function arguments).
void parse(const std::string &str)
Parse a string and create an expression.
void toList(const std::string &sep=",")
Make sure the expression is a list of expression separated by sep, eg "term1,term2,...
An interface to a constraint.
TableRow represents a row in a TableWorkspace.
A property class for workspaces.
A boundary constraint is designed to be used to set either upper or lower (or both) boundaries on a s...
void declareCostFunctionProperty()
Declare a "CostFunction" property.
std::shared_ptr< CostFunctions::CostFuncFitting > getCostFunctionInitialized() const
Create a cost function from the "CostFunction" property and make it ready for evaluation.
std::shared_ptr< API::IFunction > m_function
Pointer to the fitting function.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Output
An output workspace.