Skip to content

Commit

Permalink
gamma -> theta
Browse files Browse the repository at this point in the history
  • Loading branch information
dburkhardt committed Sep 6, 2018
1 parent c1e84f4 commit 74897db
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 114 deletions.
10 changes: 5 additions & 5 deletions graphtools/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def Graph(data,
distance='euclidean',
thresh=1e-4,
kernel_symm='+',
gamma=None,
theta=None,
n_landmark=None,
n_svd=100,
beta=1,
Expand Down Expand Up @@ -75,12 +75,12 @@ def Graph(data,
Defines method of MNN symmetrization.
'+' : additive
'*' : multiplicative
'gamma' : min-max
'theta' : min-max
'none' : no symmetrization
gamma: float (default: None)
Min-max symmetrization constant or matrix. Only used if kernel_symm='gamma'.
K = `gamma * min(K, K.T) + (1 - gamma) * max(K, K.T)`
theta: float (default: None)
Min-max symmetrization constant or matrix. Only used if kernel_symm='theta'.
K = `theta * min(K, K.T) + (1 - theta) * max(K, K.T)`
precomputed : {'distance', 'affinity', 'adjacency', `None`}, optional (default: `None`)
If the graph is precomputed, this variable denotes which graph
Expand Down
67 changes: 33 additions & 34 deletions graphtools/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
elementwise_maximum,
set_diagonal)


class Base(object):
"""Class that deals with key-word arguments but is otherwise
just an object.
Expand Down Expand Up @@ -311,12 +310,12 @@ class BaseGraph(with_metaclass(abc.ABCMeta, Base)):
Defines method of MNN symmetrization.
'+' : additive
'*' : multiplicative
'gamma' : min-max
'theta' : min-max
'none' : no symmetrization
gamma: float (default: 0.5)
theta: float (default: 0.5)
Min-max symmetrization constant.
K = `gamma * min(K, K.T) + (1 - gamma) * max(K, K.T)`
K = `theta * min(K, K.T) + (1 - theta) * max(K, K.T)`
initialize : `bool`, optional (default : `True`)
if false, don't create the kernel matrix.
Expand All @@ -337,11 +336,11 @@ class BaseGraph(with_metaclass(abc.ABCMeta, Base)):
"""

def __init__(self, kernel_symm='+',
gamma=None,
theta=None,
initialize=True, **kwargs):
self.kernel_symm = kernel_symm
self.gamma = gamma
self._check_symmetrization(kernel_symm, gamma)
self.theta = theta
self._check_symmetrization(kernel_symm, theta)

if initialize:
tasklogger.log_debug("Initializing kernel...")
Expand All @@ -350,25 +349,25 @@ def __init__(self, kernel_symm='+',
tasklogger.log_debug("Not initializing kernel.")
super().__init__(**kwargs)

def _check_symmetrization(self, kernel_symm, gamma):
if kernel_symm not in ['+', '*', 'gamma', None]:
def _check_symmetrization(self, kernel_symm, theta):
if kernel_symm not in ['+', '*', 'theta', None]:
raise ValueError(
"kernel_symm '{}' not recognized. Choose from "
"'+', '*', 'gamma', or 'none'.".format(kernel_symm))
elif kernel_symm != 'gamma' and gamma is not None:
warnings.warn("kernel_symm='{}' but gamma is not None. "
"Setting kernel_symm='gamma'.".format(kernel_symm))
self.kernel_symm = kernel_symm = 'gamma'

if kernel_symm == 'gamma':
if gamma is None:
warnings.warn("kernel_symm='gamma' but gamma not given. "
"Defaulting to gamma=0.5.")
self.gamma = gamma = 0.5
elif not isinstance(gamma, numbers.Number) or \
gamma < 0 or gamma > 1:
raise ValueError("gamma {} not recognized. Expected "
"a float between 0 and 1".format(gamma))
"'+', '*', 'theta', or 'none'.".format(kernel_symm))
elif kernel_symm != 'theta' and theta is not None:
warnings.warn("kernel_symm='{}' but theta is not None. "
"Setting kernel_symm='theta'.".format(kernel_symm))
self.kernel_symm = kernel_symm = 'theta'

if kernel_symm == 'theta':
if theta is None:
warnings.warn("kernel_symm='theta' but theta not given. "
"Defaulting to theta=0.5.")
self.theta = theta = 0.5
elif not isinstance(theta, numbers.Number) or \
theta < 0 or theta > 1:
raise ValueError("theta {} not recognized. Expected "
"a float between 0 and 1".format(theta))

def _build_kernel(self):
"""Private method to build kernel matrix
Expand Down Expand Up @@ -400,26 +399,26 @@ def symmetrize_kernel(self, K):
elif self.kernel_symm == "*":
tasklogger.log_debug("Using multiplication symmetrization.")
K = K.multiply(K.T)
elif self.kernel_symm == 'gamma':
elif self.kernel_symm == 'theta':
tasklogger.log_debug(
"Using gamma symmetrization (gamma = {}).".format(self.gamma))
K = self.gamma * elementwise_minimum(K, K.T) + \
(1 - self.gamma) * elementwise_maximum(K, K.T)
"Using theta symmetrization (theta = {}).".format(self.theta))
K = self.theta * elementwise_minimum(K, K.T) + \
(1 - self.theta) * elementwise_maximum(K, K.T)
elif self.kernel_symm is None:
tasklogger.log_debug("Using no symmetrization.")
pass
else:
# this should never happen
raise ValueError(
"Expected kernel_symm in ['+', '*', 'gamma' or None]. "
"Got {}".format(self.gamma))
"Expected kernel_symm in ['+', '*', 'theta' or None]. "
"Got {}".format(self.theta))
return K

def get_params(self):
"""Get parameters from this object
"""
return {'kernel_symm': self.kernel_symm,
'gamma': self.gamma}
'theta': self.theta}

def set_params(self, **params):
"""Set parameters on this object
Expand All @@ -429,7 +428,7 @@ def set_params(self, **params):
Valid parameters:
Invalid parameters: (these would require modifying the kernel matrix)
- kernel_symm
- gamma
- theta
Parameters
----------
Expand All @@ -439,8 +438,8 @@ def set_params(self, **params):
-------
self
"""
if 'gamma' in params and params['gamma'] != self.gamma:
raise ValueError("Cannot update gamma. Please create a new graph")
if 'theta' in params and params['theta'] != self.theta:
raise ValueError("Cannot update theta. Please create a new graph")
if 'kernel_symm' in params and \
params['kernel_symm'] != self.kernel_symm:
raise ValueError(
Expand Down
80 changes: 40 additions & 40 deletions graphtools/graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -886,33 +886,33 @@ def __init__(self, data, sample_idx,

super().__init__(data, n_pca=n_pca, **kwargs)

def _check_symmetrization(self, kernel_symm, gamma):
if kernel_symm == 'gamma' and gamma is not None and \
not isinstance(gamma, numbers.Number):
# matrix gamma
def _check_symmetrization(self, kernel_symm, theta):
if kernel_symm == 'theta' and theta is not None and \
not isinstance(theta, numbers.Number):
# matrix theta
try:
gamma.shape
theta.shape
except AttributeError:
raise ValueError("gamma {} not recognized. "
raise ValueError("theta {} not recognized. "
"Expected a float between 0 and 1 "
"or a [n_batch,n_batch] matrix of "
"floats between 0 and 1".format(gamma))
if not np.shape(gamma) == (len(self.samples),
"floats between 0 and 1".format(theta))
if not np.shape(theta) == (len(self.samples),
len(self.samples)):
raise ValueError(
"Matrix gamma must be of shape "
"Matrix theta must be of shape "
"({}), got ({})".format(
(len(self.samples),
len(self.samples)), gamma.shape))
elif np.max(gamma) > 1 or np.min(gamma) < 0:
len(self.samples)), theta.shape))
elif np.max(theta) > 1 or np.min(theta) < 0:
raise ValueError(
"Values in matrix gamma must be between"
"Values in matrix theta must be between"
" 0 and 1, got values between {} and {}".format(
np.max(gamma), np.min(gamma)))
elif np.any(gamma != gamma.T):
raise ValueError("gamma must be a symmetric matrix")
np.max(theta), np.min(theta)))
elif np.any(theta != theta.T):
raise ValueError("theta must be a symmetric matrix")
else:
super()._check_symmetrization(kernel_symm, gamma)
super()._check_symmetrization(kernel_symm, theta)

def _weight_knn(self, sample_size=None):
"""Select adaptive values of knn
Expand Down Expand Up @@ -1070,14 +1070,14 @@ def build_kernel(self):
return K

def symmetrize_kernel(self, K):
if self.kernel_symm == 'gamma' and self.gamma is not None and \
not isinstance(self.gamma, numbers.Number):
# matrix gamma
if self.kernel_symm == 'theta' and self.theta is not None and \
not isinstance(self.theta, numbers.Number):
# matrix theta
# Gamma can be a matrix with specific values transitions for
# each batch. This allows for technical replicates and
# experimental samples to be corrected simultaneously
tasklogger.log_debug("Using gamma symmetrization. "
"Gamma:\n{}".format(self.gamma))
tasklogger.log_debug("Using theta symmetrization. "
"Gamma:\n{}".format(self.theta))
for i, sample_i in enumerate(self.samples):
for j, sample_j in enumerate(self.samples):
if j < i:
Expand All @@ -1086,9 +1086,9 @@ def symmetrize_kernel(self, K):
self.sample_idx == sample_j)]
Kji = K[np.ix_(self.sample_idx == sample_j,
self.sample_idx == sample_i)]
Kij_symm = self.gamma[i, j] * \
Kij_symm = self.theta[i, j] * \
elementwise_minimum(Kij, Kji.T) + \
(1 - self.gamma[i, j]) * \
(1 - self.theta[i, j]) * \
elementwise_maximum(Kij, Kji.T)
K = set_submatrix(K, self.sample_idx == sample_i,
self.sample_idx == sample_j, Kij_symm)
Expand All @@ -1100,7 +1100,7 @@ def symmetrize_kernel(self, K):
K = super().symmetrize_kernel(K)
return K

def build_kernel_to_data(self, Y, gamma=None):
def build_kernel_to_data(self, Y, theta=None):
"""Build transition matrix from new data to the graph
Creates a transition matrix such that `Y` can be approximated by
Expand All @@ -1120,8 +1120,8 @@ def build_kernel_to_data(self, Y, gamma=None):
to the existing data. `n_features` must match
either the ambient or PCA dimensions
gamma : array-like or `None`, optional (default: `None`)
if `self.gamma` is a matrix, gamma values must be explicitly
theta : array-like or `None`, optional (default: `None`)
if `self.theta` is a matrix, theta values must be explicitly
specified between `Y` and each sample in `self.data`
Returns
Expand All @@ -1131,15 +1131,15 @@ def build_kernel_to_data(self, Y, gamma=None):
Transition matrix from `Y` to `self.data`
"""
raise NotImplementedError
tasklogger.log_warning("building MNN kernel to gamma is experimental")
if not isinstance(self.gamma, str) and \
not isinstance(self.gamma, numbers.Number):
if gamma is None:
tasklogger.log_warning("building MNN kernel to theta is experimental")
if not isinstance(self.theta, str) and \
not isinstance(self.theta, numbers.Number):
if theta is None:
raise ValueError(
"self.gamma is a matrix but gamma is not provided.")
elif len(gamma) != len(self.samples):
"self.theta is a matrix but theta is not provided.")
elif len(theta) != len(self.samples):
raise ValueError(
"gamma should have one value for every sample")
"theta should have one value for every sample")

Y = self._check_extension_shape(Y)
kernel_xy = []
Expand All @@ -1156,26 +1156,26 @@ def build_kernel_to_data(self, Y, gamma=None):
kernel_yx = sparse.vstack(kernel_yx) # n_cells_x x n_cells_y

# symmetrize
if gamma is not None:
if theta is not None:
# Gamma can be a vector with specific values transitions for
# each batch. This allows for technical replicates and
# experimental samples to be corrected simultaneously
K = np.empty_like(kernel_xy)
for i, sample in enumerate(self.samples):
sample_idx = self.sample_idx == sample
K[:, sample_idx] = gamma[i] * \
K[:, sample_idx] = theta[i] * \
kernel_xy[:, sample_idx].minimum(
kernel_yx[sample_idx, :].T) + \
(1 - gamma[i]) * \
(1 - theta[i]) * \
kernel_xy[:, sample_idx].maximum(
kernel_yx[sample_idx, :].T)
if self.gamma == "+":
if self.theta == "+":
K = (kernel_xy + kernel_yx.T) / 2
elif self.gamma == "*":
elif self.theta == "*":
K = kernel_xy.multiply(kernel_yx.T)
else:
K = self.gamma * kernel_xy.minimum(kernel_yx.T) + \
(1 - self.gamma) * kernel_xy.maximum(kernel_yx.T)
K = self.theta * kernel_xy.minimum(kernel_yx.T) + \
(1 - self.theta) * kernel_xy.maximum(kernel_yx.T)
return K


Expand Down
2 changes: 1 addition & 1 deletion test/test_exact.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def test_set_params():
assert G.get_params() == {'n_pca': 20,
'random_state': 42,
'kernel_symm': '+',
'gamma': None,
'theta': None,
'knn': 3,
'decay': 10,
'distance': 'euclidean',
Expand Down
6 changes: 3 additions & 3 deletions test/test_knn.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def test_set_params():
'n_pca': 20,
'random_state': 42,
'kernel_symm': '+',
'gamma': None,
'theta': None,
'knn': 3,
'decay': None,
'distance': 'euclidean',
Expand All @@ -204,11 +204,11 @@ def test_set_params():
assert_raises(ValueError, G.set_params, decay=10)
assert_raises(ValueError, G.set_params, distance='manhattan')
assert_raises(ValueError, G.set_params, thresh=1e-3)
assert_raises(ValueError, G.set_params, gamma=0.99)
assert_raises(ValueError, G.set_params, theta=0.99)
assert_raises(ValueError, G.set_params, kernel_symm='*')
G.set_params(knn=G.knn,
decay=G.decay,
thresh=G.thresh,
distance=G.distance,
gamma=G.gamma,
theta=G.theta,
kernel_symm=G.kernel_symm)
2 changes: 1 addition & 1 deletion test/test_landmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_set_params():
assert G.get_params() == {'n_pca': 20,
'random_state': 42,
'kernel_symm': '+',
'gamma': None,
'theta': None,
'n_landmark': 500,
'knn': 3,
'decay': None,
Expand Down

0 comments on commit 74897db

Please sign in to comment.