/
Measurements.jl
135 lines (116 loc) · 5.07 KB
/
Measurements.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
### Measurements.jl --- Uncertainty propagation library
#
# Copyright (C) 2016, 2017 Mosè Giordano.
#
# Maintainer: Mosè Giordano <mose AT gnu DOT org>
# Keywords: uncertainty, error propagation, physics
#
# This file is a part of Measurements.jl.
#
# License is MIT "Expat".
#
### Commentary:
#
# This file is the entry point of the package: it defines the new `Measurement'
# type, some functions to handle it within Julia and the new functions provided
# by the package and exposed to users.
#
### Code:
__precompile__()
module Measurements
# Calculus is used to calculate numerical derivatives in "@uncertain" macro.
using Calculus
# Functions provided by this package and exposed to users
export Measurement, measurement, ±
# Define the "Derivatives" type, used inside "Measurement" type. This should be
# a lightweight and immutable dictionary.
include("derivatives-type.jl")
##### New Type: Measurement
# Definition. The Measurement type is composed by the following fields:
# * val: the nominal value of the measurement
# * err: the uncertainty, assumed to be standard deviation
# * tag: a (hopefully) unique identifier, it is used to identify a specific
# measurement in the list of derivatives. NOTE: independent measurements
# have tag > 0. For dependent quantities the tag is 0.
# * der: the list of derivates. It is a lightweight dictionary, whose keys
# are the tuples (nominal value, uncertainty, tag) of all independent
# variables from which the object has been derived, the corresponding value
# is the partial derivative of the object with respect to that independent
# variable. This dictionary is useful to trace the contribution of each
# measurement and propagate the uncertainty in the case of functions with
# more than one argument (in order to deal with correlation between
# arguments).
struct Measurement{T<:AbstractFloat} <: AbstractFloat
val::T
err::T
tag::UInt64
der::Derivatives{T}
end
function Measurement(val::V, err::E, tag::UInt64,
der::Derivatives{D}) where {V,E,D}
T = promote_type(V, E, D)
return Measurement(T(val), T(err), tag, Derivatives{T}(der))
end
Measurement{T}(x::Measurement{S}) where {T,S} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T,S <: Real} = convert(Measurement{T}, x)
# disambiguities
Measurement{T}(x::S) where {T, S<:Rational} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T, S<:Complex} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T, S<:Base.TwicePrecision} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {P, T, S<:Rational{P}} = convert(Measurement{T}, x)
Measurement{T}(x::S) where {T, S <: AbstractChar} = convert(Measurement{T}, x)
function Measurement{T}(::S) where {T, S}
throw(ArgumentError("cannot convert `$S` to `Measurement{$T}`"))
end
# Functions to quickly create an empty Derivatives object.
@generated empty_der1(x::Measurement{T}) where {T<:AbstractFloat} = Derivatives{T}()
@generated empty_der2(x::T) where {T<:AbstractFloat} = Derivatives{x}()
# Start from 1, 0 is reserved to derived quantities
const tag_counter = Threads.Atomic{UInt64}(1)
measurement(x::Measurement) = x
measurement(val::T) where {T<:AbstractFloat} = Measurement(val, zero(T), UInt64(0), empty_der2(val))
measurement(val::Real) = measurement(float(val))
function measurement(val::T, err::T) where {T<:AbstractFloat}
newder = empty_der2(val)
if iszero(err)
Measurement{T}(val, err, UInt64(0), newder)
else
tag = Threads.atomic_add!(tag_counter, UInt64(1))
return Measurement{T}(val, err, tag, Derivatives(newder, (val, err, tag)=>one(T)))
end
end
measurement(val::Real, err::Real) = measurement(promote(float(val), float(err))...)
measurement(::Missing, ::Union{Real,Missing} = missing) = missing
const ± = measurement
"""
measurement(val::Real, [err::Real]) -> Measurement
measurement(::Missing, [err::Union{Real,Missing}]) -> Missing
val ± err -> Measurement
Return a `Measurement` object with `val` as nominal value and `err` as
uncertainty. `err` defaults to 0 if omitted.
The binary operator `±` is equivalent to `measurement`, so you can construct a
`Measurement` object by explicitly writing `123 ± 4`.
If `val` is `missing`, `missing` is returned.
"""
measurement
include("conversions.jl")
include("comparisons-tests.jl")
include("utils.jl")
include("math.jl")
include("linear_algebra.jl")
include("show.jl")
include("parsing.jl")
if !isdefined(Base,:get_extension)
using Requires
using RecipesBase
include("../ext/MeasurementsRecipesBaseExt.jl")
end
@static if !isdefined(Base, :get_extension)
function __init__()
@require BaseType = "7fbed51b-1ef5-4d67-9085-a4a9b26f478c" include("../ext/MeasurementsBaseTypeExt.jl")
@require Unitful="1986cc42-f94f-5a68-af5c-568840ba703d" include("../ext/MeasurementsUnitfulExt.jl")
@require SpecialFunctions="276daf66-3868-5448-9aa4-cd146d93841b" include("../ext/MeasurementsSpecialFunctionsExt.jl")
@require Juno="e5e0dc1b-0480-54bc-9374-aad01c23163d" include("../ext/MeasurementsJunoExt.jl")
end
end
end # module