Error propagation code
The troublesome part here is Abs
is not symbolically differentiable:
D[Abs[c], c]
% /. c -> 1
Abs'[c] Abs'[1] (* doesn't make sense *)
To circumvent this, one way is to use ND
instead of D
:
Needs["NumericalCalculus`"]
ND[Abs[a + b I], a, 1] /. b -> 1
0.707107
Another way is to use ComplexExpand
before differentiation:
D[Abs[a + b I] // ComplexExpand, a];
% /. {a -> 1, b -> 1.}
0.707107
Corresponding fixed code:
err1[F_, w__] := {F[First /@ {w} /. List -> Sequence],
Block[{parms = Table[Unique[], {x, 1, Length[{w}]}],
values = Table[{w}[[i, 1]], {i, 1, Length[{w}]}],
errors = Table[{w}[[i, 2]], {i, 1, Length[{w}]}]},
Sqrt[
Total[Table[(D[ComplexExpand[F[parms /. List -> Sequence]], parms[[i]]]*
errors[[i]])^2,
{i, 1, Length[values]}] /.
Table[parms[[i]] -> values[[i]], {i, 1, Length[values]}]]]]}
err2[F_, w__] := {F[First /@ {w} /. List -> Sequence],
Block[{parms = Table[Unique[], {x, 1, Length[{w}]}],
values = Table[{w}[[i, 1]], {i, 1, Length[{w}]}],
errors = Table[{w}[[i, 2]], {i, 1, Length[{w}]}]},
Sqrt[
Total[Table[(ND[F[parms /. List -> Sequence], parms[[i]], values[[i]]]*
errors[[i]])^2,
{i, 1, Length[values]}] /.
Table[parms[[i]] -> values[[i]], {i, 1, Length[values]}]]]]}
And my attempt to make them conciser:
err1c[F_, w__] :=
Block[{var =
Table[Unique[], {Length@{w}}]}, {F @@ #, (#2^2).MapThread[
ND[F @@ var, #, #2] &, {var, #}]^2 /. Thread[var -> #] //
Sqrt} & @@ ({w}\[Transpose])]
err2c[F_, w__] :=
Block[{var =
Table[Unique[], {Length@{w}}]}, {F @@ #, (#2^2).(D[
F @@ var // ComplexExpand, {var}] /. Thread[var -> #])^2 //
Sqrt} & @@ ({w}\[Transpose])]
As of Mathematica 12, there are some experimental functions for uncertainty propagation. In particular, see AroundReplace
Using ComplexExpand[] (as suggested by @xzczd),
AroundReplace[Abs[x + I y] // ComplexExpand, {x -> Around[2, 0.1], y -> Around[3, 0.2]}]
Around[3.605551275463989, 0.17541160386140586`]