Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP real-valued functional #1333

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions odl/operator/default_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class LinCombOperator(Operator):
LinCombOperator(a, b)([x, y]) == a * x + b * y
"""

def __init__(self, space, a, b):
def __init__(self, space, a, b, rangeType=None):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that wasn't meant to be pushed... I used it for a mapping-oparator from R2 to C...

"""Initialize a new instance.

Parameters
Expand All @@ -204,8 +204,11 @@ def __init__(self, space, a, b):
>>> z
rn(3).element([ 2., 4., 6.])
"""
if rangeType is None:
rangeType = space.dtype
domain = ProductSpace(space, space)
super(LinCombOperator, self).__init__(domain, space, linear=True)
super(LinCombOperator, self).__init__(domain, space.astype(rangeType),
linear=True)
self.a = a
self.b = b

Expand Down
90 changes: 43 additions & 47 deletions odl/solvers/functional/default_functionals.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def __init__(self, domain, exponent, range=None):
range = RealNumbers()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

def __init__(self, domain, exponent, range=RealNumbers()):
    ...

? I feel that the current variant is unnecessarily indirect. None is usually a proxy for some_value_that_depends_on_other_stuff_or_must_be_computed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only works if we want RealNumbers to be forever immutable. Are we sure about that? (Id be fine with it)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we can make that clearer by adding __slots__ to RealNumbers and ComplexNumbers.


super(LpNorm, self).__init__(
domain=domain, linear=False, grad_lipschitz=np.nan, range=range)
domain=domain, range=range, linear=False, grad_lipschitz=np.nan)
self.exponent = float(exponent)

# TODO: update when integration operator is in place: issue #440
Expand Down Expand Up @@ -141,7 +141,8 @@ class L1Gradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(L1Gradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain,
linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point."""
Expand All @@ -161,7 +162,8 @@ class L2Gradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(L2Gradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain,
linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point.
Expand Down Expand Up @@ -251,7 +253,7 @@ def __init__(self, vfspace, exponent=None, range=None):
raise TypeError('`space.is_power_space` must be `True`')

super(GroupL1Norm, self).__init__(
domain=vfspace, linear=False, grad_lipschitz=np.nan, range=range)
domain=vfspace, range=range, linear=False, grad_lipschitz=np.nan)
self.pointwise_norm = PointwiseNorm(vfspace, exponent)

def _call(self, x):
Expand Down Expand Up @@ -294,7 +296,7 @@ class GroupL1Gradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(GroupL1Gradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain, linear=False)

def _call(self, x, out):
"""Return ``self(x)``."""
Expand Down Expand Up @@ -382,7 +384,7 @@ def __init__(self, vfspace, exponent=None, range=None):
raise TypeError('`space.is_power_space` must be `True`')

super(IndicatorGroupL1UnitBall, self).__init__(
domain=vfspace, linear=False, grad_lipschitz=np.nan, range=range)
domain=vfspace, range=range, linear=False, grad_lipschitz=np.nan)
self.pointwise_norm = PointwiseNorm(vfspace, exponent)

def _call(self, x):
Expand Down Expand Up @@ -474,8 +476,8 @@ def __init__(self, domain, exponent, range=None):
range : `Field`, optional
Range of the functional. Default: ``domain.field``.
"""
super(IndicatorLpUnitBall, self).__init__(domain=domain, linear=False,
range=range)
super(IndicatorLpUnitBall, self).__init__(domain=domain, range=range,
linear=False)

self.__norm = LpNorm(domain, exponent, range=RealNumbers())
self.__exponent = float(exponent)
Expand Down Expand Up @@ -585,7 +587,7 @@ def __init__(self, domain, range=None):
range : `Field`, optional
Range of the functional. Default: `RealNumbers`.
"""
super(L1Norm, self).__init__(domain=domain, exponent=1, range=range)
super(L1Norm, self).__init__(domain=domain, range=range, exponent=1)

def __repr__(self):
"""Return ``repr(self)``."""
Expand Down Expand Up @@ -624,7 +626,7 @@ def __init__(self, domain, range=None):
range : `Field`, optional
Range of the functional. Default: `RealNumbers`.
"""
super(L2Norm, self).__init__(domain=domain, exponent=2, range=range)
super(L2Norm, self).__init__(domain=domain, range=range, exponent=2)

def __repr__(self):
"""Return ``repr(self)``."""
Expand Down Expand Up @@ -672,7 +674,7 @@ def __init__(self, domain, range=None):
Range of the functional. Default: `RealNumbers`.
"""
super(L2NormSquared, self).__init__(
domain=domain, linear=False, grad_lipschitz=2, range=range)
domain=domain, range=range, linear=False, grad_lipschitz=2)

# TODO: update when integration operator is in place: issue #440
def _call(self, x):
Expand Down Expand Up @@ -731,8 +733,8 @@ def __init__(self, domain, constant, range=None):
Range of the functional. Default: ``domain.field``.
"""
super(ConstantFunctional, self).__init__(
domain=domain, linear=(constant == 0), grad_lipschitz=0,
range=range)
domain=domain, range=range, linear=(constant == 0),
grad_lipschitz=0)
self.__constant = self.range.element(constant)

@property
Expand Down Expand Up @@ -790,8 +792,8 @@ def __init__(self, domain, range=None):
range : `Field`, optional
Range of the functional. Default: ``domain.field``.
"""
super(ZeroFunctional, self).__init__(domain=domain, constant=0,
range=range)
super(ZeroFunctional, self).__init__(domain=domain, range=range,
constant=0)

def __repr__(self):
"""Return ``repr(self)``."""
Expand Down Expand Up @@ -823,9 +825,9 @@ def __init__(self, field, scale):
>>> func(5)
15.0
"""
Functional.__init__(self, domain=field, linear=True, grad_lipschitz=0,
range=field)
ScalingOperator.__init__(self, field, scale)
Functional.__init__(self, domain=field, range=field, linear=True,
grad_lipschitz=0)
ScalingOperator.__init__(self, domain=field, scalar=scale)

@property
def gradient(self):
Expand Down Expand Up @@ -894,7 +896,7 @@ def __init__(self, domain, lower=None, upper=None, range=None):
>>> func([0, 1, 3]) # one point outside
inf
"""
super(IndicatorBox, self).__init__(domain, linear=False, range=range)
super(IndicatorBox, self).__init__(domain, range=range, linear=False)
self.lower = lower
self.upper = upper

Expand Down Expand Up @@ -952,7 +954,7 @@ def __init__(self, domain, range=None):
inf
"""
super(IndicatorNonnegativity, self).__init__(
domain, lower=0, upper=None, range=range)
domain, range=range, lower=0, upper=None)

def __repr__(self):
"""Return ``repr(self)``."""
Expand Down Expand Up @@ -991,7 +993,7 @@ def __init__(self, domain, constant=0, range=None):
>>> func([0, 0, 0])
2
"""
super(IndicatorZero, self).__init__(domain, linear=False, range=range)
super(IndicatorZero, self).__init__(domain, range=range, linear=False)
self.__constant = constant

@property
Expand Down Expand Up @@ -1176,7 +1178,7 @@ class KLGradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(KLGradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain, linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point.
Expand Down Expand Up @@ -1302,7 +1304,7 @@ class KLCCGradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(KLCCGradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain, linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point.
Expand Down Expand Up @@ -1447,7 +1449,7 @@ class KLCrossEntropyGradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(KLCrossEntropyGradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain, linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point.
Expand Down Expand Up @@ -1560,7 +1562,7 @@ class KLCrossEntCCGradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(KLCrossEntCCGradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain, linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point."""
Expand Down Expand Up @@ -1680,21 +1682,17 @@ def __init__(self, *functionals):
# Make a power space if the second argument is an integer
if (len(functionals) == 2 and isinstance(functionals[1], Integral)):
functionals = [functionals[0]] * functionals[1]
for func1 in functionals:
if all(func1.range.contains_set(func2.range)
for func2 in functionals):
range = func1.range
break
else:
raise ValueError('No functional range contained all other ranges '
'in SeparableSum of {!r}'.format(functionals))

range = RealNumbers()
for func in functionals:
range = (func.range if func.range.contains_set(range) else range)

domains = [func.domain for func in functionals]
domain = ProductSpace(*domains)
linear = all(func.is_linear for func in functionals)

super(SeparableSum, self).__init__(domain=domain, linear=linear,
range=range)
super(SeparableSum, self).__init__(domain=domain, range=range,
linear=linear)
self.__functionals = tuple(functionals)

def _call(self, x):
Expand Down Expand Up @@ -1821,8 +1819,8 @@ def __init__(self, operator=None, vector=None, constant=0):
'to match')

super(QuadraticForm, self).__init__(
domain=domain, linear=(operator is None and constant == 0),
range=range)
domain=domain, range=range,
linear=(operator is None and constant == 0))

self.__operator = operator
self.__vector = vector
Expand Down Expand Up @@ -1998,7 +1996,7 @@ def __init__(self, domain, outer_exp=1, singular_vector_exp=2, range=None):
if range is None:
range = RealNumbers()
super(NuclearNorm, self).__init__(
domain=domain, linear=False, grad_lipschitz=np.nan, range=range)
domain=domain, range=range, linear=False, grad_lipschitz=np.nan)

self.outernorm = LpNorm(self.domain[0, 0], exponent=outer_exp,
range=range)
Expand Down Expand Up @@ -2078,7 +2076,7 @@ class NuclearNormProximal(Operator):
def __init__(self, sigma):
self.sigma = float(sigma)
super(NuclearNormProximal, self).__init__(
func.domain, func.domain, linear=False)
func.domain, range=func.domain, linear=False)

def _call(self, x):
"""Return ``self(x)``."""
Expand Down Expand Up @@ -2205,9 +2203,8 @@ def __init__(self, domain, outer_exp=1, singular_vector_exp=2, range=None):
if range is None:
range = getattr(domain, 'field', RealNumbers())
super(IndicatorNuclearNormUnitBall, self).__init__(
domain=domain, linear=False, grad_lipschitz=np.nan, range=range)
self.__norm = NuclearNorm(domain, outer_exp, singular_vector_exp,
range=RealNumbers())
domain=domain, range=range, linear=False, grad_lipschitz=np.nan)
self.__norm = NuclearNorm(domain, outer_exp, singular_vector_exp)

def _call(self, x):
"""Return ``self(x)``."""
Expand Down Expand Up @@ -2318,7 +2315,7 @@ def __init__(self, functional, sigma=1.0, range=None):
range = RealNumbers()

super(MoreauEnvelope, self).__init__(
domain=functional.domain, linear=False, range=range)
domain=functional.domain, range=range, linear=False)
self.__functional = functional
self.__sigma = sigma
self.__range = range
Expand Down Expand Up @@ -2436,9 +2433,8 @@ def __init__(self, domain, gamma, range=None):
if range is None:
range = RealNumbers()

super(Huber, self).__init__(
domain=domain, linear=False, grad_lipschitz=grad_lipschitz,
range=range)
super(Huber, self).__init__(domain=domain, range=range, linear=False,
grad_lipschitz=grad_lipschitz)

@property
def gamma(self):
Expand Down Expand Up @@ -2532,7 +2528,7 @@ class HuberGradient(Operator):
def __init__(self):
"""Initialize a new instance."""
super(HuberGradient, self).__init__(
functional.domain, functional.domain, linear=False)
functional.domain, range=functional.domain, linear=False)

def _call(self, x):
"""Apply the gradient operator to the given point."""
Expand Down
Loading