Esempio n. 1
0
def implicit_real(fn, x_min, x_max, epsilon):
    """Return the uncertain real number ``x``, that solves :math:`fn(x) = 0`

    The function fn() must take a single argument and x_min and
    x_max must define a range in which there is one (and only one)
    sign change in fn(x).

    The number 'epsilon' is a tolerance for accepting convergence.
    
    A RuntimeError will be raised if the root-search algorithm fails.

    An AssertionError will be raised if the preconditions for a 
    search to begin are not satisfied.

    Parameters
    ----------
    fn : a function of one argument
    x_min, x_max, epsilon : float

    Returns
    -------
    UncertainReal

    .. versionadded:: 1.3.4

    """
    xk, dy_dx = nr_get_root(fn, x_min, x_max, epsilon)

    # In an implicit function F(x,...) = 0, where
    # we solve F = 0 by finding a value for `x`,
    # `x` depends implicitly on the other arguments.
    # The influence set of `F` and the influence set
    # of `x` should are the same.
    # `x` is not an elementary uncertain number;
    # it is implicitly a function of the other
    # arguments to F().

    # The components of uncertainty of `x` are related to
    # the components of `F` as follows:
    #       u_i(x) = -( dF/dx_i / dF/dx ) * u_i(xi)

    y = fn(UncertainReal._constant(xk))
    dx_dy = -1 / dy_dx

    return UncertainReal(xk, vector.scale_vector(y._u_components, dx_dy),
                         vector.scale_vector(y._d_components, dx_dy),
                         vector.scale_vector(y._i_components, dx_dy))
Esempio n. 2
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)