-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Measurement in IAU #464
Comments
It seems that the IAU is not the only library customer for such a utility. #510 mentioned plenty of constants that are also specified with some uncertainty that might change over the years if we get a better way to measure them. |
We should also study what Python did for a similar feature: https://pythonhosted.org/uncertainties. |
Some additional resources of interest might be:
... the issue I see is that this can be very simple for measurement with uncorrelated stdev errors ... up to very complex when handling this thoroughly for correlated errors, n-dimensional systems, or errors that go beyond statistical r.m.s.-style errors. |
Hi @mpusz and @mattkretz, I have been working a bit on the concept of propagation of uncertainties. The implementation is showcased in this compiler-explorer example. The core of this implementation is based on the enum class Correlation { UnCorrelated, Correlated };
template <is_arithmetic_or_complex_v T, Correlation correlation>
struct UncertainValue;
template <is_arithmetic_or_complex_v T>
struct UncertainValue<T, Correlation::UnCorrelated> {
using value_type = T;
T value; /// stores the mean value
T uncertainty = static_cast<T>(0); /// stores the standard deviation of the value
UncertainValue(T val, T uncert) : value(val), uncertainty(uncert) {}
};
template <typename T>
UncertainValue(T, T) -> UncertainValue<T, Correlation::UnCorrelated>;
template <is_arithmetic_or_complex_v T>
struct UncertainValue<T, Correlation::Correlated> {
using value_type = T;
T value; /// stores the mean value
T uncertainty = static_cast<T>(0); /// stores the standard deviation of the value
std::uint64_t correlationID = 0UZ; /// correlation ID - fully correlated if ID1==ID2 - issue: redundant 8bytes for each samples
UncertainValue(T val, T uncert, std::uint64_t correlationID_ = get_unique_id()) : value(val), uncertainty(uncert), correlationID(correlationID_) {}
};
template <typename T>
UncertainValue(T, T, std::uint64_t) -> UncertainValue<T, Correlation::Correlated>; Notably, I'd would like your input on:
To give a flavour of the operators needed for the propagation and to give the flavour of the boilerplate code I mentioned above: template<typename T, typename U>
requires UncertainValueType<T> || UncertainValueType<U>
constexpr auto operator+(const T& lhs, const U& rhs) noexcept {
using ValueTypeT = UncertainValueType_t<T>;
using ValueTypeU = UncertainValueType_t<U>;
if constexpr (UncertainValueType<T> && UncertainValueType<U>) {
if constexpr (is_complex_v<ValueTypeT> || is_complex_v<ValueTypeU>) {
// we are dealing with complex numbers
if constexpr (CorrelatedUncertainValue<T> && CorrelatedUncertainValue<U>) {
if (areCorrelated(lhs, rhs)) {
// add value and uncertainties as if they are vectors in 2D space because they are correlated.
return T{lhs.value + rhs.value, lhs.uncertainty + rhs.uncertainty, lhs.correlationID};
}
}
// values are not both complex and/or not both correlated -> use the standard uncorrelated calculation.
ValueTypeT newUncertainty = {
std::hypot(std::real(lhs.uncertainty), std::real(rhs.uncertainty)),
std::hypot(std::imag(lhs.uncertainty), std::imag(rhs.uncertainty))
};
return T{lhs.value + rhs.value, newUncertainty};
} else {
// both ValueType[T,U] are arithmetic uncertainties
if constexpr (CorrelatedUncertainValue<T> && CorrelatedUncertainValue<U>) {
if (areCorrelated(lhs, rhs)) {
return T{lhs.value + rhs.value, lhs.uncertainty + rhs.uncertainty, lhs.correlationID};
}
}
return T{lhs.value + rhs.value, std::hypot(lhs.uncertainty, rhs.uncertainty)};
}
} else if constexpr (UncertainValueType<T> && is_arithmetic_or_complex_v<ValueTypeU>) {
return T{lhs.value + rhs, lhs.uncertainty};
} else if constexpr (is_arithmetic_or_complex_v<ValueTypeT> && UncertainValueType<U>) {
return U{lhs + rhs.value, rhs.uncertainty};
} else {
static_assert(std::is_arithmetic_v<ValueTypeT> && std::is_arithmetic_v<ValueTypeU>);
return lhs + rhs; // unlikely to be called due to default '+' definition
}
} I appreciate any insights, comments, or suggestions you can provide to improve this implementation. Your expertise in this area is invaluable. |
Hi @RalphSteinhagen, thanks for sharing this nice code! I am on vacation now, so I did not have much time for this review. I will probably come back to you with more details when I will learn more about handling uncertainties. For now, a really short feedback:
|
Absolutely ... 🙈 ... was a bit q-n-d for this concept.
I'd prefer to test w.r.t. the functionality rather than the actual implementation. This should help composition and help users to have parallel functionally equivalent implementation of the struct.
This is IMO a longer discussion to be had. There are pros/cons for both sides that need to be evaluated. |
ISO 80000-6 provides the following text output for uncertainty:
Which means |
That's very similar to the example in Part 1's §7.2.4. |
Improve and productize a
measurement
class and use it in the definition of the IAU system based on https://web.archive.org/web/20131110215339/http://asa.usno.navy.mil/static/files/2014/Astronomical_Constants_2014.pdf.The text was updated successfully, but these errors were encountered: