示例#1
0
    def test__cholesky_banded(self, its=100):
        for it in range(its):
            size = random.choice([0, 1, randint(0, 10), randint(0, 100)])
            if rand_bool():
                mat_bm = gen_pos_def_BandMat(size, transposed=False)
            else:
                mat_bm = gen_symmetric_BandMat(size, transposed=False)
                # make it a bit more likely to be pos def
                bm.diag(mat_bm)[:] = np.abs(bm.diag(mat_bm)) + 0.1
            depth = mat_bm.l
            lower = rand_bool()
            if lower:
                mat_half_data = mat_bm.data[depth:]
            else:
                mat_half_data = mat_bm.data[:(depth + 1)]
            overwrite = rand_bool()

            mat_half_data_arg = mat_half_data.copy()
            try:
                chol_data = bla._cholesky_banded(
                    mat_half_data_arg, overwrite_ab=overwrite, lower=lower
                )
            except la.LinAlgError as e:
                # First part of the message is e.g. "2-th leading minor".
                msgRe = (r'^' + re.escape(str(e)[:15]) +
                         r'.*not positive definite$')
                with self.assertRaisesRegexp(la.LinAlgError, msgRe):
                    sla.cholesky(mat_bm.full(), lower=lower)
            else:
                assert np.shape(chol_data) == (depth + 1, size)
                if lower:
                    chol_bm = bm.BandMat(depth, 0, chol_data)
                    mat_bm_again = bm.dot_mm(chol_bm, chol_bm.T)
                else:
                    chol_bm = bm.BandMat(0, depth, chol_data)
                    mat_bm_again = bm.dot_mm(chol_bm.T, chol_bm)
                assert_allclose(mat_bm_again.full(), mat_bm.full())

                if size > 0:
                    self.assertEqual(
                        np.may_share_memory(chol_data, mat_half_data_arg),
                        overwrite
                    )

            if not overwrite:
                assert np.all(mat_half_data_arg == mat_half_data)
示例#2
0
    def test__cholesky_banded(self, its=100):
        for it in range(its):
            size = random.choice([0, 1, randint(0, 10), randint(0, 100)])
            if rand_bool():
                mat_bm = gen_pos_def_BandMat(size, transposed=False)
            else:
                mat_bm = gen_symmetric_BandMat(size, transposed=False)
                # make it a bit more likely to be pos def
                bm.diag(mat_bm)[:] = np.abs(bm.diag(mat_bm)) + 0.1
            depth = mat_bm.l
            lower = rand_bool()
            if lower:
                mat_half_data = mat_bm.data[depth:]
            else:
                mat_half_data = mat_bm.data[:(depth + 1)]
            overwrite = rand_bool()

            mat_half_data_arg = mat_half_data.copy()
            try:
                chol_data = bla._cholesky_banded(mat_half_data_arg,
                                                 overwrite_ab=overwrite,
                                                 lower=lower)
            except la.LinAlgError as e:
                # First part of the message is e.g. "2-th leading minor".
                msgRe = (r'^' + re.escape(str(e)[:15]) +
                         r'.*not positive definite$')
                with self.assertRaisesRegexp(la.LinAlgError, msgRe):
                    sla.cholesky(mat_bm.full(), lower=lower)
            else:
                assert np.shape(chol_data) == (depth + 1, size)
                if lower:
                    chol_bm = bm.BandMat(depth, 0, chol_data)
                    mat_bm_again = bm.dot_mm(chol_bm, chol_bm.T)
                else:
                    chol_bm = bm.BandMat(0, depth, chol_data)
                    mat_bm_again = bm.dot_mm(chol_bm.T, chol_bm)
                assert_allclose(mat_bm_again.full(), mat_bm.full())

                if size > 0:
                    self.assertEqual(
                        np.may_share_memory(chol_data, mat_half_data_arg),
                        overwrite)

            if not overwrite:
                assert np.all(mat_half_data_arg == mat_half_data)
示例#3
0
    def test_diag(self, its=50):
        for it in range(its):
            size = random.choice([0, 1, randint(0, 10), randint(0, 100)])

            vec = randn(size)
            mat_bm = bm.diag(vec)
            assert isinstance(mat_bm, bm.BandMat)
            assert_allequal(mat_bm.full(), np.diag(vec))
            assert mat_bm.data.base is vec
            if size > 0:
                assert np.may_share_memory(mat_bm.data, vec)

            mat_bm = gen_BandMat(size)
            vec = bm.diag(mat_bm)
            assert_allequal(vec, np.diag(mat_bm.full()))
            assert vec.base is mat_bm.data
            if size > 0:
                assert np.may_share_memory(vec, mat_bm.data)
示例#4
0
    def test_diag(self, its=50):
        for it in range(its):
            size = random.choice([0, 1, randint(0, 10), randint(0, 100)])

            vec = randn(size)
            mat_bm = bm.diag(vec)
            assert isinstance(mat_bm, bm.BandMat)
            assert_allequal(mat_bm.full(), np.diag(vec))
            assert mat_bm.data.base is vec
            if size > 0:
                assert np.may_share_memory(mat_bm.data, vec)

            mat_bm = gen_BandMat(size)
            vec = bm.diag(mat_bm)
            assert_allequal(vec, np.diag(mat_bm.full()))
            assert vec.base is mat_bm.data
            if size > 0:
                assert np.may_share_memory(vec, mat_bm.data)
示例#5
0
    def test_solve(self, its=50):
        for it in range(its):
            size = random.choice([0, 1, randint(0, 10), randint(0, 100)])
            b = randn(size)
            # the below tries to ensure the matrix is well-conditioned
            a_bm = gen_BandMat(size) + bm.diag(np.ones((size,)) * 10.0)
            a_full = a_bm.full()

            x = bla.solve(a_bm, b)
            assert_allclose(bm.dot_mv(a_bm, x), b)
            if size == 0:
                x_good = np.zeros((size,))
            else:
                x_good = sla.solve(a_full, b)
            assert_allclose(x, x_good)
            assert not np.may_share_memory(x, a_bm.data)
            assert not np.may_share_memory(x, b)
示例#6
0
    def test_solve(self, its=50):
        for it in range(its):
            size = random.choice([0, 1, randint(0, 10), randint(0, 100)])
            b = randn(size)
            # the below tries to ensure the matrix is well-conditioned
            a_bm = gen_BandMat(size) + bm.diag(np.ones((size,)) * 10.0)
            a_full = a_bm.full()

            x = bla.solve(a_bm, b)
            assert_allclose(bm.dot_mv(a_bm, x), b)
            if size == 0:
                x_good = np.zeros((size,))
            else:
                x_good = sla.solve(a_full, b)
            assert_allclose(x, x_good)
            assert not np.may_share_memory(x, a_bm.data)
            assert not np.may_share_memory(x, b)
示例#7
0
def smooth(a, Phat, N, lambda_1, lambda_2):
    """
    a: (N,) number of measurements at that timestep
    Phat: (N, 3) sum of measurements at that timestep
    N: num time steps
    lambda_1, lambda_2: regularization parameters

    solves the optimization problem (over P \in R^{Tx3}):
    minimize ||diag(a)*P-Phat||^2 + lambda_1/N*||D_2*P||^2 + lambda_2/N*||D_3*P||^2

    returns:
        - P: (N, 3) matrix with full trajectory
    """
    # A in Banded Matrix form
    A = bm.diag(1. * a)

    # D_2 and D_3 in Banded Matrix form transposed
    D_2_bm_T = bm.BandMat(
        1, 1,
        np.hstack([
            np.zeros((3, 1)),
            np.repeat([[1.], [-2.], [1.]], N - 2, axis=1),
            np.zeros((3, 1))
        ]))
    D_3_bm_T = bm.BandMat(
        2, 2,
        np.hstack([
            np.zeros((5, 2)),
            np.repeat([[-1.], [2.], [0.], [-2.], [1.]], N - 4, axis=1),
            np.zeros((5, 2))
        ]))

    # XP=B normal equations
    X = bm.dot_mm(A, A) + lambda_1 / N * bm.dot_mm(
        D_2_bm_T, D_2_bm_T.T) + lambda_2 / N * bm.dot_mm(D_3_bm_T, D_3_bm_T.T)
    l_and_u = (X.l, X.u)  # lower and upper band bounds
    B = np.hstack([
        np.expand_dims(bm.dot_mv(A, Phat[:, 0]), -1),
        np.expand_dims(bm.dot_mv(A, Phat[:, 1]), -1),
        np.expand_dims(bm.dot_mv(A, Phat[:, 2]), -1)
    ])

    # solve normal equations
    P = solve_banded(l_and_u, X.data, B)

    return P
示例#8
0
def mlpg_grad(mean_frames, variance_frames, windows, grad_output):
    """MLPG gradient computation

    Parameters are same as :func:`nnmnkwii.paramgen.mlpg` except for
    ``grad_output``. See the function docmenent for what the parameters mean.

    Let :math:`d` is the index of static features, :math:`l` is the index
    of windows, gradients :math:`g_{d,l}` can be computed by:

    .. math::

        g_{d,l} = (\sum_{l} W_{l}^{T}P_{d,l}W_{l})^{-1} W_{l}^{T}P_{d,l}

    where :math:`W_{l}` is a banded window matrix and :math:`P_{d,l}` is a
    diagonal precision matrix.

    Assuming the variances are diagonals, MLPG can be performed in
    dimention-by-dimention efficiently.

    Let :math:`o_{d}` be ``T`` dimentional back-propagated gradients, the
    resulting gradients :math:`g'_{l,d}` to be propagated are
    computed as follows:

    .. math::

        g'_{d,l} = o_{d}^{T} g_{d,l}


    Args:
        mean_frames (numpy.ndarray): Means.
        variance_frames (numpy.ndarray): Variances.
        windows (list): Windows.
        grad_output: Backpropagated output gradient, shape (``T x static_dim``)

    Returns:
        numpy.ndarray: Gradients to be back propagated, shape: (``T x D``)

    See also:
        :func:`nnmnkwii.autograd.mlpg`, :class:`nnmnkwii.autograd.MLPG`
    """
    T, D = mean_frames.shape
    win_mats = build_win_mats(windows, T)
    static_dim = D // len(windows)

    max_win_width = np.max([max(win_mat.l, win_mat.u) for win_mat in win_mats])

    grads = np.zeros((T, D), dtype=np.float32)
    for d in range(static_dim):
        sdw = max([win_mat.l + win_mat.u for win_mat in win_mats])

        # R: \sum_{l} W_{l}^{T}P_{d,l}W_{l}
        R = bm.zeros(sdw, sdw, T)  # overwritten in the loop

        # dtype = np.float64 for bandmat
        precisions = np.zeros((len(windows), T), dtype=np.float64)

        for win_idx, win_mat in enumerate(win_mats):
            precisions[win_idx] = 1 / \
                variance_frames[:, win_idx * static_dim + d]

            # use zero precisions at edge frames for dynamic features
            if win_idx != 0:
                precisions[win_idx, :max_win_width] = 0
                precisions[win_idx, -max_win_width:] = 0

            bm.dot_mm_plus_equals(win_mat.T,
                                  win_mat,
                                  target_bm=R,
                                  diag=precisions[win_idx])

        for win_idx, win_mat in enumerate(win_mats):
            # r: W_{l}^{T}P_{d,l}
            r = bm.dot_mm(win_mat.T, bm.diag(precisions[win_idx]))

            # grad_{d, l} = R^{-1r}
            grad = solve_banded((R.l, R.u), R.data, r.full())
            assert grad.shape == (T, T)

            # Finally we get grad for a particular dimension
            grads[:, win_idx * static_dim + d] = grad_output[:, d].T.dot(grad)

    return grads
示例#9
0
def solve_banded_fast(y,
                      alpha,
                      band=1,
                      do_edges=True,
                      W=None,
                      rmse=0.1,
                      _D=None):
    """
    Speed up to actually solve the thing really fast
    using
        scipy.linalg.solve_banded(L.T, y)
    """
    # do some prep work
    nT = y.shape[0]
    q = np.zeros(nT)
    eidx = y > 0
    q[eidx] = 1
    Iobs = q
    # unc
    #Iobs /= C_obs[band]
    #y /= C_obs[band]
    nObs = eidx.sum()
    """
    *-- Alpha value --*
    This needs to be normalised by
    the number of observations
    OR DO I ??
    """
    """
    make D matrix
    in flattened form
    faster to pre-compute as is
    it's a standard length
    """
    #_D = None
    if _D == None:
        D = make_D_matrix(nT)
    else:
        D = _D
    """
    get a smooth solution
    """
    # weighting for outliers
    Wr = np.ones(nT)

    scale = (nObs)
    # re-scale alpha
    """
    re-scale alpha

    So trying something a little
    different...

    Basically allow this to vary across
    the time-series too based on the number of samples 
    in a local window. I think this should improve the
    scaling of alpha so we can keep edges where there
    are lots of samples....
    """
    #winN = np.ones(20)
    #nScale = np.convolve(eidx, winN, mode='same')
    #alpha = 0.5*alpha * nScale + 0.5*alpha * scale
    alpha = alpha * scale
    alpha = np.minimum(alpha, 200)
    alpha = np.maximum(alpha, 70)
    D2 = bm.dot_mm(D.T, D)
    dl = alpha * D2.data[2][:-1]
    di = alpha * D2.data[1] + Iobs * Wr
    du = alpha * D2.data[0][1:]
    ret = GTSV(dl, di, du, y, False, False, False, False)
    x0 = ret[-2]
    #import pdb; pdb.set_trace()
    """
    *-- now do edge preserving --*
    """
    cost = []
    tx = np.arange(len(y))[eidx]
    if do_edges:
        # Define some stuff
        converged = False
        convT = 1e-5
        itera = 0
        MAXITER = 10
        MINITER = 8
        T = 0.01  # threshold in the change needed...
        # Define the weights matrix
        _wg = np.ones(nT)
        W = bm.diag(_wg)
        # run until convergence
        x = x0
        R = []
        co0 = 1e3
        var_C = C_obs[band]
        sig_C = np.sqrt(var_C)
        obs = y[eidx]
        _w = np.ones(nT - 1)
        _w0 = np.ones(nT - 1) * 100
        while not converged and itera < MAXITER:
            """
            Robust smoothing...
            using the method from the garcia
            dct paper
            """
            r = x[eidx] - y[eidx]
            """
            h is the leverage from the
            hat matrix ege the inverse matrix
            """
            h = 0.0009
            """
            Robust estimate of the standard
            deviation of residuals
            """
            #sig_rob = 1.4826 * np.median(r - np.median(r))
            ui = r / (sig_C)
            #Wr[eidx] = (1-(ui/4.685)**2)**2
            # mask bad
            #bad = np.abs(ui/4.685)>2
            #Wr[eidx][bad]=0
            """
            *- edge preserving --*

            So this should now be fixed.
            The functional is

            w = T / sqrt( T**2 +  (dx * alpha**2)**2  )
            """
            """
            The derivative has to be scale by
            alpha
            """
            #dx = np.diff(x) * alpha**2
            #ww[1:] = T/(np.sqrt(T**2 +  dx**2))
            dx = np.diff(x) * alpha**2
            # only want drop in NIR
            dx[dx > 0] = 0
            """
            Thereshol T has to be scaled relative
                    to alpha (alpha actually works well)
            """
            T = 0.7 * alpha
            # calulcate the edge preserving functional w
            _w = T / np.sqrt(T**2 + dx**2)
            _w = np.exp(-dx**2 / T**2)
            """
            Scaling from CERC
            """
            wScale = nObs / np.sum(_w)
            _w = _w * wScale
            #_w = scipy.ndimage.minimum_filter(_w, 8)
            co1 = np.sqrt(np.sum((x0[eidx] - obs)**2 / var_C) / nObs)
            # faster version
            cost.append(co1)
            # remove nan
            if itera > 2:
                """
                Allow for some initial robust
                outlier rejection
                """
                np.place(_w, np.isnan(_w), 1)
                W.data[0, :-1] = _w
            else:
                _w[:] = 1
            """
            Remove edge effects
            -- force every outside
                in the edge windows back to 1
            """
            W.data[0, :5] = 1
            W.data[0, -5:] = 1
            """
            I cant figure out the flat formulat to do below
            """
            reg = alpha * bm.dot_mm(bm.dot_mm(D.T, W), D)
            dl = reg.data[2][:-1]
            di = reg.data[1] + Iobs * Wr
            du = reg.data[0][1:]
            ret = GTSV(dl, di, du, y, False, False, False, False)
            x = ret[-2]
            co = np.sum((x - x0)**2) / np.sum(x0**2) + 1e-6
            cow = np.sum((_w - _w0)**2) / np.sum(_w0**2) + 1e-6
            #if co < convT:
            #    converged=True
            if cow < 1e5:
                converged = True
            if np.abs(co1 - co0) < 0.01:
                converged = True
            if itera < MINITER:
                converged = False
            if _w.min() < 0.1:
                converged = True
            x0 = x
            _w0 = _w
            co0 = co1
            itera += 1
        """

        Do some checking

        So some times the algorithm freaks out
        and over does the edge preserving...

        Let's do some checks

        1. Want one strong area with a small w



        If w meets certain conditions optimise it a bit

        - Sparsify it..
            eg take the min and keep one min
                place the min at the end of its window
        """
        if np.any(_w < 0.3):
            """
            sufficent edge process
            to check whats going on
            """
            """
            Check 1.

            What percentage of timesteps are below
            a threhsold --> eg is w all over the place?
            and we've added to many edges
            """
            reset = False
            frac = (_w < 0.3).sum() / float(_w.shape[0])
            # want no more than 5%
            if frac > 0.1:
                # re-set edge process
                _w.fill(1)
                reset = True
            idx = np.nanargmin(_w)
            val = _w[idx]
            """
            Want to check a couple of things

            1. If min w aligns with an observation
                how far off the prediction are we?

                --> this helps remove cases where
                    we have shot noise from clouds
                    or cloud shadows
            """
            if eidx[idx]:  # have an observ
                if np.abs((y[idx] - x[idx])) / np.sqrt(C_obs[band]) > 2:
                    # it's a nonse break remove
                    _w.fill(1)
                    reset = True
            else:
                # check the surrounding pixels
                # to find some obs to check
                low = np.maximum(idx - 40, 0)
                upp = np.minimum(idx + 40, 363)
                mask = eidx[low:upp]
                yy = y[low:upp][mask]
                xx = x[low:upp][mask]
                _ww = _w[low:upp][mask]
                zz = np.abs((yy - xx)) / np.sqrt(C_obs[band])
                cond = np.logical_and(_ww < 0.3, zz > 2).sum()
                if cond > 4:
                    _w.fill(1)
                    reset = True
            """
            Resolve it...
            """
            if not reset:
                """
                So this is probably a step
                fix it up a bit
                """
                #_w[:]=1
                _w[idx] = 0.01
                pass
            W.data[0, :-1] = _w
            """
            I cant figure out the flat formulat to do below
            """
            reg = alpha * bm.dot_mm(bm.dot_mm(D.T, W), D)
            dl = reg.data[2][:-1]
            di = reg.data[1] + Iobs
            du = reg.data[0][1:]
            ret = GTSV(dl, di, du, y, False, False, False, False)
            x = ret[-2]
    else:
        # use the smooth solution
        x = x0
    if type(W) != None and do_edges == False:
        """
        Weights matrix supplied already...
        use this for edges...
        """
        # make W into a bandmat matrix
        W = bm.diag(W)
        reg = alpha * bm.dot_mm(bm.dot_mm(D.T, W), D)
        dl = reg.data[2][:-1]
        di = reg.data[1] + Iobs
        du = reg.data[0][1:]
        ret = GTSV(dl, di, du, y, False, False, False, False)
        x = ret[-2]
    """
    Add the MSE (eg obs unc estimate) to inflat unc...
    """
    C = GTSV(dl, di, du, np.eye(nT), False, False, False, False)[-2]
    unc = np.sqrt(C_obs[band] * np.diag(C))
    return x, unc, W.data.flatten(), D