mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[poincare] Add comments and alternative algorithm to beautify units
Change-Id: I4b1ce9528d9d6796fe08f8566ee5d1efafa1d87d
This commit is contained in:
committed by
Émilie Feral
parent
51f1cdb076
commit
11e41bb4dc
@@ -85,6 +85,8 @@ public:
|
||||
public:
|
||||
template<typename T>
|
||||
struct Vector {
|
||||
/* SupportSize is defined as the number of distinct base units.
|
||||
* Norm is defined as the sum of all unit exponents absolute values. */
|
||||
struct Metrics {
|
||||
size_t supportSize;
|
||||
T norm;
|
||||
|
||||
@@ -331,21 +331,81 @@ static bool CanSimplifyUnitProduct(
|
||||
* 'bestRemainder' are updated accordingly. */
|
||||
Unit::Dimension::Vector<Integer> simplifiedExponents;
|
||||
Integer (*operationOnExponents)(const Integer &, const Integer &) = entryUnitExponent == -1 ? Integer::Addition : Integer::Subtraction;
|
||||
|
||||
#if 0
|
||||
/* In the current algorithm, simplification is attempted using derived units
|
||||
* with no exponents. Some good simplifications might be missed:
|
||||
* For instance with _A^2*_s^2, a first attempt will be to simplify to
|
||||
* _C_A_s which has a bigger supportSize and will not be kept, the output
|
||||
* will stay _A^2*_s^2.
|
||||
* With the commented code, this issue is solved by trying to simplify with
|
||||
* the highest exponent possible, so that, in this example, _A^2*_s^2 can be
|
||||
* simplified to _C^2.
|
||||
* An optimization might be possible using algorithms minimizing the sum of
|
||||
* absolute difference of array elements */
|
||||
int n = 0;
|
||||
Integer best_norm;
|
||||
Integer norm_temp = unitsMetrics.norm;
|
||||
/* To extend this algorithm to square root simplifications, rational exponents
|
||||
* can be handled, and a 1/2 step can be used (but it should be asserted that
|
||||
* no square root simplification is performed if all exponents are integers.*/
|
||||
int step = 1;
|
||||
for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) {
|
||||
// Setting simplifiedExponents to unitsExponents
|
||||
simplifiedExponents.setCoefficientAtIndex(i, unitsExponents.coefficientAtIndex(i));
|
||||
}
|
||||
do {
|
||||
best_norm = norm_temp;
|
||||
n+= step;
|
||||
for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) {
|
||||
// Simplify unitsExponents with base units from derived unit
|
||||
simplifiedExponents.setCoefficientAtIndex(i, operationOnExponents(simplifiedExponents.coefficientAtIndex(i), step * entryUnitExponents->coefficientAtIndex(i)));
|
||||
}
|
||||
Unit::Dimension::Vector<Integer>::Metrics simplifiedMetrics = simplifiedExponents.metrics();
|
||||
norm_temp = Integer::Addition(n, simplifiedMetrics.norm);
|
||||
|
||||
} while (norm_temp.isLowerThan(best_norm));
|
||||
// Undo last step as it did not reduce the norm
|
||||
n -= step;
|
||||
|
||||
Integer derivedMetricNorm = n * step * entryUnitNorm;
|
||||
#else
|
||||
Integer derivedMetricNorm = entryUnitNorm;
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) {
|
||||
// Simplify unitsExponents with base units from derived unit
|
||||
#if 0
|
||||
simplifiedExponents.setCoefficientAtIndex(i, operationOnExponents(simplifiedExponents.coefficientAtIndex(i), -step * entryUnitExponents->coefficientAtIndex(i)));
|
||||
#else
|
||||
simplifiedExponents.setCoefficientAtIndex(i, operationOnExponents(unitsExponents.coefficientAtIndex(i), entryUnitExponents->coefficientAtIndex(i)));
|
||||
#endif
|
||||
}
|
||||
Unit::Dimension::Vector<Integer>::Metrics simplifiedMetrics = simplifiedExponents.metrics();
|
||||
// Compute metrics to evaluate the simplification
|
||||
|
||||
bool isSimpler = (1 + simplifiedMetrics.supportSize < unitsMetrics.supportSize);
|
||||
|
||||
/* Note: A metric is considered simpler if the support size (number of
|
||||
* symbols) is reduced. A norm taking coefficients into account is possible.
|
||||
* One could use the sum of all coefficients to favor _C_s from _A_s^2.
|
||||
* However, replacing _m_s^-2 with _N_kg^-1 should be avoided. */
|
||||
// TODO : Remove Metrics vectors entirely
|
||||
Unit::Dimension::Vector<Integer>::Metrics candidateMetrics = {
|
||||
.supportSize = 1 + simplifiedMetrics.supportSize,
|
||||
.norm = Integer::Addition(entryUnitNorm, simplifiedMetrics.norm)
|
||||
.norm = Integer::Addition(derivedMetricNorm, simplifiedMetrics.norm)
|
||||
};
|
||||
bool isSimpler = candidateMetrics.supportSize < unitsMetrics.supportSize ||
|
||||
(candidateMetrics.supportSize == unitsMetrics.supportSize &&
|
||||
candidateMetrics.norm.isLowerThan(unitsMetrics.norm));
|
||||
|
||||
if (isSimpler) {
|
||||
#if 0
|
||||
bestUnitExponent = entryUnitExponent * n * step;
|
||||
#else
|
||||
bestUnitExponent = entryUnitExponent;
|
||||
#endif
|
||||
bestRemainderExponents = simplifiedExponents;
|
||||
bestRemainderMetrics = simplifiedMetrics;
|
||||
/* unitsMetrics (support size and norm) is updated and will be taken into
|
||||
* account in next iterations of CanSimplifyUnitProduct. */
|
||||
unitsMetrics = candidateMetrics;
|
||||
}
|
||||
return isSimpler;
|
||||
@@ -402,9 +462,11 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
||||
while (unitsMetrics.supportSize > 1) {
|
||||
const Unit::Dimension * bestDim = nullptr;
|
||||
int8_t bestUnitExponent = 0;
|
||||
// Look up in the table of derived units.
|
||||
for (const Unit::Dimension * dim = Unit::DimensionTable + Unit::NumberOfBaseUnits; dim < Unit::DimensionTableUpperBound; dim++) {
|
||||
const Unit::Dimension::Vector<int8_t> * entryUnitExponents = dim->vector();
|
||||
int8_t entryUnitNorm = entryUnitExponents->metrics().norm;
|
||||
// A simplification is tried by either multiplying or dividing
|
||||
if (CanSimplifyUnitProduct(
|
||||
unitsExponents, unitsMetrics,
|
||||
entryUnitExponents, entryUnitNorm, 1,
|
||||
@@ -417,30 +479,48 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
||||
bestUnitExponent, bestRemainderExponents, bestRemainderMetrics
|
||||
))
|
||||
{
|
||||
/* If successful, unitsMetrics, bestUnitExponent,
|
||||
* bestRemainderExponents and bestRemainderMetrics have been updated*/
|
||||
bestDim = dim;
|
||||
}
|
||||
}
|
||||
if (bestDim == nullptr) {
|
||||
// No simplification could be performed
|
||||
break;
|
||||
}
|
||||
// Build and add the best derived unit
|
||||
Expression derivedUnit = Unit::Builder(bestDim, bestDim->stdRepresentative(), bestDim->stdRepresentativePrefix());
|
||||
|
||||
#if 0
|
||||
if (bestUnitExponent != 1) {
|
||||
derivedUnit = Power::Builder(derivedUnit, Rational::Builder(bestUnitExponent));
|
||||
}
|
||||
#else
|
||||
assert(bestUnitExponent == 1 || bestUnitExponent == -1);
|
||||
if (bestUnitExponent == -1) {
|
||||
derivedUnit = Power::Builder(derivedUnit, Rational::Builder(-1));
|
||||
}
|
||||
#endif
|
||||
|
||||
const int position = unitsAccu.numberOfChildren();
|
||||
unitsAccu.addChildAtIndexInPlace(derivedUnit, position, position);
|
||||
// Update remainder units and their exponents for next simplifications
|
||||
unitsExponents = bestRemainderExponents;
|
||||
unitsMetrics = bestRemainderMetrics;
|
||||
}
|
||||
// Apply simplifications
|
||||
if (unitsAccu.numberOfChildren() > 0) {
|
||||
// Divide by derived units
|
||||
units = Division::Builder(units, unitsAccu.clone()).deepReduce(reductionContext);
|
||||
Expression newUnits;
|
||||
// Separate units and generated values
|
||||
units = units.removeUnit(&newUnits);
|
||||
// Assemble final value
|
||||
Multiplication m = Multiplication::Builder(units);
|
||||
self.replaceWithInPlace(m);
|
||||
m.addChildAtIndexInPlace(self, 0, 1);
|
||||
self = m;
|
||||
// Update units with derived and base units
|
||||
if (newUnits.isUninitialized()) {
|
||||
units = unitsAccu;
|
||||
} else {
|
||||
|
||||
@@ -215,6 +215,33 @@ QUIZ_CASE(poincare_simplification_units) {
|
||||
assert_parsed_expression_simplify_to("_mol^-1", "1×_mol^\u0012-1\u0013");
|
||||
assert_parsed_expression_simplify_to("_cd^-1", "1×_cd^\u0012-1\u0013");
|
||||
|
||||
/* Power of SI units */
|
||||
assert_parsed_expression_simplify_to("_s^3", "1×_s^3");
|
||||
assert_parsed_expression_simplify_to("_m^2", "1×_m^2");
|
||||
assert_parsed_expression_simplify_to("_m^3", "1×_m^3");
|
||||
assert_parsed_expression_simplify_to("_m^(1/2)", "1×_m^\u00121/2\u0013");
|
||||
|
||||
/* Possible improvements */
|
||||
/* Ignored derived metrics :
|
||||
* -> Possible solution : Favor unities from user input. We do not want to
|
||||
* favor positive exponents to avoid a Velocity being displayed as _m*_Hz
|
||||
* assert_parsed_expression_simplify_to("_Hz", "_Hz");
|
||||
* assert_parsed_expression_simplify_to("_S", "_S");
|
||||
*/
|
||||
/* Non unitary exponents on Derived metrics :
|
||||
* -> See CanSimplifyUnitProduct in multiplication.cpp
|
||||
* assert_parsed_expression_simplify_to("_C^3", "1×_C^3");
|
||||
* assert_parsed_expression_simplify_to("_N^(1/2)", "1×_N^\u00121/2\u0013");
|
||||
*/
|
||||
/* Taking exponents complexity into account :
|
||||
* -> See note on metrics in CanSimplifyUnitProduct in multiplication.cpp
|
||||
* assert_parsed_expression_simplify_to("_C×_s", "1×_C×_s");
|
||||
* assert_parsed_expression_simplify_to("_C^10", "1×_C^10");
|
||||
* assert_parsed_expression_simplify_to("_ha", "1×_ha");
|
||||
* FIXME : int8_t norm metric overflow, only visible with a non constant norm
|
||||
* assert_parsed_expression_simplify_to("_C^130", "1×_C^130"); */
|
||||
assert_parsed_expression_simplify_to("_m_s^-2", "1×_m×_s^\u0012-2\u0013");
|
||||
|
||||
/* SI derived units with special names and symbols */
|
||||
assert_parsed_expression_simplify_to("_kg×_m×_s^(-2)", "1×_N");
|
||||
assert_parsed_expression_simplify_to("_kg×_m^(-1)×_s^(-2)", "1×_Pa");
|
||||
@@ -224,10 +251,6 @@ QUIZ_CASE(poincare_simplification_units) {
|
||||
assert_parsed_expression_simplify_to("_kg×_m^2×_s^(-3)×_A^(-1)", "1×_V");
|
||||
assert_parsed_expression_simplify_to("_m^(-2)×_kg^(-1)×_s^4×_A^2", "1×_F");
|
||||
assert_parsed_expression_simplify_to("_kg×_m^2×_s^(-3)×_A^(-2)", "1×_Ω");
|
||||
// FIXME _S should not be simplified to _Ω^(-1)
|
||||
// A possible solution: a unit with exponent +1 is simpler than a unit with exponent -1.
|
||||
// The same should probably go for Hz.
|
||||
// assert_parsed_expression_simplify_to("_kg^(-1)×_m^(-2)×_s^3×_A^2", "1×_S");
|
||||
assert_parsed_expression_simplify_to("_kg×_m^2×_s^(-2)×_A^(-1)", "1×_Wb");
|
||||
assert_parsed_expression_simplify_to("_kg×_s^(-2)×_A^(-1)", "1×_T");
|
||||
assert_parsed_expression_simplify_to("_kg×_m^2×_s^(-2)×_A^(-2)", "1×_H");
|
||||
|
||||
Reference in New Issue
Block a user