Numeric error propagation: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) m (syntax highlighting fixup automation) |
SqrtNegInf (talk | contribs) (→{{header|Perl}}: eliminated 'uninitialized' warnings, added subroutine signatures, additional tweaks) |
||
Line 1,692: | Line 1,692: | ||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
Following code keeps track of covariance between variables. Each variable with error contains its mean value and components of error source from a set of |
Following code keeps track of covariance between variables. Each variable with error contains its mean value and components of error source from a set of independent variables. It's more than what the task requires. |
||
<syntaxhighlight lang="perl">use |
<syntaxhighlight lang="perl">use v5.36; |
||
package ErrVar; |
|||
use strict; |
|||
package ErrVar; |
|||
# helper function, apply f to pairs (a, b) from listX and listY |
|||
sub zip(&$$) { |
|||
my ($f, $x, $y) = @_; |
|||
my $l = $#$x; |
|||
if ($l < $#$y) { $l = $#$y }; |
|||
# helper function, apply function 'f' to pairs (a, b) from listX and listY |
|||
my @out; |
|||
sub zip ($f, $x, $y) { |
|||
my @out; |
|||
local $a = $x->[$_]; |
|||
$y = [(0) x @$x] unless @$y; # if not defined |
|||
local $b = $y->[$_]; |
|||
push @out, $f->($x->[$_], $y->[$_]) for 0 .. $#$x; |
|||
\@out |
|||
} |
|||
\@out |
|||
} |
} |
||
use overload |
use overload |
||
'""' => \&_str, |
|||
'+' => \&_add, |
|||
'-' => \&_sub, |
|||
'*' => \&_mul, |
|||
'/' => \&_div, |
|||
'bool' => \&_bool, |
|||
'<=>' => \&_ncmp, |
|||
'neg' => \&_neg, |
|||
'sqrt' => \&_sqrt, |
|||
'log' => \&_log, |
|||
'exp' => \&_exp, |
|||
'**' => \&_pow, |
|||
'**' => \&_pow, |
|||
; |
; |
||
# make a variable with mean value and a list of coefficient to |
# make a variable with mean value and a list of coefficient to |
||
# variables providing independent errors |
# variables providing independent errors |
||
sub make { |
sub make ($x, @v) { bless [$x, @v] } |
||
my $x = shift; |
|||
bless [$x, [@{+shift}]] |
|||
} |
|||
sub _str { sprintf "%g±%.3g", $_[0][0], sigma($_[0]) } |
|||
# mean value of the var, or just the input if it's not of this class |
# mean value of the var, or just the input if it's not of this class |
||
sub mean ($x) { ref $x && $x->isa(__PACKAGE__) ? $x->[0] : $x } |
|||
sub mean { |
|||
my $x = shift; |
|||
ref($x) && $x->isa(__PACKAGE__) ? $x->[0] : $x |
|||
} |
|||
# return variance index array |
# return variance index array |
||
sub vlist ($x) { ref $x && $x->isa(__PACKAGE__) ? $x->[1] : [] } |
|||
sub vlist { |
|||
my $x = shift; |
|||
ref($x) && $x->isa(__PACKAGE__) ? $x->[1] : []; |
|||
} |
|||
sub variance { |
sub variance ($x) { |
||
return 0 unless ref($x) and $x->isa(__PACKAGE__); |
|||
my $x = shift; |
|||
my $s; |
|||
return 0 unless ref($x) and $x->isa(__PACKAGE__); |
|||
$s += $_ * $_ for @{$x->[1]}; |
|||
my $s; |
|||
$s |
|||
$s += $_ * $_ for (@{$x->[1]}); |
|||
$s |
|||
} |
} |
||
sub covariance { |
sub covariance ($x, $y) { |
||
return 0 unless ref($x) && $x->isa(__PACKAGE__); |
|||
my ($x, $y) = @_; |
|||
return 0 unless ref($y) && $y->isa(__PACKAGE__); |
|||
my $s; |
|||
return 0 unless ref($y) && $y->isa(__PACKAGE__); |
|||
zip sub ($a,$b) { $s += $a * $b }, vlist($x), vlist($y); |
|||
$s |
|||
zip { $s += $a * $b } vlist($x), vlist($y); |
|||
$s |
|||
} |
} |
||
sub sigma { sqrt variance |
sub sigma ($v) { sqrt variance $v } |
||
# to determine if a var is probably zero. we use 1σ here |
# to determine if a var is probably zero. we use 1σ here |
||
sub _bool { |
sub _bool ($x, $, $) { |
||
abs(mean $x) > sigma $x |
|||
my $x = shift; |
|||
return abs(mean($x)) > sigma($x); |
|||
} |
} |
||
sub _ncmp { |
sub _ncmp ($a, $b, $) { |
||
return 0 unless my $x = $a - $b; |
|||
mean($x) > 0 ? 1 : -1 |
|||
} |
} |
||
sub _neg { |
sub _neg ($x, $, $) { |
||
bless [ -mean($x), [map(-$_, @{vlist $x}) ] ]; |
|||
my $x = shift; |
|||
bless [ -mean($x), [map(-$_, @{vlist($x)}) ] ]; |
|||
} |
} |
||
sub _add { |
sub _add ($x, $y, $) { |
||
my ($x0, $y0) = ( mean($x), mean($y)); |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
bless [$x0 + $y0, zip sub ($a,$b) {$a + $b}, $xv, $yv] |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
bless [$x0 + $y0, zip {$a + $b} $xv, $yv]; |
|||
} |
} |
||
sub _sub { |
sub _sub ($x, $y, $) { |
||
my ($x0, $y0) = ( mean($x), mean($y)); |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
bless [$x0 - $y0, zip sub ($a,$b) {$a - $b}, $xv, $yv] |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
bless [$x0 - $y0, zip {$a - $b} $xv, $yv]; |
|||
} |
} |
||
sub _mul { |
sub _mul ($x, $y, $) { |
||
my ($x0, $y0) = ( mean($x), mean($y)); |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
$xv = [ map($y0 * $_, @$xv) ]; |
|||
$yv = [ map($x0 * $_, @$yv) ]; |
|||
bless [$x0 * $y0, zip sub ($a,$b) {$a + $b}, $xv, $yv] |
|||
$yv = [ map($x0 * $_, @$yv) ]; |
|||
bless [$x0 * $y0, zip {$a + $b} $xv, $yv]; |
|||
} |
} |
||
sub _div { |
sub _div ($x, $y, $) { |
||
my ($x0, $y0) = ( mean($x), mean($y)); |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
$xv = [ map($_/$y0, @$xv) ]; |
|||
$yv = [ map($x0 * $_/$y0/$y0, @$yv) ]; |
|||
bless [$x0 / $y0, zip sub ($a,$b) {$a + $b}, $xv, $yv] |
|||
my ($xv, $yv) = (vlist($x), vlist($y)); |
|||
$xv = [ map($_/$y0, @$xv) ]; |
|||
$yv = [ map($x0 * $_/$y0/$y0, @$yv) ]; |
|||
bless [$x0 / $y0, zip {$a + $b} $xv, $yv]; |
|||
} |
} |
||
sub _sqrt { |
sub _sqrt ($x, $, $) { |
||
my ($x0, $xv) = ( mean($x), vlist($x) ); |
|||
my $x = shift; |
|||
$x0 = sqrt($x0); |
|||
$xv = [ map($_ / 2 / $x0, @$xv) ]; |
|||
bless [$x0, $xv] |
|||
$x0 = sqrt($x0); |
|||
$xv = [ map($_ / 2 / $x0, @$xv) ]; |
|||
bless [$x0, $xv] |
|||
} |
} |
||
sub _pow { |
sub _pow ($x, $y, $) { |
||
if ($x < 0) { |
|||
die "Can't take pow of negative number $x" if int($y) != $y or $y & 1; |
|||
if ($swap) { ($x, $y) = ($y, $x) } |
|||
$x = -$x; |
|||
} |
|||
if (int($y) != $y || ($y & 1)) { |
|||
exp($y * log $x) |
|||
die "Can't take pow of negative number $x"; |
|||
} |
|||
$x = -$x; |
|||
} |
|||
exp($y * log $x) |
|||
} |
} |
||
sub _exp { |
sub _exp ($x, $, $) { |
||
my ($x0, $xv) = ( exp(mean($x)), vlist($x) ); |
|||
my $x = shift; |
|||
bless [ $x0, [map($x0 * $_, @$xv) ] ] |
|||
my $x0 = exp(mean($x)); |
|||
my $xv = vlist($x); |
|||
bless [ $x0, [map($x0 * $_, @$xv) ] ] |
|||
} |
} |
||
sub _log { |
sub _log ($x, $, $) { |
||
my ($x0, $xv) = ( mean($x), vlist($x) ); |
|||
my $x = shift; |
|||
bless [ log($x0), [ map($_ / $x0, @$xv) ] ] |
|||
my $x0 = mean($x); |
|||
my $xv = vlist($x); |
|||
bless [ log($x0), [ map($_ / $x0, @$xv) ] ] |
|||
} |
} |
||
sub _str { sprintf '%g±%.3g', $_[0][0], sigma($_[0]) } |
|||
"If this package were to be in its own file, you need some truth value to end it like this."; |
|||
package main; |
package main; |
||
Line 1,873: | Line 1,828: | ||
my $z1 = sqrt(($x1 - $x2) ** 2 + ($y1 - $y2) ** 2); |
my $z1 = sqrt(($x1 - $x2) ** 2 + ($y1 - $y2) ** 2); |
||
say "distance: $z1"; |
|||
# this is not for task requirement |
# this is not for task requirement |
||
my $a = $x1 + $x2; |
my $a = $x1 + $x2; |
||
my $b = $y1 - 2 * $x2; |
my $b = $y1 - 2 * $x2; |
||
say "covariance between $a and $b: ", $a->covariance($b);</syntaxhighlight> |
|||
{{out}} |
|||
<pre>distance: 111.803±2.49 |
|||
covariance between 300±2.46 and -350±4.56: -9.68</syntaxhighlight> |
|||
covariance between 300±2.46 and -350±4.56: -9.68</pre> |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |