Esempio n. 1
0
    def test_construction(self):
        # import here to get the tests to work with Python 2.7 on linux
        from GTC.context import _context

        x_value = 1.2
        x_u = 0.5
        x_dof = 6
        x1 = UncertainReal._elementary(x_value,
                                       x_u,
                                       x_dof,
                                       None,
                                       independent=True)
        x2 = UncertainReal._elementary(x_value,
                                       x_u,
                                       x_dof,
                                       None,
                                       independent=True)

        # uid's must be in order
        self.assertTrue(x1._node.uid < x2._node.uid)
        self.assertEqual(x_dof, x1._node.df)
        self.assertEqual(x_dof, x2._node.df)

        self.assertTrue(
            _context._registered_leaf_nodes[x1._node.uid].df == x_dof)
        self.assertTrue(
            _context._registered_leaf_nodes[x2._node.uid].df == x_dof)

        # illegal dof is checked when the object is created
        self.assertRaises(ValueError, UncertainReal._elementary, x_value, x_u,
                          0, None, False)
Esempio n. 2
0
    def test(self):
        x = ureal(1, 1, 4)
        self.assertEqual(4, x.df)
        self.assertEqual(4, welch_satterthwaite(x)[1])

        # product with zero values
        x1 = ureal(0, 1, 4)
        x2 = ureal(0, 1, 3)
        y = x1 * x2
        self.assertEqual(0, y.u)
        self.assertTrue(nan is y.df)

        # Pathological case - not sure it can be created in practice
        x1 = ureal(1, 1)
        x2 = UncertainReal._elementary(1, 0, 4, label=None, independent=True)
        x3 = UncertainReal._elementary(1, 0, 3, label=None, independent=True)
        y = x1 + x2 + x3
        self.assertEqual(len(y._u_components), 3)
        self.assertTrue(inf is y.df)
Esempio n. 3
0
def nr_get_root(fn, x_min, x_max, epsilon):
    """Return the x-location of the root and the derivative at that point.
    
    This is a utility function used by implicit_real(). 
    It searches within the range for a real root.
    
    Parameters
    ----------
    fn : a function with a single argument
    x_min, x_max, epsilon : float

    Returns
    -------
    (float,float)

    .. versionadded:: 1.3.4

    """
    if x_max <= x_min:
        raise RuntimeError("Invalid search range: {!s}".format((x_min, x_max)))

    lower, upper = x_min, x_max

    ureal = lambda x, u: UncertainReal._elementary(x, u, inf, None, True)
    value = lambda x: x.x if isinstance(x, UncertainReal) else float(x)

    x = ureal(lower, 1.0)
    f_x = fn(x)
    fl = value(f_x)

    assert isinstance(f_x,UncertainReal),\
           "fn() must return an UncertainReal, got: %s" % type(f_x)

    if abs(fl) < epsilon:
        return fl, f_x.sensitivity(x)

    x = ureal(upper, 1.0)
    f_x = fn(x)
    fu = value(f_x)

    if abs(fu) < epsilon:
        return fu, f_x.sensitivity(x)

    if fl * fu >= 0.0:
        raise RuntimeError(
            "range does not appear to contain a root: {}".format((fl, fu)))

    if fl > 0.0:
        lower, upper = upper, lower

    # First place to look is the middle
    xk = (lower + upper) / 2.0
    dx2 = abs(upper - lower)
    dx = dx2

    x = ureal(xk, 1.0)
    f_x = fn(x)
    f = value(f_x)
    df = f_x.sensitivity(x)

    for i in xrange(100):
        if (((xk - upper) * df - f) * ((xk - lower) * df - f) > 0.0
                or (abs(2.0 * f) > abs(dx2 * df))):
            # Bisect if Newton out of range or
            # not decreasing fast enough.
            dx2 = dx
            dx = (upper - lower) / 2.0

            # If the bisection is too small then the root is found
            if (abs(dx) <= epsilon):
                return xk, df
            else:
                xk = lower + dx

        else:
            # Use Newton step
            dx2 = dx
            dx = f / df

            # If the change is ~ 0 then accept the root
            if (abs(dx) <= epsilon):
                return xk, df
            else:
                xk -= dx

        # Test convergence
        if (abs(dx) <= epsilon):
            return xk, df

        # Evaluate for next iteration;
        x = ureal(xk, 1.0)
        f_x = fn(x)
        f = value(f_x)
        df = f_x.sensitivity(x)

        if (f < 0.0):
            lower = xk
        else:
            upper = xk

    raise RuntimeError("Failed to converge")
Esempio n. 4
0
File: type_b.py Progetto: MSLNZ/GTC
def line_fit_wtls(x, y, u_x=None, u_y=None, a_b=None, r_xy=None):
    """Perform straight-line regression with uncertainty in ``x`` and ``y``

    .. versionadded:: 1.2
    
    :arg x: list of uncertain real numbers for the independent variable
    :arg y: list of uncertain real numbers for the dependent variable
    :arg u_x: a sequence of uncertainties for the ``x`` data
    :arg u_y: a sequence of uncertainties for the ``y`` data
    :arg a_b: a pair of initial estimates for the intercept and slope
    :arg r_xy: correlation between x-y pairs [default: 0]

    Returns a :class:`~type_b.LineFitWTLS` object

    The elements of ``x`` and ``y`` must be uncertain numbers
    with non-zero uncertainties. If specified, the optional arguments 
    ``u_x`` and ``u_y`` will be used uncertainties to weight 
    the data for the regression, otherwise the uncertainties of
    the uncertain numbers in the sequences are used.
    
    The optional argument ``a_b`` can be used to provide a pair 
    of initial estimates for the intercept and slope. Otherwise, 
    initial estimates will be obtained by calling `line_fit_wls`.
    
    Implements a Weighted Total Least Squares algorithm
    that allows for correlation between x-y pairs. See reference: 
    
    M Krystek and M Anton, *Meas. Sci. Technol.* **22** (2011) 035101 (9pp)
        
    **Example**::

        # Pearson-York test data
        # see, e.g., Lybanon, M. in Am. J. Phys 52 (1), January 1984 
        >>> xin=[0.0,0.9,1.8,2.6,3.3,4.4,5.2,6.1,6.5,7.4]
        >>> wx=[1000.0,1000.0,500.0,800.0,200.0,80.0,60.0,20.0,1.8,1.0]
        >>> yin=[5.9,5.4,4.4,4.6,3.5,3.7,2.8,2.8,2.4,1.5]
        >>> wy=[1.0,1.8,4.0,8.0,20.0,20.0,70.0,70.0,100.0,500.0]

        # Convert weights to standard uncertainties 
        >>> uxin=[1./math.sqrt(wx_i) for wx_i in wx ]
        >>> uyin=[1./math.sqrt(wy_i) for wy_i in wy ]

        # Define uncertain numbers
        >>> x = [ ureal(xin_i,uxin_i) for xin_i,uxin_i in zip(xin,uxin) ]
        >>> y = [ ureal(yin_i,uyin_i) for yin_i,uyin_i in zip(yin,uyin) ]

        # TLS returns uncertain numbers
        >>> a,b = type_b.line_fit_wtls(x,y).a_b
        >>> a
        ureal(5.47991018...,0.29193349...,inf)
        >>> b
        ureal(-0.48053339...,0.057616740...,inf)

    """
    N = len(x)
    if N != len(y):
        raise RuntimeError(
            "Different sequence lengths: len({!r}) != len({!r})".format(x, y))

    if (u_x is not None or u_y is not None):
        if (u_x is None or u_y is None):
            raise RuntimeError("You must supply ``u_x`` and ``u_y``")
        elif (r_xy is None):
            # default value will be uncorrelated
            r_xy = [0] * len(u_x)

        if len(u_x) != N or len(u_y) != N:
            raise RuntimeError(
                "incompatible sequence lengths: {!r}, {!r}".format(u_x, u_y))

    for x_i, y_i in izip(x, y):
        assert isinstance(x_i, UncertainReal), 'uncertain real required'
        assert isinstance(y_i, UncertainReal), 'uncertain real required'

    # Needed to define UNs locally
    ureal = lambda x, u: UncertainReal._elementary(x, u, inf, None, True)

    if a_b is None:
        a_b = line_fit_wls(x, y, u_y).a_b

    a0 = value(a_b[0])
    b0 = value(a_b[1])

    # initial value for `alpha`
    alpha0 = math.atan(b0)

    # chi_sq(alpha0) -> chisquared
    chi_sq = ChiSq(x, y, u_x, u_y, r_xy)

    # Search for the minimum chi-squared wrt alpha
    x1 = alpha0 - HALF_PI
    x2 = alpha0 + HALF_PI

    # `brent` requires three points that bracket the minimum.
    # the `x1`, `alpha0`, `x2` parameters should be real,
    # but `data` will return an uncertain number
    # and expects an uncertain number argument.
    #
    # Returns x, fn(x) and df_dx(x), all floats
    alpha1, fn_alpha1, df_alpha1 = _dbrent(x1, alpha0, x2, chi_sq)

    # dChiSq_a( alpha ) will return dChiSq_dalpha(`alpha`)
    # dChiSq_a(alpha0) -> 1st partial derivative of chisquared at alpha0
    dChiSq_a = dChiSq_dalpha(x, y, u_x, u_y, r_xy)

    # Need the partial derivative of dChiSq_a wrt alpha
    alpha = ureal(alpha1, 1)
    F_alpha = dChiSq_a(alpha)
    dalpha_dF = -1.0 / F_alpha.sensitivity(alpha)

    # Now we define `alpha` with sensitivity to the ``x`` and ``y`` data,
    # via the object ``F_alpha``, which represents the 1st partial derivative
    # of chi-squared at alpha1 (ideally zero, but really only close to the root).
    F_alpha = dChiSq_a(UncertainReal._constant(alpha1))
    alpha = UncertainReal(alpha1, scale_vector(F_alpha._u_components,
                                               dalpha_dF),
                          scale_vector(F_alpha._d_components, dalpha_dF),
                          scale_vector(F_alpha._i_components, dalpha_dF))

    # The sensitivity of p_hat to the x and y data is via
    # `alpha`, `x_bar` and `y_bar` in eqn (43)
    p_hat = chi_sq.p_hat(alpha)

    # Note we have reversed the definitions of `a` and `b` here
    b = alpha._tan()
    a = p_hat / alpha._cos()

    N = len(x)

    ssr = chi_sq(UncertainReal._constant(alpha1)).x

    return LineFitWTLS(a, b, ssr, N)
Esempio n. 5
0
File: type_b.py Progetto: MSLNZ/GTC
def _dbrent(ax, bx, cx, fn, tol=math.sqrt(EPSILON)):
    """
    Minimise fn() and return x, fn(x) and df_dx(x), all floats
    
    `fn` must be a univariate function of an uncertain real that returns
    an uncertain real number.

    `ax`, `bx` and `cx` must be floats. `bx` must be between `ax` and `cx`
    and fn(bx) must be less than both fn(ax) and fn(cx).

    `context` - a GTC context
    
    `tol` - the fractional precision

    See also Numerical Recipes in C, 2nd ed, Section 10.3
    
    """
    ITMAX = 100

    deriv = lambda y, x: y.sensitivity(x)
    ureal = lambda x, u: UncertainReal._elementary(x, u, inf, None, True)
    e = 0.0  # The distance moved on the step before last

    a = ax if ax < cx else cx
    b = ax if ax > cx else cx

    assert a <= bx and bx <= b, "Invalid initial values in _dbrent"

    # if fn( ureal(bx,1)) > fn( ureal(a,1)) or fn(ureal(bx,1)) > fn(ureal(b,1)):
    # assert False

    x = w = v = bx

    _u_ = ureal(x, 1.0)
    fn_u = fn(_u_)

    fw = fv = fx = value(fn_u)
    dw = dv = dx = deriv(fn_u, _u_)

    # The routine keeps track of `a` and `b`, which bracket the minimum,
    # `x` is the point with the least function value found so far,
    # `w` is the point with the second least value, `v` is the previous
    # value of `w`, `u` is the point at which the function was most
    # recently evaluated.

    for i in xrange(ITMAX):

        xm = 0.5 * (a + b)
        tol1 = tol * abs(x) + ZEPS
        tol2 = 2.0 * tol1

        if abs(x - xm) <= (tol2 - 0.5 * (b - a)):
            return x, fx, dx

        if abs(e) > tol1:
            # initialise the d's to be out of bracket
            d1 = 2.0 * (b - a)
            d2 = d1

            # Secant method
            if dw != dx: d1 = (w - x) * dx / (dx - dw)
            if dv != dx: d2 = (v - x) * dx / (dx - dv)

            # Choose one estimate.
            # Insist that it be within the bracket
            # and on the side pointed to by the derivative at `x`
            u1 = x + d1
            u2 = x + d2
            OK1 = (a - u1) * (u1 - b) > 0.0 and dx * d1 <= 0.0
            OK2 = (a - u2) * (u2 - b) > 0.0 and dx * d2 <= 0.0

            olde, e = e, d

            if OK1 or OK2:
                if OK1 and OK2:
                    d = d1 if abs(d1) < abs(d2) else d2
                elif OK1:
                    d = d1
                else:
                    d = d2

                if abs(d) <= abs(0.5 * olde):
                    u = x + d
                    if (u - a < tol2) or (b - u < tol2):
                        d = math.copysign(tol1, xm - x)
                else:
                    # choose segment by the sign of the derivative
                    e = a - x if dx >= 0.0 else b - x
                    d = 0.5 * e
            else:
                e = a - x if dx >= 0.0 else b - x
                d = 0.5 * e

        else:
            e = a - x if dx >= 0.0 else b - x
            d = 0.5 * e

        if abs(d) >= tol1:
            u = x + d

            _u_ = ureal(u, 1.0)
            fn_u = fn(_u_)

            fu = value(fn_u)
            du = deriv(fn_u, _u_)

        else:
            # Smallest step possible
            u = x + math.copysign(tol1, d)

            _u_ = ureal(u, 1.0)
            fn_u = fn(_u_)

            fu = value(fn_u)
            du = deriv(fn_u, _u_)

            # If the minimum sized step downhill
            # goes up, then we are done!
            if fu > fx:
                return u, fu, du

        assert a <= u and u <= b, (a, u, b)  # invariant

        if fu <= fx:
            # Found a new best point

            # Update the bracket on one side so that the previous
            # `x` value is now the limit and the new `x` value is
            # contained.
            if u >= x:
                a = x
            else:
                b = x

            v, fv, dv = w, fw, dw
            w, fw, dw = x, fx, dx
            x, fx, dx = u, fu, du

            assert a <= x and x <= b, (a, x, b)  # invariant

        else:
            # The point `x` has not been bettered

            # `x` does not change, but `u` was inside the
            # bracket so we can tighten the noose.
            if u < x:
                a = u
            else:
                b = u

            # `w` is the second best point and `v` is the 3rd best
            if (fu <= fw) or (x == w):
                v, fv, dv = w, fw, dw
                w, fw, dw = u, fu, du

            elif (fu < fv) or (v == x) or (v == w):
                v, fv, dv = u, fu, du

            assert a <= x and x <= b, (a, x, b)  # invariant

        assert fx <= fw and fx <= fv  # invariant

    raise RuntimeError('Exceeded iteration limit in `_dbrent`')