# Source code for graspologic.align.sign_flips

# Copyright (c) Microsoft Corporation and contributors.

import numpy as np

from .base import BaseAlign

[docs]class SignFlips(BaseAlign):
"""
Flips the signs of all entries in one dataset, X along some of the
dimensions. In particular, it does so in a way that brings this dataset to
the same orthant as the second dataset, Y, according to some criterion,
computed along each dimension. The two critera currently available are the
median and the maximum (in magnitude) value along each dimension.

This module can also be used to bring the dataset to the first orthant
(i.e. with all criteras being positive) by providing the identity matrix as
the second dataset.

Parameters
----------
criterion : string, {'median' (default), 'max'}, optional
String describing the criterion used to choose whether to flip signs.
Two options are currently supported:

- 'median'
Uses the median along each dimension
- 'max'
Uses the maximum (in magintude) along each dimension

Attributes
----------
Q_ : array, size (d, d)
Final orthogonal matrix, used to modify X.

"""

def __init__(
self,
criterion="median",
):
# checking criterion argument
if type(criterion) is not str:
raise TypeError("Criterion must be str")
if criterion not in ["median", "max"]:
raise ValueError(f"{criterion} is not a valid criterion.")

super().__init__()

self.criterion = criterion

def set_criterion_function(self):
# perform a check, in case it was modified directly
if self.criterion not in ["median", "max"]:
raise ValueError(f"{self.criterion} is not a valid criterion")

if self.criterion == "median":

def median_criterion(X):
return np.median(X, axis=0)

self.criterion_function_ = median_criterion
if self.criterion == "max":

def max_criterion(X):
return X[np.argmax(np.abs(X), axis=0), np.arange(X.shape[1])]

self.criterion_function_ = max_criterion

[docs]    def fit(self, X, Y):
"""
Uses the two datasets to learn the matrix :attr:~graspologic.align.SignFlips.Q_ that aligns the
first dataset with the second.

In sign flips, :attr:~graspologic.align.SignFlips.Q_ is an diagonal orthogonal matrices (i.e. a
matrix with 1 or -1 in each entry on diagonal and 0 everywhere else)
picked such that all dimensions of X @ :attr:~graspologic.align.SignFlips.Q_
and Y are in the same orthant using some critera (median or max magnitude).

Parameters
----------
X : np.ndarray, shape (n, d)
Dataset to be mapped to Y, must have same number of dimensions
(axis 1) as Y.

Y : np.ndarray, shape (m, d)
Target dataset, must have same number of dimensions (axis 1) as X.

Returns
-------
self : returns an instance of self

"""
X, Y = self._check_datasets(X, Y)
_, d = X.shape

self.set_criterion_function()
X_criterias = self.criterion_function_(X)
Y_criterias = self.criterion_function_(Y)

val = np.multiply(X_criterias, Y_criterias)
t_X = (val >= 0) * 2 - 1

self.Q_ = np.diag(t_X)
return self