[4bae1ef] | 1 | # This program is public domain |
---|
| 2 | """ |
---|
| 3 | Error propogation algorithms for simple arithmetic |
---|
| 4 | |
---|
| 5 | Warning: like the underlying numpy library, the inplace operations |
---|
| 6 | may return values of the wrong type if some of the arguments are |
---|
| 7 | integers, so be sure to create them with floating point inputs. |
---|
| 8 | """ |
---|
| 9 | from __future__ import division # Get true division |
---|
[9a5097c] | 10 | import numpy as np |
---|
[4bae1ef] | 11 | |
---|
| 12 | |
---|
| 13 | def div(X, varX, Y, varY): |
---|
| 14 | """Division with error propagation""" |
---|
| 15 | # Direct algorithm: |
---|
| 16 | # Z = X/Y |
---|
| 17 | # varZ = (varX/X**2 + varY/Y**2) * Z**2 |
---|
| 18 | # = (varX + varY * Z**2) / Y**2 |
---|
| 19 | # Indirect algorithm to minimize intermediates |
---|
| 20 | Z = X/Y # truediv => Z is a float |
---|
| 21 | varZ = Z**2 # Z is a float => varZ is a float |
---|
| 22 | varZ *= varY |
---|
| 23 | varZ += varX |
---|
| 24 | T = Y**2 # Doesn't matter if T is float or int |
---|
| 25 | varZ /= T |
---|
| 26 | return Z, varZ |
---|
| 27 | |
---|
| 28 | |
---|
| 29 | def mul(X, varX, Y, varY): |
---|
| 30 | """Multiplication with error propagation""" |
---|
| 31 | # Direct algorithm: |
---|
| 32 | Z = X * Y |
---|
| 33 | varZ = Y**2 * varX + X**2 * varY |
---|
| 34 | # Indirect algorithm won't ensure floating point results |
---|
| 35 | # varZ = Y**2 |
---|
| 36 | # varZ *= varX |
---|
| 37 | # Z = X**2 # Using Z to hold the temporary |
---|
| 38 | # Z *= varY |
---|
| 39 | # varZ += Z |
---|
| 40 | # Z[:] = X |
---|
| 41 | # Z *= Y |
---|
| 42 | return Z, varZ |
---|
| 43 | |
---|
| 44 | |
---|
| 45 | def sub(X, varX, Y, varY): |
---|
| 46 | """Subtraction with error propagation""" |
---|
| 47 | Z = X - Y |
---|
| 48 | varZ = varX + varY |
---|
| 49 | return Z, varZ |
---|
| 50 | |
---|
| 51 | |
---|
| 52 | def add(X, varX, Y, varY): |
---|
| 53 | """Addition with error propagation""" |
---|
| 54 | Z = X + Y |
---|
| 55 | varZ = varX + varY |
---|
| 56 | return Z, varZ |
---|
| 57 | |
---|
| 58 | |
---|
| 59 | def exp(X, varX): |
---|
| 60 | """Exponentiation with error propagation""" |
---|
[9a5097c] | 61 | Z = np.exp(X) |
---|
[4bae1ef] | 62 | varZ = varX * Z**2 |
---|
| 63 | return Z, varZ |
---|
| 64 | |
---|
| 65 | |
---|
| 66 | def log(X, varX): |
---|
| 67 | """Logarithm with error propagation""" |
---|
[9a5097c] | 68 | Z = np.log(X) |
---|
[4bae1ef] | 69 | varZ = varX / X**2 |
---|
| 70 | return Z, varZ |
---|
| 71 | |
---|
| 72 | # Confirm this formula before using it |
---|
| 73 | # def pow(X,varX, Y,varY): |
---|
| 74 | # Z = X**Y |
---|
[9a5097c] | 75 | # varZ = (Y**2 * varX/X**2 + varY * np.log(X)**2) * Z**2 |
---|
[4bae1ef] | 76 | # return Z,varZ |
---|
| 77 | # |
---|
| 78 | |
---|
| 79 | |
---|
| 80 | def pow(X, varX, n): |
---|
| 81 | """X**n with error propagation""" |
---|
| 82 | # Direct algorithm |
---|
| 83 | # Z = X**n |
---|
| 84 | # varZ = n*n * varX/X**2 * Z**2 |
---|
| 85 | # Indirect algorithm to minimize intermediates |
---|
| 86 | Z = X**n |
---|
| 87 | varZ = varX / X |
---|
| 88 | varZ /= X |
---|
| 89 | varZ *= Z |
---|
| 90 | varZ *= Z |
---|
| 91 | varZ *= n**2 |
---|
| 92 | return Z, varZ |
---|
| 93 | |
---|
| 94 | |
---|
| 95 | def div_inplace(X, varX, Y, varY): |
---|
| 96 | """In-place division with error propagation""" |
---|
| 97 | # Z = X/Y |
---|
| 98 | # varZ = (varX + varY * (X/Y)**2) / Y**2 = (varX + varY * Z**2) / Y**2 |
---|
| 99 | X /= Y # X now has Z = X/Y |
---|
| 100 | T = X**2 # create T with Z**2 |
---|
| 101 | T *= varY # T now has varY * Z**2 |
---|
| 102 | varX += T # varX now has varX + varY*Z**2 |
---|
| 103 | del T # may want to use T[:] = Y for vectors |
---|
| 104 | T = Y # reuse T for Y |
---|
| 105 | T **= 2 # T now has Y**2 |
---|
| 106 | varX /= T # varX now has varZ |
---|
| 107 | return X, varX |
---|
| 108 | |
---|
| 109 | |
---|
| 110 | def mul_inplace(X, varX, Y, varY): |
---|
| 111 | """In-place multiplication with error propagation""" |
---|
| 112 | # Z = X * Y |
---|
| 113 | # varZ = Y**2 * varX + X**2 * varY |
---|
| 114 | T = Y**2 # create T with Y**2 |
---|
| 115 | varX *= T # varX now has Y**2 * varX |
---|
| 116 | del T # may want to use T[:] = X for vectors |
---|
| 117 | T = X # reuse T for X**2 * varY |
---|
| 118 | T **=2 # T now has X**2 |
---|
| 119 | T *= varY # T now has X**2 * varY |
---|
| 120 | varX += T # varX now has varZ |
---|
| 121 | X *= Y # X now has Z |
---|
| 122 | return X, varX |
---|
| 123 | |
---|
| 124 | |
---|
| 125 | def sub_inplace(X, varX, Y, varY): |
---|
| 126 | """In-place subtraction with error propagation""" |
---|
| 127 | # Z = X - Y |
---|
| 128 | # varZ = varX + varY |
---|
| 129 | X -= Y |
---|
| 130 | varX += varY |
---|
| 131 | return X, varX |
---|
| 132 | |
---|
| 133 | |
---|
| 134 | def add_inplace(X, varX, Y, varY): |
---|
| 135 | """In-place addition with error propagation""" |
---|
| 136 | # Z = X + Y |
---|
| 137 | # varZ = varX + varY |
---|
| 138 | X += Y |
---|
| 139 | varX += varY |
---|
| 140 | return X, varX |
---|
| 141 | |
---|
| 142 | |
---|
| 143 | def pow_inplace(X, varX, n): |
---|
| 144 | """In-place X**n with error propagation""" |
---|
| 145 | # Direct algorithm |
---|
| 146 | # Z = X**n |
---|
| 147 | # varZ = abs(n) * varX/X**2 * Z**2 |
---|
| 148 | # Indirect algorithm to minimize intermediates |
---|
| 149 | varX /= X |
---|
| 150 | varX /= X # varX now has varX/X**2 |
---|
| 151 | X **= n # X now has Z = X**n |
---|
| 152 | varX *= X |
---|
| 153 | varX *= X # varX now has varX/X**2 * Z**2 |
---|
| 154 | varX *= n**2 # varX now has varZ |
---|
| 155 | return X, varX |
---|