Mentions légales du service

Skip to content
Snippets Groups Projects
Commit d928fe84 authored by hhakim's avatar hhakim
Browse files

Enhance pyfaust.proj.* and pyfaust.factparams.Constraint*

No need to pass a matrix for certain ConstraintMat definitions (hankel, circ, toeplitz, id).
Ensure that default PALM4MSA constraint normalized/pos parameters are consistent with default Constraint*/projs parameters.
parent e5c977eb
Branches
Tags
No related merge requests found
......@@ -114,6 +114,222 @@ class StoppingCriterion(object):
"""
return self.__str__()
class ConstraintName:
"""
This class defines the names for the sub-types of constraints into the ConstraintGeneric hierarchy of classes.
The table <a href="constraint.png">here</a> is a summary of the available
constraints.
Attributes:
SP: Designates a constraint on the sparsity/0-norm of a matrix.
SPCOL: Designates a sparsity/0-norm constraint on the columns of a matrix.
SPLIN: Designates a sparsity/0-norm constraint on the rows of a matrix.
SPLINCOL: Designates a constraint that imposes both SPLIN and SPCOL constraints (see example above for clarification).
SP_POS: Designates a constraint that imposes a SP constraints and besides set to zero the negative coefficients (it doesn't apply to complex matrices).
NORMCOL: Designates a 2-norm constraint on each column of a matrix.
NORMLIN: Designates a 2-norm constraint on each row of a matrix.
CONST: Designates a constraint imposing to a matrix to be constant.
SUPP: Designates a constraint by a support matrix S (element-wisely multiplying the matrix to constrain to obtain a matrix for which the 2-norm equals 1, see: ConstraintMat.project()).
SKPERM: SKPERM prox/constraint.
ID: Identity prox/constraint.
name: The name of the constraint (actually an integer among the valid constants).
Example:
>>> # SPLINCOL Comprehensive Example
>>> # This constraint doesn't necessarily
>>> # lead to a image matrix with asked sparsity respected
>>> # both for columns and rows
>>> from numpy.random import rand
>>> from numpy.linalg import norm
>>> n = 10; m = 10; v = 2;
>>> M = rand(10,10)
>>> Mspcol = ConstraintInt('spcol', n, m, v).project(M)
>>> Msplin = ConstraintInt('splin', n, m, v).project(M)
>>> Mp = ConstraintInt('splincol', n, m, v).project(M)
>>> Mp_ = Mspcol + np.where(Mspcol != 0, 0, Msplin) # the sum of Mspcol and Msplin minus their non-zero matrix intersection
>>> Mp_/= norm(Mp_)
>>> # Mp is approximately equal to Mp_
>>> print(norm(Mp-Mp_,2)/norm(Mp_), 2)
0.00769281206496
>>> from numpy import count_nonzero
>>> count_nonzero(Mp[:,1])
3
>>> # sparsity value v is not respected
>>> count_nonzero(Mp_[:,1])
3
>>> count_nonzero(Mp_[1,:])
2
>>> count_nonzero(Mp[1,:])
2
>>> # v is respected for this row
"""
SP = 0 # Int Constraint
SPCOL = 1 # Int Constraint
SPLIN=2 # Int Constraint
NORMCOL = 3 # Real Constraint
SPLINCOL = 4 # Int Constraint
CONST = 5 # Mat Constraint
SP_POS = 6 # Int Constraint
BLKDIAG = 7 # Mat Constraint
SUPP = 8 # Mat Constraint
NORMLIN = 9 # Real Constraint
TOEPLITZ = 10 # Mat Constraint
CIRC = 11 # Mat constraint
HANKEL = 12 # Mat cons.
SKPERM = 13 # Int constraint
ID = 14 # Mat cons.
def __init__(self, name):
"""
Constructor of the ConstraintName object.
Args:
name: must be a valid constraint name (integer among the
static constants defined in the class: ConstraintName.SP, ...).
"""
if(isinstance(name,str)):
name = ConstraintName.str2name_int(name)
if(not isinstance(name, np.int) or not
ConstraintName._arg_is_int_const(name) \
and not ConstraintName._arg_is_real_const(name) \
and not ConstraintName._arg_is_mat_const(name)):
raise ValueError("name must be an integer among ConstraintName.SP,"
"ConstraintName.SPCOL, ConstraintName.NORMCOL,"
"ConstraintName.SPLINCOL, ConstraintName.CONST,"
"ConstraintName.SP_POS," # ConstraintName.BLKDIAG,
"ConstraintName.SUPP, ConstraintName.NORMLIN, "
"ConstraintName.TOEPLITZ, ConstraintName.CIRC")
self.name = name
@staticmethod
def _arg_is_int_const(name):
return name in [ConstraintName.SP, ConstraintName.SPCOL,
ConstraintName.SPLIN, ConstraintName.SPLINCOL,
ConstraintName.SP_POS, ConstraintName.SKPERM]
@staticmethod
def _arg_is_real_const(name):
return name in [ConstraintName.NORMCOL, ConstraintName.NORMLIN]
@staticmethod
def _arg_is_mat_const(name):
return name in [ConstraintName.SUPP, ConstraintName.CONST,
ConstraintName.CIRC, ConstraintName.TOEPLITZ,
ConstraintName.HANKEL, ConstraintName.BLKDIAG,
ConstraintName.ID]
def is_int_constraint(self):
"""
A delegate for ConstraintGeneric.is_int_constraint.
"""
return ConstraintName._arg_is_int_const(self.name)
def is_real_constraint(self):
"""
A delegate for ConstraintGeneric.is_real_constraint.
"""
return ConstraintName._arg_is_real_const(self.name)
def is_mat_constraint(self):
"""
A delegate for ConstraintGeneric.is_mat_constraint.
"""
return ConstraintName._arg_is_mat_const(self.name)
def name_str(self):
return ConstraintName.name_int2str(self.name)
@staticmethod
def name_int2str(_id):
"""
Converts a int constraint short name to its str constant name equivalent.
For example, name_int2str(ConstraintName.SP) returns 'sp'.
"""
err_msg = "Invalid argument to designate a ConstraintName."
if(not isinstance(_id, int)):
raise ValueError(err_msg)
if(_id == ConstraintName.SP):
_str = 'sp'
elif(_id == ConstraintName.SPLIN):
_str = 'splin'
elif(_id == ConstraintName.SPCOL):
_str = 'spcol'
elif(_id == ConstraintName.SPLINCOL):
_str = 'splincol'
elif(_id == ConstraintName.SP_POS):
_str = 'sppos'
elif(_id == ConstraintName.SKPERM):
_str = 'skperm'
elif(_id == ConstraintName.NORMCOL):
_str = 'normcol'
elif(_id == ConstraintName.NORMLIN):
_str = 'normlin'
elif(_id == ConstraintName.SUPP):
_str = 'supp'
elif(_id == ConstraintName.CONST):
_str = 'const'
elif(_id == ConstraintName.CIRC):
_str = 'circ'
elif(_id == ConstraintName.TOEPLITZ):
_str = 'toeplitz'
elif(_id == ConstraintName.HANKEL):
_str = 'hankel'
elif(_id == ConstraintName.BLKDIAG):
_str = 'blockdiag'
elif _id == ConstraintName.ID:
_str = 'id'
else:
raise ValueError(err_msg)
return _str
@staticmethod
def str2name_int(_str):
"""
Converts a str constraint short name to its integer constant name equivalent.
For example, str2name_int('sp') returns ConstraintName.SP.
"""
err_msg = "Invalid argument to designate a ConstraintName."
if(not isinstance(_str, str)):
raise ValueError(err_msg)
if(_str == 'sp'):
id = ConstraintName.SP
elif(_str == 'splin'):
id = ConstraintName.SPLIN
elif(_str == 'spcol'):
id = ConstraintName.SPCOL
elif(_str == 'splincol'):
id = ConstraintName.SPLINCOL
elif(_str == 'sppos'):
id = ConstraintName.SP_POS
elif(_str == 'skperm'):
id = ConstraintName.SKPERM
elif(_str == 'normcol'):
id = ConstraintName.NORMCOL
elif(_str == 'normlin'):
id = ConstraintName.NORMLIN
elif(_str == 'supp'):
id = ConstraintName.SUPP
elif(_str == 'const'):
id = ConstraintName.CONST
elif(_str == 'circ'):
id = ConstraintName.CIRC
elif(_str == 'toeplitz'):
id = ConstraintName.TOEPLITZ
elif(_str == 'hankel'):
id = ConstraintName.HANKEL
elif(_str == 'blockdiag'):
id = ConstraintName.BLKDIAG
elif(_str == 'id'):
id = ConstraintName.ID
else:
raise ValueError(err_msg)
return id
class ConstraintGeneric(ABC):
"""
This is the parent class for representing a factor constraint in FAuST factorization algorithms.
......@@ -296,6 +512,11 @@ class ConstraintMat(ConstraintGeneric):
This class represents a matrix-based constraint to apply on a matrix.
"""
normalized_default = {ConstraintName.ID: False, ConstraintName.TOEPLITZ: True, ConstraintName.CIRC: True,
ConstraintName.HANKEL: True, ConstraintName.SUPP: True, ConstraintName.CONST: False,
ConstraintName.BLKDIAG: True}
def __init__(self, name, cons_value=None, shape=None, normalized=None, pos=False, cons_value_sz=None):
"""
Constructs a matrix type constraint.
......@@ -305,10 +526,13 @@ class ConstraintMat(ConstraintGeneric):
ID, SUPP, CONST, TOEPLITZ or CIRC(ULANT) (cf. ConstraintName) or it can also be one of the
more handy str aliases which are respectively: 'supp' and 'const'.
cons_value: the value of the constraint, it must be a numpy.array
shape: the shape of the matrix (only useful for identity prox,
shape: (optional) the shape of the matrix (only useful for identity prox,
ConstraintName.ID. In this case the cons_value argument is None).
that defines the constraint (the matrix support for SUPP and the
constant matrix for CONST).
normalized: None because the default value depends on the
constraint name (it can be False or True, see
ConstraintMat.normalized_default).
Example:
>>> from pyfaust.factparams import ConstraintMat
......@@ -341,22 +565,28 @@ class ConstraintMat(ConstraintGeneric):
raise ValueError('either shape or cons_value must be defined')
super(ConstraintMat, self).__init__(name, _shape[0], _shape[1],
cons_value, normalized, pos)
if(not isinstance(cons_value, np.ndarray)):
if cons_value is not None and not isinstance(cons_value, np.ndarray):
raise TypeError('ConstraintMat must receive a numpy.ndarray as cons_value '
'argument.')
self.cons_value = np.asfortranarray(self._cons_value)
self._cons_value = self.cons_value
if(cons_value_sz == None):
self._cons_value_sz = self._num_cols*self._num_rows
if cons_value is not None:
self.cons_value = np.asfortranarray(self._cons_value)
self._cons_value = self.cons_value
if cons_value_sz is None:
self._cons_value_sz = self._num_cols*self._num_rows
else:
self._cons_value_sz = cons_value_sz
else:
self._cons_value_sz = cons_value_sz
if(normalized == None):
self.normalized = False
self._cons_value = None
self._cons_value_sz = 0
if normalized is None:
self.normalized = ConstraintMat.normalized_default[self._name.name]
if(not isinstance(self._name, ConstraintName) or not self._name.is_mat_constraint()):
raise TypeError('ConstraintMat first argument must be a '
'ConstraintName with a matrix type name '
'(name.is_mat_constraint() must return True)')
if self._name.name != ConstraintName.ID and cons_value is None:
no_mandatory_cons_value = [ConstraintName.ID, ConstraintName.TOEPLITZ,
ConstraintName.HANKEL, ConstraintName.CIRC]
if self._name.name not in no_mandatory_cons_value and cons_value is None:
raise ValueError('you must specify a matrix as cons_value except if'
' the ConstraintName is ID.')
if not shape is None and not cons_value is None \
......@@ -472,221 +702,6 @@ class ConstraintReal(ConstraintGeneric):
self.normalized, self.pos)
class ConstraintName:
"""
This class defines the names for the sub-types of constraints into the ConstraintGeneric hierarchy of classes.
The table <a href="constraint.png">here</a> is a summary of the available
constraints.
Attributes:
SP: Designates a constraint on the sparsity/0-norm of a matrix.
SPCOL: Designates a sparsity/0-norm constraint on the columns of a matrix.
SPLIN: Designates a sparsity/0-norm constraint on the rows of a matrix.
SPLINCOL: Designates a constraint that imposes both SPLIN and SPCOL constraints (see example above for clarification).
SP_POS: Designates a constraint that imposes a SP constraints and besides set to zero the negative coefficients (it doesn't apply to complex matrices).
NORMCOL: Designates a 2-norm constraint on each column of a matrix.
NORMLIN: Designates a 2-norm constraint on each row of a matrix.
CONST: Designates a constraint imposing to a matrix to be constant.
SUPP: Designates a constraint by a support matrix S (element-wisely multiplying the matrix to constrain to obtain a matrix for which the 2-norm equals 1, see: ConstraintMat.project()).
SKPERM: SKPERM prox/constraint.
ID: Identity prox/constraint.
name: The name of the constraint (actually an integer among the valid constants).
Example:
>>> # SPLINCOL Comprehensive Example
>>> # This constraint doesn't necessarily
>>> # lead to a image matrix with asked sparsity respected
>>> # both for columns and rows
>>> from numpy.random import rand
>>> from numpy.linalg import norm
>>> n = 10; m = 10; v = 2;
>>> M = rand(10,10)
>>> Mspcol = ConstraintInt('spcol', n, m, v).project(M)
>>> Msplin = ConstraintInt('splin', n, m, v).project(M)
>>> Mp = ConstraintInt('splincol', n, m, v).project(M)
>>> Mp_ = Mspcol + np.where(Mspcol != 0, 0, Msplin) # the sum of Mspcol and Msplin minus their non-zero matrix intersection
>>> Mp_/= norm(Mp_)
>>> # Mp is approximately equal to Mp_
>>> print(norm(Mp-Mp_,2)/norm(Mp_), 2)
0.00769281206496
>>> from numpy import count_nonzero
>>> count_nonzero(Mp[:,1])
3
>>> # sparsity value v is not respected
>>> count_nonzero(Mp_[:,1])
3
>>> count_nonzero(Mp_[1,:])
2
>>> count_nonzero(Mp[1,:])
2
>>> # v is respected for this row
"""
SP = 0 # Int Constraint
SPCOL = 1 # Int Constraint
SPLIN=2 # Int Constraint
NORMCOL = 3 # Real Constraint
SPLINCOL = 4 # Int Constraint
CONST = 5 # Mat Constraint
SP_POS = 6 # Int Constraint
BLKDIAG = 7 # Mat Constraint
SUPP = 8 # Mat Constraint
NORMLIN = 9 # Real Constraint
TOEPLITZ = 10 # Mat Constraint
CIRC = 11 # Mat constraint
HANKEL = 12 # Mat cons.
SKPERM = 13 # Int constraint
ID = 14 # Mat cons.
def __init__(self, name):
"""
Constructor of the ConstraintName object.
Args:
name: must be a valid constraint name (integer among the
static constants defined in the class: ConstraintName.SP, ...).
"""
if(isinstance(name,str)):
name = ConstraintName.str2name_int(name)
if(not isinstance(name, np.int) or not
ConstraintName._arg_is_int_const(name) \
and not ConstraintName._arg_is_real_const(name) \
and not ConstraintName._arg_is_mat_const(name)):
raise ValueError("name must be an integer among ConstraintName.SP,"
"ConstraintName.SPCOL, ConstraintName.NORMCOL,"
"ConstraintName.SPLINCOL, ConstraintName.CONST,"
"ConstraintName.SP_POS," # ConstraintName.BLKDIAG,
"ConstraintName.SUPP, ConstraintName.NORMLIN, "
"ConstraintName.TOEPLITZ, ConstraintName.CIRC")
self.name = name
@staticmethod
def _arg_is_int_const(name):
return name in [ConstraintName.SP, ConstraintName.SPCOL,
ConstraintName.SPLIN, ConstraintName.SPLINCOL,
ConstraintName.SP_POS, ConstraintName.SKPERM]
@staticmethod
def _arg_is_real_const(name):
return name in [ConstraintName.NORMCOL, ConstraintName.NORMLIN]
@staticmethod
def _arg_is_mat_const(name):
return name in [ConstraintName.SUPP, ConstraintName.CONST,
ConstraintName.CIRC, ConstraintName.TOEPLITZ,
ConstraintName.HANKEL, ConstraintName.BLKDIAG,
ConstraintName.ID]
def is_int_constraint(self):
"""
A delegate for ConstraintGeneric.is_int_constraint.
"""
return ConstraintName._arg_is_int_const(self.name)
def is_real_constraint(self):
"""
A delegate for ConstraintGeneric.is_real_constraint.
"""
return ConstraintName._arg_is_real_const(self.name)
def is_mat_constraint(self):
"""
A delegate for ConstraintGeneric.is_mat_constraint.
"""
return ConstraintName._arg_is_mat_const(self.name)
def name_str(self):
return ConstraintName.name_int2str(self.name)
@staticmethod
def name_int2str(_id):
"""
Converts a int constraint short name to its str constant name equivalent.
For example, name_int2str(ConstraintName.SP) returns 'sp'.
"""
err_msg = "Invalid argument to designate a ConstraintName."
if(not isinstance(_id, int)):
raise ValueError(err_msg)
if(_id == ConstraintName.SP):
_str = 'sp'
elif(_id == ConstraintName.SPLIN):
_str = 'splin'
elif(_id == ConstraintName.SPCOL):
_str = 'spcol'
elif(_id == ConstraintName.SPLINCOL):
_str = 'splincol'
elif(_id == ConstraintName.SP_POS):
_str = 'sppos'
elif(_id == ConstraintName.SKPERM):
_str = 'skperm'
elif(_id == ConstraintName.NORMCOL):
_str = 'normcol'
elif(_id == ConstraintName.NORMLIN):
_str = 'normlin'
elif(_id == ConstraintName.SUPP):
_str = 'supp'
elif(_id == ConstraintName.CONST):
_str = 'const'
elif(_id == ConstraintName.CIRC):
_str = 'circ'
elif(_id == ConstraintName.TOEPLITZ):
_str = 'toeplitz'
elif(_id == ConstraintName.HANKEL):
_str = 'hankel'
elif(_id == ConstraintName.BLKDIAG):
_str = 'blockdiag'
elif _id == ConstraintName.ID:
_str = 'id'
else:
raise ValueError(err_msg)
return _str
@staticmethod
def str2name_int(_str):
"""
Converts a str constraint short name to its integer constant name equivalent.
For example, str2name_int('sp') returns ConstraintName.SP.
"""
err_msg = "Invalid argument to designate a ConstraintName."
if(not isinstance(_str, str)):
raise ValueError(err_msg)
if(_str == 'sp'):
id = ConstraintName.SP
elif(_str == 'splin'):
id = ConstraintName.SPLIN
elif(_str == 'spcol'):
id = ConstraintName.SPCOL
elif(_str == 'splincol'):
id = ConstraintName.SPLINCOL
elif(_str == 'sppos'):
id = ConstraintName.SP_POS
elif(_str == 'skperm'):
id = ConstraintName.SKPERM
elif(_str == 'normcol'):
id = ConstraintName.NORMCOL
elif(_str == 'normlin'):
id = ConstraintName.NORMLIN
elif(_str == 'supp'):
id = ConstraintName.SUPP
elif(_str == 'const'):
id = ConstraintName.CONST
elif(_str == 'circ'):
id = ConstraintName.CIRC
elif(_str == 'toeplitz'):
id = ConstraintName.TOEPLITZ
elif(_str == 'hankel'):
id = ConstraintName.HANKEL
elif(_str == 'blockdiag'):
id = ConstraintName.BLKDIAG
elif(_str == 'id'):
id = ConstraintName.ID
else:
raise ValueError(err_msg)
return id
class ConstraintList(object):
"""
A helper class for constructing a list of consistent pyfaust.proj.proj_gen projectors or ConstraintGeneric objects.
......
......@@ -40,7 +40,7 @@ class proj_id(proj_gen):
def __init__(self, shape):
super(proj_id, self).__init__(shape)
self.constraint = ConstraintMat('id', cons_value=np.empty(shape))
self.constraint = ConstraintMat('id', shape=shape)
self.constraint._num_rows = shape[0]
self.constraint._num_cols = shape[1]
......@@ -61,7 +61,7 @@ class toeplitz(proj_gen):
[ 0.35021359, 0.65125443, 0.29650327, 0.62199717, 0.38925701]])
"""
def __init__(self, shape, normalized=False, pos=False):
def __init__(self, shape, normalized=True, pos=False):
"""
Args:
shape: the size of the input matrix.
......@@ -69,7 +69,7 @@ class toeplitz(proj_gen):
pos: True to skip negative values (replaced by zero) of the matrix to project.
"""
super(toeplitz, self).__init__(shape)
self.constraint = ConstraintMat('toeplitz', cons_valuenp.empty(shape),
self.constraint = ConstraintMat('toeplitz', shape=shape,
normalized=normalized, pos=pos)
class circ(proj_gen):
......@@ -101,7 +101,7 @@ class circ(proj_gen):
"""
super(circ, self).__init__(shape)
self.constraint = ConstraintMat('circ', cons_value=np.empty(shape),
self.constraint = ConstraintMat('circ', shape=shape,
normalized=normalized, pos=pos)
class hankel(proj_gen):
......@@ -129,7 +129,7 @@ class hankel(proj_gen):
pos: True to skip negative values (replaced by zero) of the matrix to project.
"""
super(hankel, self).__init__(shape)
self.constraint = ConstraintMat('hankel', cons_value=np.empty(shape),
self.constraint = ConstraintMat('hankel', shape=shape,
normalized=normalized, pos=pos)
......@@ -330,7 +330,7 @@ class supp(proj_gen):
normalized: True to normalize the projection image according to its Frobenius norm.
pos: True to skip negative values (replaced by zero) of the matrix to project.
"""
super(supp, self).__init__(shape)
super(supp, self).__init__(S.shape)
self.constraint = ConstraintMat('supp', cons_value=S,
normalized=normalized, pos=pos)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment