BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
RangedDistributions.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Param/Distrib/RangedDistributions.cpp
6 //! @brief Implements classes representing ranged one-dimensional distributions.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************** //
14 
16 #include "Base/Utils/PyFmt.h"
20 #include <limits>
21 
22 namespace
23 {
24 template <class T> std::unique_ptr<T> makeCopy(const T& item);
25 
26 const double gate_stddev_factor = 2.0 * std::sqrt(3.0);
27 } // namespace
28 
30  : m_n_samples(5), m_sigma_factor(2.0), m_limits(RealLimits::limitless())
31 {
33 }
34 
35 RangedDistribution::RangedDistribution(size_t n_samples, double sigma_factor,
36  const RealLimits& limits)
37  : m_n_samples(n_samples), m_sigma_factor(sigma_factor), m_limits(limits)
38 {
40 }
41 
42 RangedDistribution::RangedDistribution(size_t n_samples, double sigma_factor, double min,
43  double max)
44  : m_n_samples(n_samples), m_sigma_factor(sigma_factor), m_limits(RealLimits::limited(min, max))
45 {
47 }
48 
50 
51 std::vector<ParameterSample> RangedDistribution::generateSamples(double mean, double stddev) const
52 {
53  auto generator = distribution(mean, stddev);
54  if (!generator->isDelta())
55  return generator->equidistantSamples(m_n_samples, m_sigma_factor, m_limits);
56 
57  // handling the case of delta distributions
58  auto samples = generator->equidistantSamples(m_n_samples, m_sigma_factor, m_limits);
59  ParameterSample& sample = samples[0]; // there is only one element in the vector
60  sample.weight = 1.0 / m_n_samples;
61  return std::vector<ParameterSample>(m_n_samples, sample);
62 }
63 
64 std::vector<std::vector<ParameterSample>>
65 RangedDistribution::generateSamples(const std::vector<double>& mean,
66  const std::vector<double>& stddev) const
67 {
68  if (mean.size() != stddev.size())
69  throw std::runtime_error("Error in RangedDistribution::generateSamples: mean and variance "
70  "vectors shall be of the same size");
71 
72  const size_t size = mean.size();
73 
74  std::vector<std::vector<ParameterSample>> result;
75  result.resize(size);
76  for (size_t i = 0; i < size; ++i)
77  result[i] = generateSamples(mean[i], stddev[i]);
78  return result;
79 }
80 
81 std::unique_ptr<IDistribution1D> RangedDistribution::distribution(double mean, double stddev) const
82 {
83  if (stddev < 0.0)
84  throw std::runtime_error(
85  "Error in RangedDistribution::distribution: standard deviation is less than zero");
86  return distribution_impl(mean, stddev);
87 }
88 
89 std::string RangedDistribution::pyString() const
90 {
91  std::stringstream result;
92  result << pyfmt::indent() << "distribution = " << name();
93  result << "(" << m_n_samples << ", " << pyfmt::printDouble(m_sigma_factor);
94  if (!m_limits.isLimitless())
96  result << ")";
97  return result.str();
98 }
99 
101 {
102  if (m_n_samples < 1u)
103  throw std::runtime_error("Error in RangedDistribution::checkInitialization: number of "
104  "samples shall be positive");
105 
106  if (m_sigma_factor < 0.0)
107  throw std::runtime_error("Error in RangedDistribution::checkInitialization: sigma factor "
108  "shall be non-negative.");
109 
111  return;
112 
114  throw std::runtime_error("Error in RangedDistribution::checkInitialization: lower limit "
115  "shall not exceed the upper one.");
116 }
117 
119 
120 RangedDistributionGate::RangedDistributionGate(size_t n_samples, double sigma_factor,
121  const RealLimits& limits)
122  : RangedDistribution(n_samples, sigma_factor, limits)
123 {
124 }
125 
126 RangedDistributionGate::RangedDistributionGate(size_t n_samples, double sigma_factor, double min,
127  double max)
128  : RangedDistribution(n_samples, sigma_factor, min, max)
129 {
130 }
131 
133 {
134  return makeCopy(*this).release();
135 }
136 
137 std::string RangedDistributionGate::name() const
138 {
139  return "ba.RangedDistributionGate";
140 }
141 
142 std::unique_ptr<IDistribution1D> RangedDistributionGate::distribution_impl(double mean,
143  double stddev) const
144 {
145  const double x_min = mean - gate_stddev_factor * stddev;
146  const double x_max = mean + gate_stddev_factor * stddev;
147  return std::make_unique<DistributionGate>(x_min, x_max);
148 }
149 
151 
152 RangedDistributionLorentz::RangedDistributionLorentz(size_t n_samples, double hwhm_factor,
153  const RealLimits& limits)
154  : RangedDistribution(n_samples, hwhm_factor, limits)
155 {
156 }
157 
158 RangedDistributionLorentz::RangedDistributionLorentz(size_t n_samples, double hwhm_factor,
159  double min, double max)
160  : RangedDistribution(n_samples, hwhm_factor, min, max)
161 {
162 }
163 
165 {
166  return makeCopy(*this).release();
167 }
168 
170 {
171  return "ba.RangedDistributionLorentz";
172 }
173 
174 std::unique_ptr<IDistribution1D> RangedDistributionLorentz::distribution_impl(double median,
175  double hwhm) const
176 {
177  return std::make_unique<DistributionLorentz>(median, hwhm);
178 }
179 
181 
182 RangedDistributionGaussian::RangedDistributionGaussian(size_t n_samples, double sigma_factor,
183  const RealLimits& limits)
184  : RangedDistribution(n_samples, sigma_factor, limits)
185 {
186 }
187 
188 RangedDistributionGaussian::RangedDistributionGaussian(size_t n_samples, double sigma_factor,
189  double min, double max)
190  : RangedDistribution(n_samples, sigma_factor, min, max)
191 {
192 }
193 
195 {
196  return makeCopy(*this).release();
197 }
198 
200 {
201  return "ba.RangedDistributionGaussian";
202 }
203 
204 std::unique_ptr<IDistribution1D> RangedDistributionGaussian::distribution_impl(double mean,
205  double stddev) const
206 {
207  return std::make_unique<DistributionGaussian>(mean, stddev);
208 }
209 
211 
212 RangedDistributionLogNormal::RangedDistributionLogNormal(size_t n_samples, double sigma_factor,
213  const RealLimits& limits)
214  : RangedDistribution(n_samples, sigma_factor, limits)
215 {
216 }
217 
218 RangedDistributionLogNormal::RangedDistributionLogNormal(size_t n_samples, double sigma_factor,
219  double min, double max)
220  : RangedDistribution(n_samples, sigma_factor, min, max)
221 {
222 }
223 
225 {
226  return makeCopy(*this).release();
227 }
228 
230 {
231  return "ba.RangedDistributionLogNormal";
232 }
233 
234 std::unique_ptr<IDistribution1D> RangedDistributionLogNormal::distribution_impl(double mean,
235  double stddev) const
236 {
237  const double mean_2 = mean * mean;
238  if (mean_2 <= std::numeric_limits<double>::min())
239  throw std::runtime_error("Error in DistributionLogNormal::distribution: mean square value "
240  "is less or indistinguishable from zero.");
241 
242  const double scale = std::sqrt(std::log(stddev * stddev / mean_2 + 1.0));
243  const double median = mean * std::exp(-scale * scale / 2.0);
244  return std::make_unique<DistributionLogNormal>(median, scale);
245 }
246 
248 
249 RangedDistributionCosine::RangedDistributionCosine(size_t n_samples, double sigma_factor,
250  const RealLimits& limits)
251  : RangedDistribution(n_samples, sigma_factor, limits)
252 {
253 }
254 
255 RangedDistributionCosine::RangedDistributionCosine(size_t n_samples, double sigma_factor,
256  double min, double max)
257  : RangedDistribution(n_samples, sigma_factor, min, max)
258 {
259 }
260 
262 {
263  return makeCopy(*this).release();
264 }
265 
267 {
268  return "ba.RangedDistributionCosine";
269 }
270 
271 std::unique_ptr<IDistribution1D> RangedDistributionCosine::distribution_impl(double mean,
272  double stddev) const
273 {
274  return std::make_unique<DistributionCosine>(mean, stddev);
275 }
276 
277 namespace
278 {
279 template <class T> std::unique_ptr<T> makeCopy(const T& item)
280 {
281  return std::make_unique<T>(item.nSamples(), item.sigmaFactor(), item.limits());
282 }
283 } // namespace
Defines classes representing one-dimensional distributions.
Defines class ParameterSample.
Defines functions in namespace pyfmt.
Defines functions in namespace pyfmt.
Defines classes representing ranged one-dimensional distributions.
A parameter value with a weight, as obtained when sampling from a distribution.
RangedDistributionCosine * clone() const override
std::unique_ptr< IDistribution1D > distribution_impl(double mean, double stddev) const override
Returns underlying IDistribution1D object.
std::string name() const override
Returns distribution name for python-formatted text.
Uniform distribution function.
RangedDistributionGate * clone() const override
std::string name() const override
Returns distribution name for python-formatted text.
std::unique_ptr< IDistribution1D > distribution_impl(double mean, double stddev) const override
Returns underlying IDistribution1D object.
Gaussian distribution with standard deviation std_dev.
std::string name() const override
Returns distribution name for python-formatted text.
std::unique_ptr< IDistribution1D > distribution_impl(double mean, double stddev) const override
Returns underlying IDistribution1D object.
RangedDistributionGaussian * clone() const override
Log-normal distribution.
RangedDistributionLogNormal * clone() const override
std::string name() const override
Returns distribution name for python-formatted text.
std::unique_ptr< IDistribution1D > distribution_impl(double mean, double stddev) const override
Returns underlying IDistribution1D object.
Lorentz distribution with median and hwhm.
std::string name() const override
Returns distribution name for python-formatted text.
std::unique_ptr< IDistribution1D > distribution_impl(double median, double hwhm) const override
Returns underlying IDistribution1D object.
RangedDistributionLorentz * clone() const override
Interface for one-dimensional ranged distributions.
std::string pyString() const
Prints python-formatted definition of the distribution.
~RangedDistribution() override
virtual std::unique_ptr< IDistribution1D > distribution_impl(double mean, double stddev) const =0
Returns underlying IDistribution1D object.
std::vector< ParameterSample > generateSamples(double mean, double stddev) const
std::unique_ptr< IDistribution1D > distribution(double mean, double stddev) const
Public interface function to underlying IDistribution1D object.
virtual std::string name() const =0
Returns distribution name for python-formatted text.
Limits for a real fit parameter.
Definition: RealLimits.h:25
bool hasLowerAndUpperLimits() const
if has lower and upper limit
Definition: RealLimits.cpp:77
double upperLimit() const
Returns upper limit.
Definition: RealLimits.cpp:60
double lowerLimit() const
Returns lower limit.
Definition: RealLimits.cpp:38
bool isLimitless() const
Definition: RealLimits.cpp:171
std::unique_ptr< T > makeCopy(const T &item)
std::string printDouble(double input)
Definition: PyFmt.cpp:43
std::string indent(size_t width)
Returns a string of blanks with given width.
Definition: PyFmt.cpp:141
std::string printRealLimitsArg(const RealLimits &limits, const std::string &units)
Prints RealLimits in the form of argument (in the context of ParameterDistribution and similar).
Definition: PyFmtLimits.cpp:61