Newer
Older
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source
// & Institut Laue - Langevin
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidAPI/IFunction.h"
#include "MantidAPI/ConstraintFactory.h"
#include "MantidAPI/IConstraint.h"
#include "MantidAPI/IFunctionWithLocation.h"
#include "MantidAPI/Jacobian.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/MultiDomainFunction.h"
#include "MantidAPI/SpectrumInfo.h"
Federico Montesino Pouzols
committed
#include "MantidGeometry/Instrument.h"
#include "MantidGeometry/Instrument/Component.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
#include "MantidGeometry/Instrument/DetectorInfo.h"
#include "MantidGeometry/Instrument/FitParameter.h"
#include "MantidGeometry/Instrument/ParameterMap.h"
#include "MantidGeometry/muParser_Silent.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/IPropertyManager.h"
#include "MantidKernel/Logger.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/ProgressBase.h"
#include "MantidKernel/Strings.h"
#include "MantidKernel/UnitFactory.h"
#include <boost/lexical_cast.hpp>
#include "MantidKernel/StringTokenizer.h"
#include <sstream>
namespace Mantid {
namespace API {
using namespace Geometry;
namespace {
/// static logger
Kernel::Logger g_log("IFunction");
/// Struct that helps sort ties in correct order of application.
struct TieNode {
size_t left;
// Indices of parameters on the right-hand-side of the expression
std::vector<size_t> right;
// This tie must be applied before the other if the RHS of the other
// contains this (left) parameter.
bool operator<(TieNode const &other) const {
return std::find(other.right.begin(), other.right.end(), left) !=
other.right.end();
}
};
/**
* Destructor
*/
/**
* Virtual copy constructor
*/
boost::shared_ptr<IFunction> IFunction::clone() const {
auto clonedFunction =
FunctionFactory::Instance().createInitialized(this->asString());
for (size_t i = 0; i < this->nParams(); i++) {
double error = this->getError(i);
clonedFunction->setError(i, error);
}
return clonedFunction;
/**
* Attach a progress reporter
* @param reporter :: A pointer to a progress reporter that can be called during
* function evaluation
*/
void IFunction::setProgressReporter(
boost::shared_ptr<Kernel::ProgressBase> reporter) {
m_progReporter = reporter;
m_progReporter->setNotifyStep(0.01);
}
/**
* If a reporter object is set, reports progress with an optional message
* @param msg :: A message to display (default = "")
*/
void IFunction::reportProgress(const std::string &msg) const {
if (m_progReporter) {
const_cast<Kernel::ProgressBase *>(m_progReporter.get())->report(msg);
}
}
/**
*
* @returns true if a progress reporter is set & evalaution has been requested
*to stop
*/
bool IFunction::cancellationRequestReceived() const {
if (m_progReporter)
return m_progReporter->hasCancellationBeenRequested();
else
return false;
}
/** Base class implementation calculates the derivatives numerically.
* @param domain :: The domain of the function
* @param jacobian :: A Jacobian matrix. It is expected to have dimensions of
* domain.size() by nParams().
void IFunction::functionDeriv(const FunctionDomain &domain,
Jacobian &jacobian) {
calNumericalDeriv(domain, jacobian);
/** Check if an active parameter i is actually active
* @param i :: Index of a parameter.
*/
bool IFunction::isActive(size_t i) const {
return getParameterStatus(i) == Active;
}
/**
* Query if the parameter is fixed
* @param i :: The index of a declared parameter
* @return true if parameter i is fixed
*/
bool IFunction::isFixed(size_t i) const {
auto status = getParameterStatus(i);
return status == Fixed || status == FixedByDefault;
/// Check if a parameter i is fixed by default (not by user).
/// @param i :: The index of a parameter
/// @return true if parameter i is fixed by default
bool IFunction::isFixedByDefault(size_t i) const {
return getParameterStatus(i) == FixedByDefault;
}
/// This method doesn't create a tie
/// @param i :: A declared parameter index to be fixed
/// @param isDefault :: If true fix it by default
///
void IFunction::fix(size_t i, bool isDefault) {
auto status = getParameterStatus(i);
if (status == Tied) {
throw std::runtime_error("Cannot fix parameter " + std::to_string(i) +
" (" + parameterName(i) + "): it has a tie.");
}
if (isDefault) {
setParameterStatus(i, FixedByDefault);
} else {
setParameterStatus(i, Fixed);
}
}
/** Makes a parameter active again. It doesn't change the parameter's tie.
* @param i :: A declared parameter index to be restored to active
*/
void IFunction::unfix(size_t i) {
auto status = getParameterStatus(i);
if (status == Tied) {
throw std::runtime_error("Cannot unfix parameter " + std::to_string(i) +
" (" + parameterName(i) + "): it has a tie.");
}
setParameterStatus(i, Active);
/**
* Ties a parameter to other parameters
* @param parName :: The name of the parameter to tie.
* @param isDefault :: Flag to mark as default the value of an object associated
* with this reference: a tie or a constraint.
* @return newly ties parameters
*/
void IFunction::tie(const std::string &parName, const std::string &expr,
bool isDefault) {
auto ti = std::make_unique<ParameterTie>(this, parName, expr, isDefault);
fix(getParameterIndex(*ti));
addTie(std::move(ti));
/**
* Add ties to the function.
* @param ties :: Comma-separated list of name=value pairs where name is a
*parameter name and value
* is a math expression tying the parameter to other parameters or a constant.
* @param isDefault :: Flag to mark as default the value of an object associated
*with this reference: a tie or a constraint.
void IFunction::addTies(const std::string &ties, bool isDefault) {
Expression list;
list.parse(ties);
list.toList();
for (const auto &t : list) {
if (t.name() == "=" && t.size() >= 2) {
size_t n = t.size() - 1;
const std::string value = t[n].str();
this->tie(t[i].name(), value, isDefault);
/** Removes the tie off a parameter. The parameter becomes active
* This method can be used when constructing and editing the IFunction in a GUI
* @param parName :: The name of the parameter which ties will be removed.
*/
void IFunction::removeTie(const std::string &parName) {
size_t i = parameterIndex(parName);
this->removeTie(i);
}
/// Write all parameter ties owned by this function to a string
/// @return A tie string for the parameter.
std::string IFunction::writeTies() const {
for (auto &tie : m_ties) {
if (tie->isDefault())
continue;
if (!first) {
tieStream << ',';
} else {
first = false;
tieStream << tie->asString(this);
}
/**
* Attaches a tie to this ParamFunction. The attached tie is owned by the
* ParamFunction.
* @param tie :: A pointer to a new tie
*/
void IFunction::addTie(std::unique_ptr<ParameterTie> tie) {
auto iPar = getParameterIndex(*tie);
bool found = false;
for (auto &m_tie : m_ties) {
auto mPar = getParameterIndex(*m_tie);
if (mPar == iPar) {
found = true;
m_tie = std::move(tie);
break;
}
}
if (!found) {
m_ties.emplace_back(std::move(tie));
setParameterStatus(iPar, Tied);
bool IFunction::hasOrderedTies() const { return !m_orderedTies.empty(); }
for (auto &&tie : m_orderedTies) {
tie->eval();
}
}
/**
* Apply the ties.
*/
void IFunction::applyTies() {
if (hasOrderedTies()) {
applyOrderedTies();
} else {
for (auto &tie : m_ties) {
tie->eval();
}
}
}
/**
* Used to find ParameterTie for a parameter i
*/
class ReferenceEqual {
/// The function that has the tie
const IFunction &m_fun;
/// index to find
const size_t m_i;
public:
/// Constructor
explicit ReferenceEqual(const IFunction &fun, size_t i)
: m_fun(fun), m_i(i) {}
/// Bracket operator
/// @param p :: the element you are looking for
/// @return True if found
template <class T> bool operator()(const std::unique_ptr<T> &p) {
return m_fun.getParameterIndex(*p) == m_i;
}
};
/** Removes i-th parameter's tie if it is tied or does nothing.
* @param i :: The index of the tied parameter.
* @return True if successfull
*/
bool IFunction::removeTie(size_t i) {
if (i >= nParams()) {
throw std::out_of_range("Function parameter index out of range.");
}
auto it =
std::find_if(m_ties.begin(), m_ties.end(), ReferenceEqual(*this, i));
if (it != m_ties.end()) {
m_ties.erase(it);
setParameterStatus(i, Active);
return true;
}
unfix(i);
return false;
}
/** Get tie of parameter number i
* @param i :: The index of a declared parameter.
* @return A pointer to the tie
*/
ParameterTie *IFunction::getTie(size_t i) const {
auto it =
std::find_if(m_ties.cbegin(), m_ties.cend(), ReferenceEqual(*this, i));
if (it != m_ties.cend()) {
return it->get();
}
return nullptr;
}
/** Remove all ties
*/
void IFunction::clearTies() {
for (size_t i = 0; i < nParams(); ++i) {
setParameterStatus(i, Active);
}
m_ties.clear();
}
/** Add a constraint
* @param ic :: Pointer to a constraint.
*/
void IFunction::addConstraint(std::unique_ptr<IConstraint> ic) {
size_t iPar = ic->parameterIndex();
bool found = false;
for (auto &constraint : m_constraints) {
if (constraint->parameterIndex() == iPar) {
found = true;
constraint = std::move(ic);
break;
}
}
if (!found) {
m_constraints.emplace_back(std::move(ic));
}
}
/** Get constraint of parameter number i
* @param i :: The index of a declared parameter.
* @return A pointer to the constraint or NULL
*/
IConstraint *IFunction::getConstraint(size_t i) const {
auto it = std::find_if(m_constraints.cbegin(), m_constraints.cend(),
ReferenceEqual(*this, i));
if (it != m_constraints.cend()) {
return it->get();
}
return nullptr;
}
/** Remove a constraint
* @param parName :: The name of a parameter which constarint to remove.
*/
void IFunction::removeConstraint(const std::string &parName) {
size_t iPar = parameterIndex(parName);
for (auto it = m_constraints.begin(); it != m_constraints.end(); ++it) {
if (iPar == (**it).getLocalIndex()) {
m_constraints.erase(it);
break;
}
}
}
/** Set a constraint penalty
* @param parName :: The name of a constraint
* @param c :: The penalty
*/
void IFunction::setConstraintPenaltyFactor(const std::string &parName,
const double &c) {
size_t iPar = parameterIndex(parName);
for (auto &constraint : m_constraints) {
if (iPar == constraint->getLocalIndex()) {
constraint->setPenaltyFactor(c);
g_log.warning()
<< parName
<< " does not have constraint so setConstraintPenaltyFactor failed"
<< "\n";
/// Remove all constraints.
void IFunction::clearConstraints() { m_constraints.clear(); }
void IFunction::setUpForFit() {
for (auto &constraint : m_constraints) {
constraint->setParamToSatisfyConstraint();
}
}
/// Write all parameter constraints owned by this function to a string
/// @return A constraint string for the parameter.
std::string IFunction::writeConstraints() const {
std::ostringstream stream;
bool first = true;
for (auto &constrint : m_constraints) {
if (constrint->isDefault())
continue;
if (!first) {
stream << ',';
} else {
first = false;
stream << constrint->asString();
return stream.str();
}
/**
* Writes a string that can be used in FunctionFunctory to create a copy of this
* IFunction
* @return string representation of the function
*/
std::string IFunction::asString() const { return writeToString(); }
/**
* Writes this function into a string.
* @param parentLocalAttributesStr :: A preformatted string with local
* attributes of a parent composite function. Can be passed in by a
* CompositeFunction (eg MultiDomainFunction).
* @return string representation of the function
*/
std::string
IFunction::writeToString(const std::string &parentLocalAttributesStr) const {
std::ostringstream ostr;
ostr << "name=" << this->name();
// print the attributes
std::vector<std::string> attr = this->getAttributeNames();
for (const auto &attName : attr) {
std::string attValue = this->getAttribute(attName).value();
if (!attValue.empty() && attValue != "\"\"") {
ostr << ',' << attName << '=' << attValue;
}
}
std::vector<std::string> ties;
// print the parameters
for (size_t i = 0; i < nParams(); i++) {
std::ostringstream paramOut;
paramOut << parameterName(i) << '=' << getParameter(i);
ostr << ',' << paramOut.str();
// Output non-default ties only.
if (getParameterStatus(i) == Fixed) {
ties.emplace_back(paramOut.str());
std::string constraints = writeConstraints();
// print constraints
if (!constraints.empty()) {
ostr << ",constraints=(" << constraints << ")";
}
// collect the non-default ties
auto tiesString = writeTies();
if (!tiesString.empty()) {
ties.emplace_back(tiesString);
}
// print the ties
if (!ties.empty()) {
ostr << ",ties=(" << Kernel::Strings::join(ties.begin(), ties.end(), ",")
<< ")";
}
// "local" attributes of a parent composite function
ostr << parentLocalAttributesStr;
return ostr.str();
}
/** Add a list of constraints from a string
* @param str :: A comma-separated list of constraint expressions.
* @param isDefault :: Flag to mark as default the value of an object associated
*with this reference.
void IFunction::addConstraints(const std::string &str, bool isDefault) {
Expression list;
list.parse(str);
list.toList();
for (auto it = list.begin(); it != list.end(); ++it) {
auto expr = (*it);
if (expr.terms()[0].str().compare("penalty") == 0) {
continue;
}
if ((it + 1) != list.end()) {
auto next_expr = *(it + 1);
if (next_expr.terms()[0].str().compare("penalty") == 0) {
auto c = std::unique_ptr<IConstraint>(
ConstraintFactory::Instance().createInitialized(this, expr,
isDefault));
double penalty_factor = std::stof(next_expr.terms()[1].str(), NULL);
c->setPenaltyFactor(penalty_factor);
this->addConstraint(std::move(c));
} else {
auto c = std::unique_ptr<IConstraint>(
ConstraintFactory::Instance().createInitialized(this, expr,
isDefault));
this->addConstraint(std::move(c));
}
} else {
auto c = std::unique_ptr<IConstraint>(
ConstraintFactory::Instance().createInitialized(this, expr,
isDefault));
this->addConstraint(std::move(c));
}
}
/**
* Return a vector with all parameter names.
*/
std::vector<std::string> IFunction::getParameterNames() const {
for (size_t i = 0; i < nParams(); ++i) {
out.emplace_back(parameterName(i));
/** Set a function handler
* @param handler :: A new handler
*/
void IFunction::setHandler(std::unique_ptr<FunctionHandler> handler) {
m_handler = std::move(handler);
if (handler && handler->function().get() != this) {
throw std::runtime_error("Function handler points to a different function");
}
m_handler->init();
}
/// Function to return all of the categories that contain this function
const std::vector<std::string> IFunction::categories() const {
Mantid::Kernel::StringTokenizer tokenizer(
category(), categorySeparator(),
Mantid::Kernel::StringTokenizer::TOK_TRIM |
Mantid::Kernel::StringTokenizer::TOK_IGNORE_EMPTY);
return tokenizer.asVector();
}
/**
* Operator <<
* @param ostr :: The output stream
* @param f :: The IFunction
*/
std::ostream &operator<<(std::ostream &ostr, const IFunction &f) {
ostr << f.asString();
return ostr;
}
namespace {
/**
* Const attribute visitor returning the type of the attribute
*/
class AttType : public IFunction::ConstAttributeVisitor<std::string> {
protected:
/// Apply if string
std::string apply(const std::string & /*str*/) const override {
return "std::string";
}
std::string apply(const int & /*i*/) const override { return "int"; }
std::string apply(const double & /*d*/) const override { return "double"; }
std::string apply(const bool & /*i*/) const override { return "bool"; }
std::string apply(const std::vector<double> & /*unused*/) const override {
return "std::vector<double>";
}
};
std::string IFunction::Attribute::type() const {
AttType tmp;
return apply(tmp);
}
namespace {
/**
* Const attribute visitor returning the value of the attribute as a string
*/
class AttValue : public IFunction::ConstAttributeVisitor<std::string> {
public:
explicit AttValue(bool quoteString = false)
: IFunction::ConstAttributeVisitor<std::string>(),
m_quoteString(quoteString) {}
protected:
/// Apply if string
std::string apply(const std::string &str) const override {
return (m_quoteString) ? std::string("\"" + str + "\"") : str;
}
/// Apply if int
std::string apply(const int &i) const override { return std::to_string(i); }
std::string apply(const double &d) const override {
return boost::lexical_cast<std::string>(d);
}
/// Apply if bool
std::string apply(const bool &b) const override {
return b ? "true" : "false";
}
std::string apply(const std::vector<double> &v) const override {
if (!v.empty()) {
for (size_t i = 0; i < v.size() - 1; ++i) {
res += boost::lexical_cast<std::string>(v[i]) + ",";
}
res += boost::lexical_cast<std::string>(v.back());
res += ")";
return res;
}
private:
/// Flag to quote a string value returned
bool m_quoteString;
};
/// Copy assignment. Do not copy m_quoteValue flag.
/// @param attr :: The attribute to copy from.
IFunction::Attribute &IFunction::Attribute::operator=(const Attribute &attr) {
m_data = attr.m_data;
return *this;
}
std::string IFunction::Attribute::value() const {
AttValue tmp(m_quoteValue);
return apply(tmp);
}
/**
* Return the attribute as a string if it is a string.
*/
std::string IFunction::Attribute::asString() const {
if (m_quoteValue)
return asQuotedString();
try {
return boost::get<std::string>(m_data);
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as string");
}
}
/**
* Return the attribute as a quoted string if it is a string.
*/
std::string IFunction::Attribute::asQuotedString() const {
std::string attr;
attr = boost::get<std::string>(m_data);
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as string");
if (attr.empty())
return "\"\"";
std::string quoted(attr);
if (*(attr.begin()) != '\"')
quoted = "\"" + attr;
if (*(quoted.end() - 1) != '\"')
quoted += "\"";
return quoted;
}
/**
* Return the attribute as an unquoted string if it is a string.
*/
std::string IFunction::Attribute::asUnquotedString() const {
std::string attr;
attr = boost::get<std::string>(m_data);
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as string");
}
std::string unquoted(attr);
if (attr.empty())
return "";
if (*(attr.begin()) == '\"')
unquoted = std::string(attr.begin() + 1, attr.end() - 1);
if (*(unquoted.end() - 1) == '\"')
unquoted = std::string(unquoted.begin(), unquoted.end() - 1);
return unquoted;
}
/**
* Return the attribute as an int if it is a int.
*/
int IFunction::Attribute::asInt() const {
try {
return boost::get<int>(m_data);
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as int");
}
}
/**
* Return the attribute as a double if it is a double.
*/
double IFunction::Attribute::asDouble() const {
try {
return boost::get<double>(m_data);
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as double");
}
}
/**
* Return the attribute as a bool if it is a bool.
*/
bool IFunction::Attribute::asBool() const {
try {
return boost::get<bool>(m_data);
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as bool");
}
/**
* Return the attribute as a bool if it is a vector.
*/
std::vector<double> IFunction::Attribute::asVector() const {
try {
return boost::get<std::vector<double>>(m_data);
} catch (...) {
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as vector");
/** Sets new value if attribute is a string. If the type is wrong
* throws an exception
* @param str :: The new value
*/
void IFunction::Attribute::setString(const std::string &str) {
try {
boost::get<std::string>(m_data) = str;
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as string");
}
}
/** Sets new value if attribute is a double. If the type is wrong
* throws an exception
* @param d :: The new value
*/
void IFunction::Attribute::setDouble(const double &d) {
try {
boost::get<double>(m_data) = d;
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as double");
}
}
/** Sets new value if attribute is an int. If the type is wrong
* throws an exception
* @param i :: The new value
*/
void IFunction::Attribute::setInt(const int &i) {
try {
boost::get<int>(m_data) = i;
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as int");
}
}
/** Sets new value if attribute is an bool. If the type is wrong
* throws an exception
* @param b :: The new value
void IFunction::Attribute::setBool(const bool &b) {
try {
boost::get<bool>(m_data) = b;
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as bool");
}
/**
* Sets new value if attribute is a vector. If the type is wrong
* throws an exception
* @param v :: The new value
*/
void IFunction::Attribute::setVector(const std::vector<double> &v) {
try {
auto &value = boost::get<std::vector<double>>(m_data);
value.assign(v.begin(), v.end());
} catch (...) {
throw std::runtime_error("Trying to access a " + type() +
" attribute "
"as vector");
/// Check if a string attribute is empty
bool IFunction::Attribute::isEmpty() const {
try {
return boost::get<std::string>(m_data).empty();
} catch (...) {
throw std::runtime_error("Trying to access a " + type() +
" attribute as string");
}
}
namespace {
/**
* Attribute visitor setting new value to an attribute
*/
class SetValue : public IFunction::AttributeVisitor<> {
public:
* Constructor
* @param value :: The value to set
explicit SetValue(const std::string &value) : m_value(value) {}
protected:
/// Apply if string
void apply(std::string &str) const override { str = m_value; }
void apply(int &i) const override {
std::istringstream istr(m_value + " ");
istr >> i;
if (!istr.good())
throw std::invalid_argument("Failed to set int attribute "
"from string " +
m_value);
}
/// Apply if double
void apply(double &d) const override {
std::istringstream istr(m_value + " ");
istr >> d;
if (!istr.good())
throw std::invalid_argument("Failed to set double attribute "
"from string " +
m_value);
}
/// Apply if bool
void apply(bool &b) const override {
b = (m_value == "true" || m_value == "TRUE" || m_value == "1");
}
/// Apply if vector
void apply(std::vector<double> &v) const override {
if (m_value.empty() || m_value == "EMPTY") {
if (m_value.size() > 2) {
// check if the value is in barckets (...)
if (m_value.front() == '(' && m_value.back() == ')') {
m_value.erase(0, 1);
m_value.erase(m_value.size() - 1);
}
Mantid::Kernel::StringTokenizer tokenizer(
m_value, ",", Mantid::Kernel::StringTokenizer::TOK_TRIM);
v.resize(tokenizer.count());
for (size_t i = 0; i < v.size(); ++i) {
v[i] = boost::lexical_cast<double>(tokenizer[i]);
mutable std::string m_value; ///< the value as a string
/** Set value from a string. Throws exception if the string has wrong format
* @param str :: String representation of the new value
*/
void IFunction::Attribute::fromString(const std::string &str) {
SetValue tmp(str);
apply(tmp);
}
/// Value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
double IFunction::activeParameter(size_t i) const {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter " +
}
return getParameter(i);
}
/// Set new value of i-th active parameter. Override this method to make fitted
/// parameters different from the declared
void IFunction::setActiveParameter(size_t i, double value) {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter " +
parameterName(i));
/**
* Returns the name of an active parameter.
* @param i :: Index of a parameter. The parameter must be active.
*/
std::string IFunction::nameOfActive(size_t i) const {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter " +
parameterName(i));
}
return parameterName(i);
}
/**
* Returns the description of an active parameter.
* @param i :: Index of a parameter. The parameter must be active.
*/
std::string IFunction::descriptionOfActive(size_t i) const {
if (!isActive(i)) {
throw std::runtime_error("Attempt to use an inactive parameter " +
parameterName(i));
}
return parameterDescription(i);
}
/** Calculate numerical derivatives.
* @param domain :: The domain of the function
* @param jacobian :: A Jacobian matrix. It is expected to have dimensions of
* domain.size() by nParams().
void IFunction::calNumericalDeriv(const FunctionDomain &domain,
Jacobian &jacobian) {
/*
* There is a similar more specialized method for 1D functions in IFunction1D
* but the method takes different parameters and uses slightly different
* function calls in places making it difficult to share code. Please also
* consider that method when updating this.
*/
constexpr double epsilon = std::numeric_limits<double>::epsilon() * 100;