mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
141 lines
11 KiB
C++
141 lines
11 KiB
C++
#include <poincare/approximation_helper.h>
|
|
#include <poincare/expression.h>
|
|
#include <poincare/evaluation.h>
|
|
#include <poincare/matrix_complex.h>
|
|
#include <cmath>
|
|
#include <utility>
|
|
extern "C" {
|
|
#include <assert.h>
|
|
}
|
|
|
|
namespace Poincare {
|
|
|
|
template <typename T> bool isNegligeable(T x, T precision, T norm1, T norm2) {
|
|
T absX = std::fabs(x);
|
|
return absX <= 10.0*precision && absX/norm1 <= precision && absX/norm2 <= precision;
|
|
}
|
|
|
|
template < typename T> T minimalNonNullMagnitudeOfParts(std::complex<T> c) {
|
|
T absRealInput = std::fabs(c.real());
|
|
T absImagInput = std::fabs(c.imag());
|
|
// If the magnitude of one part is null, ignore it
|
|
absRealInput = absRealInput > 0.0 ? absRealInput : absImagInput;
|
|
absImagInput = absImagInput > 0.0 ? absImagInput : absRealInput;
|
|
return absRealInput > absImagInput ? absImagInput : absRealInput;
|
|
}
|
|
|
|
static inline int absInt(int x) { return x < 0 ? -x : x; }
|
|
|
|
template <typename T> int ApproximationHelper::PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
|
|
Evaluation<T> evaluation = expression->approximate(T(), context, complexFormat, angleUnit);
|
|
T scalar = evaluation.toScalar();
|
|
if (std::isnan(scalar) || scalar != (int)scalar) {
|
|
*isUndefined = true;
|
|
return 0;
|
|
}
|
|
return absInt((int)scalar);
|
|
}
|
|
|
|
template <typename T> std::complex<T> ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::complex<T> result, std::complex<T> input1, std::complex<T> input2) {
|
|
/* Cheat: openbsd functions (cos, sin, tan, cosh, acos, pow...) are
|
|
* numerical implementation and thus are approximative.
|
|
* The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid
|
|
* weird results as acos(1) = 6E-17 or cos(π/2) = 4E-17, we round the result
|
|
* to its 1E-6 or 1E-14 precision when its ratio with the argument (π/2 in the
|
|
* example) is smaller than epsilon. This way, we have sin(π) ~ 0 and
|
|
* sin(1E-15)=1E-15.
|
|
* We can't do that for all evaluation as the user can operate on values as
|
|
* small as 1E-308 (in double) and most results still be correct. */
|
|
T magnitude1 = minimalNonNullMagnitudeOfParts(input1);
|
|
T magnitude2 = minimalNonNullMagnitudeOfParts(input2);
|
|
T precision = 10.0*Expression::Epsilon<T>();
|
|
if (isNegligeable(result.imag(), precision, magnitude1, magnitude2)) {
|
|
result.imag(0);
|
|
}
|
|
if (isNegligeable(result.real(), precision, magnitude1, magnitude2)) {
|
|
result.real(0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T> Evaluation<T> ApproximationHelper::Map(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute<T> compute) {
|
|
assert(expression->numberOfChildren() == 1);
|
|
Evaluation<T> input = expression->childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit);
|
|
if (input.type() == EvaluationNode<T>::Type::Complex) {
|
|
return compute(static_cast<Complex<T> &>(input).stdComplex(), complexFormat, angleUnit);
|
|
} else {
|
|
assert(input.type() == EvaluationNode<T>::Type::MatrixComplex);
|
|
MatrixComplex<T> m = static_cast<MatrixComplex<T> &>(input);
|
|
MatrixComplex<T> result = MatrixComplex<T>::Builder();
|
|
for (int i = 0; i < m.numberOfChildren(); i++) {
|
|
result.addChildAtIndexInPlace(compute(m.complexAtIndex(i), complexFormat, angleUnit), i, i);
|
|
}
|
|
result.setDimensions(m.numberOfRows(), m.numberOfColumns());
|
|
return std::move(result);
|
|
}
|
|
}
|
|
|
|
template<typename T> Evaluation<T> ApproximationHelper::MapReduce(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexAndComplexReduction<T> computeOnComplexes, ComplexAndMatrixReduction<T> computeOnComplexAndMatrix, MatrixAndComplexReduction<T> computeOnMatrixAndComplex, MatrixAndMatrixReduction<T> computeOnMatrices) {
|
|
assert(expression->numberOfChildren() > 0);
|
|
Evaluation<T> result = expression->childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit);
|
|
for (int i = 1; i < expression->numberOfChildren(); i++) {
|
|
Evaluation<T> intermediateResult;
|
|
Evaluation<T> nextOperandEvaluation = expression->childAtIndex(i)->approximate(T(), context, complexFormat, angleUnit);
|
|
if (result.type() == EvaluationNode<T>::Type::Complex && nextOperandEvaluation.type() == EvaluationNode<T>::Type::Complex) {
|
|
intermediateResult = computeOnComplexes(static_cast<Complex<T> &>(result).stdComplex(), static_cast<Complex<T> &>(nextOperandEvaluation).stdComplex(), complexFormat);
|
|
} else if (result.type() == EvaluationNode<T>::Type::Complex) {
|
|
assert(nextOperandEvaluation.type() == EvaluationNode<T>::Type::MatrixComplex);
|
|
intermediateResult = computeOnComplexAndMatrix(static_cast<Complex<T> &>(result).stdComplex(), static_cast<MatrixComplex<T> &>(nextOperandEvaluation), complexFormat);
|
|
} else if (nextOperandEvaluation.type() == EvaluationNode<T>::Type::Complex) {
|
|
assert(result.type() == EvaluationNode<T>::Type::MatrixComplex);
|
|
intermediateResult = computeOnMatrixAndComplex(static_cast<MatrixComplex<T> &>(result), static_cast<Complex<T> &>(nextOperandEvaluation).stdComplex(), complexFormat);
|
|
} else {
|
|
assert(result.node()->type() == EvaluationNode<T>::Type::MatrixComplex);
|
|
assert(nextOperandEvaluation.node()->type() == EvaluationNode<T>::Type::MatrixComplex);
|
|
intermediateResult = computeOnMatrices(static_cast<MatrixComplex<T> &>(result), static_cast<MatrixComplex<T> &>(nextOperandEvaluation), complexFormat);
|
|
}
|
|
result = intermediateResult;
|
|
if (result.isUndefined()) {
|
|
return Complex<T>::Undefined();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template<typename T> MatrixComplex<T> ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const MatrixComplex<T> m, const std::complex<T> c, Poincare::Preferences::ComplexFormat complexFormat, ComplexAndComplexReduction<T> computeOnComplexes) {
|
|
MatrixComplex<T> matrix = MatrixComplex<T>::Builder();
|
|
for (int i = 0; i < m.numberOfChildren(); i++) {
|
|
matrix.addChildAtIndexInPlace(computeOnComplexes(m.complexAtIndex(i), c, complexFormat), i, i);
|
|
}
|
|
matrix.setDimensions(m.numberOfRows(), m.numberOfColumns());
|
|
return matrix;
|
|
}
|
|
|
|
template<typename T> MatrixComplex<T> ApproximationHelper::ElementWiseOnComplexMatrices(const MatrixComplex<T> m, const MatrixComplex<T> n, Poincare::Preferences::ComplexFormat complexFormat, ComplexAndComplexReduction<T> computeOnComplexes) {
|
|
if (m.numberOfRows() != n.numberOfRows() || m.numberOfColumns() != n.numberOfColumns()) {
|
|
return MatrixComplex<T>::Undefined();
|
|
}
|
|
MatrixComplex<T> matrix = MatrixComplex<T>::Builder();
|
|
for (int i = 0; i < m.numberOfChildren(); i++) {
|
|
matrix.addChildAtIndexInPlace(computeOnComplexes(m.complexAtIndex(i), n.complexAtIndex(i), complexFormat), i, i);
|
|
}
|
|
matrix.setDimensions(m.numberOfRows(), m.numberOfColumns());
|
|
return matrix;
|
|
}
|
|
|
|
template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible<float>(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit);
|
|
template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible<double>(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit);
|
|
template std::complex<float> Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable<float>(std::complex<float>,std::complex<float>,std::complex<float>);
|
|
template std::complex<double> Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable<double>(std::complex<double>,std::complex<double>,std::complex<double>);
|
|
template Poincare::Evaluation<float> Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute<float> compute);
|
|
template Poincare::Evaluation<double> Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute<double> compute);
|
|
template Poincare::Evaluation<float> Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction<float> computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction<float> computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction<float> computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction<float> computeOnMatrices);
|
|
template Poincare::Evaluation<double> Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction<double> computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction<double> computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction<double> computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction<double> computeOnMatrices);
|
|
template Poincare::MatrixComplex<float> Poincare::ApproximationHelper::ElementWiseOnMatrixComplexAndComplex<float>(const Poincare::MatrixComplex<float>, const std::complex<float>, Poincare::Preferences::ComplexFormat, Poincare::Complex<float> (*)(std::complex<float>, std::complex<float>, Poincare::Preferences::ComplexFormat));
|
|
template Poincare::MatrixComplex<double> Poincare::ApproximationHelper::ElementWiseOnMatrixComplexAndComplex<double>(const Poincare::MatrixComplex<double>, std::complex<double> const, Poincare::Preferences::ComplexFormat, Poincare::Complex<double> (*)(std::complex<double>, std::complex<double>, Poincare::Preferences::ComplexFormat));
|
|
template Poincare::MatrixComplex<float> Poincare::ApproximationHelper::ElementWiseOnComplexMatrices<float>(const Poincare::MatrixComplex<float>, const Poincare::MatrixComplex<float>, Poincare::Preferences::ComplexFormat, Poincare::Complex<float> (*)(std::complex<float>, std::complex<float>, Poincare::Preferences::ComplexFormat));
|
|
template Poincare::MatrixComplex<double> Poincare::ApproximationHelper::ElementWiseOnComplexMatrices<double>(const Poincare::MatrixComplex<double>, const Poincare::MatrixComplex<double>, Poincare::Preferences::ComplexFormat, Poincare::Complex<double> (*)(std::complex<double>, std::complex<double>, Poincare::Preferences::ComplexFormat));
|
|
|
|
|
|
}
|