def constrained_nmf(X, components):
    input_H = [
        torch.tensor(component[None, :], dtype=torch.float)
        for component in components
    ]
    nmf = NMF(
        X.shape,
        n_components=3,
        initial_components=input_H,
        fix_components=[True for _ in range(len(input_H))],
    )
    n_iter = nmf.fit(torch.tensor(X), beta=2)
    return nmf
def constrained_nmf(X):
    ideal_weights = np.array(
        list(zip(*[(x, 1 - x) for x in np.linspace(0, 1, X.shape[0])])))
    input_W = [
        torch.tensor(w[None, :], dtype=torch.float) for w in ideal_weights.T
    ]
    nmf = NMF(
        X.shape,
        n_components=2,
        initial_weights=input_W,
        fix_weights=[True for _ in range(len(input_W))],
    )
    n_iter = nmf.fit(torch.tensor(X), beta=2)
    return nmf
def partial_constrained_nmf(X,
                            components,
                            fix_components=(True for _ in range(3))):
    input_H = [
        torch.tensor(component[None, :], dtype=torch.float)
        if fix_components[i] else torch.rand(1, X.shape[-1])
        for i, component in enumerate(components)
    ]
    nmf = NMF(
        X.shape,
        n_components=3,
        initial_components=input_H,
        fix_components=fix_components,
    )
    _ = nmf.fit(torch.tensor(X), beta=2)
    return nmf
Example #4
0
def sweep_components(X, n_max=None, n_min=2):
    """
    Sweeps over all values of n_components and returns a plot of losses vs n_components
    Parameters
    ----------
    X : Tensor
    n_max : int
        Max n in search, default X.shape[0]
    n_min : int
        Min n in search, default 2

    Returns
    -------
    fig
    """
    from constrainednmf.nmf.models import NMF
    mpl.rcParams.update({"font.size": fontsize, "lines.linewidth": linewidth})

    if n_max is None:
        n_max = X.shape[0]

    losses = list()
    kl_losses = list()
    for n_components in range(n_min, n_max + 1):
        nmf = NMF(X.shape, n_components)
        nmf.fit(X, beta=2, tol=1e-8, max_iter=500)
        losses.append(nmf.loss(X, beta=2))
        kl_losses.append(nmf.loss(X, beta=1))

    fig = plt.figure(
        figsize=(one_column_width, one_column_width / 2), tight_layout=True
    )  # create a figure object
    ax = fig.add_subplot(1, 1, 1)
    axes = [ax, ax.twinx()]
    x = list(range(n_min, n_max + 1))
    axes[0].plot(x, losses, color="C0", label="MSE Loss")
    axes[0].set_ylabel("MSE Loss", color="C0")
    axes[1].plot(x, kl_losses, color="C1", label="KL Loss")
    axes[1].set_ylabel("KL Loss", color="C1")
    axes[1].ticklabel_format(style='sci', scilimits=(-2, 2))
    ax.set_xlabel("Number of components")
    return fig, axes
def standard_nmf(X, n_components=4):
    nmf = NMF(X.shape, n_components)
    n_iter = nmf.fit(X, beta=2, tol=1e-8, max_iter=1000)
    return nmf
Example #6
0
def decomposition(
    Q,
    I,
    n_components=3,
    q_range=None,
    initial_components=None,
    fix_components=(),
    mode="Linear",
    kernel_width=1,
    max_iter=1000,
    bkg_removal=None,
    normalize=False,
    **kwargs,
):
    """
    Decompose and label a set of I(Q) data with optional focus bounds. Can be used for other
    1-dimensional response functions, written with total scattering in mind.

    Two operating modes are available: Linear (conventional) and Deconvolutional. The former will proceed as conventional
    NMF as implemented in sklearn, with the added flexibility of the torch implementation. The latter will include a
    convolutional kernel in the reconstruction between the component and weight matricies.

    Initial components can be set as starting conditions of the component matrix for the optimization. These components
    can be fixed or allowed to vary using the `fix_components` argument as a tuple of booleans.

    Keyword arguments are passed to the fit method

    Parameters
    ----------
    Q : array
        Ordinate Q for I(Q). Assumed to be rank 2, shape (m_patterns, n_data)
    I : array
        The intensity values for each Q, assumed to be the same shape as Q. (m_patterns, n_data)
    n_components: int
        Number of components for NMF
    q_range : tuple, list
        (Min, Max) Q values for consideration in NMF. This enables a focused region for decomposition.
    initial_components: array
        Initial starting conditions of intensity components. Assumed to be shape (n_components, n_data).
        If q_range is given, these will be trimmed in accordance with I.
    fix_components: tuple(bool)
        Flags for fixing a subset of initial components
    mode: {"Linear", "Deconvolutional"}
        Operating mode
    kernel_width: int
        Width of 1-dimensional convolutional kernel
    max_iter: int
        Maximum number of iterations for NMF
    bkg_removal: int, optional
        Integer degree for peakutils background removal
    normalize: bool, optional
        Flag for min-max normalization
    **kwargs: dict
        Arguments passed to the fit method. See nmf.models.NMFBase.

    Returns
    -------
    sub_Q : array
        Subsampled ordinate used for NMF
    sub_I : array
        Subsampled I used for NMF
    alphas : array
        Resultant weights from NMF
    components:  array
        Resultant components from NMF

    """

    # Data subselection
    if q_range is None:
        idx_min = 0
        idx_max = I.shape[1]
    else:
        idx_min = (np.where(Q[0, :] < q_range[0])[0][-1] if len(
            np.where(Q[0, :] < q_range[0])[0]) else 0)
        idx_max = (np.where(Q[0, :] > q_range[1])[0][0] if len(
            np.where(Q[0, :] > q_range[1])[0]) else I.shape[1])

    sub_I = I[:, idx_min:idx_max]
    sub_Q = Q[:, idx_min:idx_max]

    # Data manipulation
    if bkg_removal:
        import peakutils

        bases = []
        for i in range(sub_I.shape[0]):
            bases.append(peakutils.baseline(sub_I[i, :], deg=bkg_removal))
        bases = np.stack(bases)
        sub_I = sub_I - bases
    if normalize:
        sub_I = (sub_I - np.min(sub_I, axis=1, keepdims=True)) / (
            np.max(sub_I, axis=1, keepdims=True) -
            np.min(sub_I, axis=1, keepdims=True))

    # Numerical stability of non-negativity
    if np.min(sub_I) < 0:
        sub_I = sub_I - np.min(sub_I, axis=1, keepdims=True)

    # Initial components
    if mode != "Deconvolutional":
        kernel_width = 1
    n_features = sub_I.shape[1]
    if initial_components is None:
        input_H = None
    else:
        input_H = []
        for i in range(n_components):
            try:
                sub_H = initial_components[i][idx_min:idx_max]
                sub_H = sub_H[kernel_width // 2:-kernel_width // 2 + 1]
                if normalize:
                    sub_H = (sub_H - np.min(sub_H)) / (np.max(sub_H) -
                                                       np.min(sub_H))
                input_H.append(
                    torch.tensor(sub_H, dtype=torch.float).reshape(
                        1, n_features - kernel_width + 1))
            except IndexError:
                input_H.append(torch.rand(1, n_features - kernel_width + 1))

    # Model construction
    if mode == "Linear":
        model = NMF(
            sub_I.shape,
            n_components,
            initial_components=input_H,
            fix_components=fix_components,
        )
    elif mode == "Deconvolutional":
        model = NMFD(
            sub_I.shape,
            n_components,
            T=kernel_width,
            initial_components=input_H,
            fix_components=fix_components,
        )
    else:
        raise NotImplementedError

    _, W = model.fit_transform(torch.Tensor(sub_I),
                               max_iter=max_iter,
                               **kwargs)

    if len(W.shape) > 2:
        alphas = torch.mean(W, 2).data.numpy()
    else:
        alphas = W.data.numpy()

    components = torch.cat([x for x in model.H_list]).data.numpy()
    return sub_Q, sub_I, alphas, components
def standard_nmf(X):
    nmf = NMF(X.shape, n_components=2)
    n_iter = nmf.fit(torch.tensor(X), beta=2)
    return nmf
Example #8
0
def standard_nmf(X):
    nmf = NMF(X.shape, 4)
    n_iter = nmf.fit(X, beta=2, tol=1e-8, max_iter=1000)
    return nmf