Commit af46206a authored by Martin Reinecke's avatar Martin Reinecke
Browse files

domain -> target, round 1

parent d19a916d
Pipeline #72562 failed with stages
in 44 seconds
...@@ -92,22 +92,22 @@ def _actual_domain_check_linear(op, domain_dtype=None, inp=None): ...@@ -92,22 +92,22 @@ def _actual_domain_check_linear(op, domain_dtype=None, inp=None):
inp = from_random("normal", op.domain, dtype=domain_dtype) inp = from_random("normal", op.domain, dtype=domain_dtype)
elif inp is None: elif inp is None:
raise ValueError('Need to specify either dtype or inp') raise ValueError('Need to specify either dtype or inp')
assert_(inp.domain is op.domain) assert_(inp.target is op.domain)
assert_(op(inp).domain is op.target) assert_(op(inp).target is op.target)
def _actual_domain_check_nonlinear(op, loc): def _actual_domain_check_nonlinear(op, loc):
assert isinstance(loc, (Field, MultiField)) assert isinstance(loc, (Field, MultiField))
assert_(loc.domain is op.domain) assert_(loc.target is op.domain)
for wm in [False, True]: for wm in [False, True]:
lin = Linearization.make_var(loc, wm) lin = Linearization.make_var(loc, wm)
reslin = op(lin) reslin = op(lin)
assert_(lin.domain is op.domain) assert_(lin.domain is op.domain)
assert_(lin.target is op.domain) assert_(lin.target is op.domain)
assert_(lin.fld.domain is lin.domain) assert_(lin.fld.target is lin.target)
assert_(reslin.domain is op.domain) assert_(reslin.domain is op.domain)
assert_(reslin.target is op.target) assert_(reslin.target is op.target)
assert_(reslin.fld.domain is reslin.target) assert_(reslin.fld.target is reslin.target)
assert_(reslin.target is op.target) assert_(reslin.target is op.target)
assert_(reslin.jac.domain is reslin.domain) assert_(reslin.jac.domain is reslin.domain)
assert_(reslin.jac.target is reslin.target) assert_(reslin.jac.target is reslin.target)
...@@ -123,7 +123,7 @@ def _domain_check(op): ...@@ -123,7 +123,7 @@ def _domain_check(op):
for dd in [op.domain, op.target]: for dd in [op.domain, op.target]:
if not isinstance(dd, (DomainTuple, MultiDomain)): if not isinstance(dd, (DomainTuple, MultiDomain)):
raise TypeError( raise TypeError(
'The domain and the target of an operator need to', 'The domain and the target of an operator need to '
'be instances of either DomainTuple or MultiDomain.') 'be instances of either DomainTuple or MultiDomain.')
...@@ -220,7 +220,7 @@ def consistency_check(op, domain_dtype=np.float64, target_dtype=np.float64, ...@@ -220,7 +220,7 @@ def consistency_check(op, domain_dtype=np.float64, target_dtype=np.float64,
def _get_acceptable_location(op, loc, lin): def _get_acceptable_location(op, loc, lin):
if not np.isfinite(lin.fld.s_sum()): if not np.isfinite(lin.fld.s_sum()):
raise ValueError('Initial value must be finite') raise ValueError('Initial value must be finite')
dir = from_random("normal", loc.domain) dir = from_random("normal", loc.target)
dirder = lin.jac(dir) dirder = lin.jac(dir)
if dirder.norm() == 0: if dirder.norm() == 0:
dir = dir * (lin.fld.norm()*1e-5) dir = dir * (lin.fld.norm()*1e-5)
......
...@@ -174,7 +174,8 @@ class Field(Operand): ...@@ -174,7 +174,8 @@ class Field(Operand):
@property @property
def domain(self): def domain(self):
"""DomainTuple : the field's domain""" """DomainTuple : the field's domain"""
return self._domain raise NotImplementedError
return None# self._domain
@property @property
def target(self): def target(self):
...@@ -286,13 +287,13 @@ class Field(Operand): ...@@ -286,13 +287,13 @@ class Field(Operand):
Returns Returns
------- -------
Field Field
Defined on the product space of self.domain and x.domain. Defined on the product space of self.target and x.target.
""" """
if not isinstance(x, Field): if not isinstance(x, Field):
raise TypeError("The multiplier must be an instance of " + raise TypeError("The multiplier must be an instance of " +
"the Field class") "the Field class")
from .operators.outer_product_operator import OuterProduct from .operators.outer_product_operator import OuterProduct
return OuterProduct(self, x.domain)(x) return OuterProduct(self, x.target)(x)
def vdot(self, x, spaces=None): def vdot(self, x, spaces=None):
"""Computes the dot product of 'self' with x. """Computes the dot product of 'self' with x.
......
...@@ -110,8 +110,8 @@ def _structured_spaces(domain): ...@@ -110,8 +110,8 @@ def _structured_spaces(domain):
def _total_fluctuation_realized(samples): def _total_fluctuation_realized(samples):
spaces = _structured_spaces(samples[0].domain) spaces = _structured_spaces(samples[0].target)
co = ContractionOperator(samples[0].domain, spaces) co = ContractionOperator(samples[0].target, spaces)
size = co.domain.size/co.target.size size = co.domain.size/co.target.size
res = 0. res = 0.
for s in samples: for s in samples:
...@@ -606,7 +606,7 @@ class CorrelatedFieldMaker: ...@@ -606,7 +606,7 @@ class CorrelatedFieldMaker:
@staticmethod @staticmethod
def offset_amplitude_realized(samples): def offset_amplitude_realized(samples):
spaces = _structured_spaces(samples[0].domain) spaces = _structured_spaces(samples[0].target)
res = 0. res = 0.
for s in samples: for s in samples:
res = res + s.mean(spaces)**2 res = res + s.mean(spaces)**2
...@@ -621,7 +621,7 @@ class CorrelatedFieldMaker: ...@@ -621,7 +621,7 @@ class CorrelatedFieldMaker:
def slice_fluctuation_realized(samples, space): def slice_fluctuation_realized(samples, space):
"""Computes slice fluctuations from collection of field (defined in signal """Computes slice fluctuations from collection of field (defined in signal
space) realizations.""" space) realizations."""
spaces = _structured_spaces(samples[0].domain) spaces = _structured_spaces(samples[0].target)
if space >= len(spaces): if space >= len(spaces):
raise ValueError("invalid space specified; got {!r}".format(space)) raise ValueError("invalid space specified; got {!r}".format(space))
if len(spaces) == 1: if len(spaces) == 1:
...@@ -640,7 +640,7 @@ class CorrelatedFieldMaker: ...@@ -640,7 +640,7 @@ class CorrelatedFieldMaker:
def average_fluctuation_realized(samples, space): def average_fluctuation_realized(samples, space):
"""Computes average fluctuations from collection of field (defined in signal """Computes average fluctuations from collection of field (defined in signal
space) realizations.""" space) realizations."""
spaces = _structured_spaces(samples[0].domain) spaces = _structured_spaces(samples[0].target)
if space >= len(spaces): if space >= len(spaces):
raise ValueError("invalid space specified; got {!r}".format(space)) raise ValueError("invalid space specified; got {!r}".format(space))
if len(spaces) == 1: if len(spaces) == 1:
...@@ -649,7 +649,7 @@ class CorrelatedFieldMaker: ...@@ -649,7 +649,7 @@ class CorrelatedFieldMaker:
sub_spaces = set(spaces) sub_spaces = set(spaces)
sub_spaces.remove(space) sub_spaces.remove(space)
# Domain containing domain[space] and domain[0] iff total_N>0 # Domain containing domain[space] and domain[0] iff total_N>0
sub_dom = makeDomain([samples[0].domain[ind] sub_dom = makeDomain([samples[0].target[ind]
for ind in (set([0])-set(spaces)) | set([space])]) for ind in (set([0])-set(spaces)) | set([space])])
co = ContractionOperator(sub_dom, len(sub_dom)-1) co = ContractionOperator(sub_dom, len(sub_dom)-1)
size = co.domain.size/co.target.size size = co.domain.size/co.target.size
......
...@@ -42,7 +42,7 @@ class Linearization(Operand): ...@@ -42,7 +42,7 @@ class Linearization(Operand):
def __init__(self, fld, jac, metric=None, want_metric=False): def __init__(self, fld, jac, metric=None, want_metric=False):
self._fld = fld self._fld = fld
self._jac = jac self._jac = jac
if self._fld.domain != self._jac.target: if self._fld.target != self._jac.target:
raise ValueError("domain mismatch") raise ValueError("domain mismatch")
self._want_metric = want_metric self._want_metric = want_metric
self._metric = metric self._metric = metric
...@@ -217,8 +217,8 @@ class Linearization(Operand): ...@@ -217,8 +217,8 @@ class Linearization(Operand):
return self.__mul__(other) return self.__mul__(other)
from .operators.outer_product_operator import OuterProduct from .operators.outer_product_operator import OuterProduct
if other.jac is None: if other.jac is None:
return self.new(OuterProduct(self._fld, other.domain)(other), return self.new(OuterProduct(self._fld, other.target)(other),
OuterProduct(self._jac(self._fld), other.domain)) OuterProduct(self._jac(self._fld), other.target))
return self.new( return self.new(
OuterProduct(self._fld, other.target)(other._fld), OuterProduct(self._fld, other.target)(other._fld),
OuterProduct(self._jac(self._fld), other.target)._myadd( OuterProduct(self._jac(self._fld), other.target)._myadd(
...@@ -318,7 +318,7 @@ class Linearization(Operand): ...@@ -318,7 +318,7 @@ class Linearization(Operand):
the requested Linearization the requested Linearization
""" """
from .operators.scaling_operator import ScalingOperator from .operators.scaling_operator import ScalingOperator
return Linearization(field, ScalingOperator(field.domain, 1.), return Linearization(field, ScalingOperator(field.target, 1.),
want_metric=want_metric) want_metric=want_metric)
@staticmethod @staticmethod
...@@ -343,7 +343,7 @@ class Linearization(Operand): ...@@ -343,7 +343,7 @@ class Linearization(Operand):
The Jacobian is square and contains only zeroes. The Jacobian is square and contains only zeroes.
""" """
from .operators.simple_linear_operators import NullOperator from .operators.simple_linear_operators import NullOperator
return Linearization(field, NullOperator(field.domain, field.domain), return Linearization(field, NullOperator(field.target, field.target),
want_metric=want_metric) want_metric=want_metric)
@staticmethod @staticmethod
...@@ -371,7 +371,7 @@ class Linearization(Operand): ...@@ -371,7 +371,7 @@ class Linearization(Operand):
from .operators.simple_linear_operators import NullOperator from .operators.simple_linear_operators import NullOperator
from .multi_domain import MultiDomain from .multi_domain import MultiDomain
return Linearization( return Linearization(
field, NullOperator(MultiDomain.make({}), field.domain), field, NullOperator(MultiDomain.make({}), field.target),
want_metric=want_metric) want_metric=want_metric)
@staticmethod @staticmethod
...@@ -405,6 +405,6 @@ class Linearization(Operand): ...@@ -405,6 +405,6 @@ class Linearization(Operand):
return Linearization.make_var(field, want_metric) return Linearization.make_var(field, want_metric)
else: else:
ops = {key: ScalingOperator(dom, 0. if key in constants else 1.) ops = {key: ScalingOperator(dom, 0. if key in constants else 1.)
for key, dom in field.domain.items()} for key, dom in field.target.items()}
bdop = BlockDiagonalOperator(field.domain, ops) bdop = BlockDiagonalOperator(field.target, ops)
return Linearization(field, bdop, want_metric=want_metric) return Linearization(field, bdop, want_metric=want_metric)
...@@ -50,18 +50,18 @@ def _allreduce_sum_field(comm, fld): ...@@ -50,18 +50,18 @@ def _allreduce_sum_field(comm, fld):
if comm is None: if comm is None:
return fld return fld
if isinstance(fld, Field): if isinstance(fld, Field):
return Field(fld.domain, _np_allreduce_sum(fld.val)) return Field(fld.target, _np_allreduce_sum(fld.val))
res = tuple( res = tuple(
Field(f.domain, _np_allreduce_sum(comm, f.val)) Field(f.target, _np_allreduce_sum(comm, f.val))
for f in fld.values()) for f in fld.values())
return MultiField(fld.domain, res) return MultiField(fld.target, res)
class _KLMetric(EndomorphicOperator): class _KLMetric(EndomorphicOperator):
def __init__(self, KL): def __init__(self, KL):
self._KL = KL self._KL = KL
self._capability = self.TIMES | self.ADJOINT_TIMES self._capability = self.TIMES | self.ADJOINT_TIMES
self._domain = KL.position.domain self._domain = KL.position.target
def apply(self, x, mode): def apply(self, x, mode):
self._check_input(x, mode) self._check_input(x, mode)
...@@ -144,7 +144,7 @@ class MetricGaussianKL(Energy): ...@@ -144,7 +144,7 @@ class MetricGaussianKL(Energy):
if not isinstance(hamiltonian, StandardHamiltonian): if not isinstance(hamiltonian, StandardHamiltonian):
raise TypeError raise TypeError
if hamiltonian.domain is not mean.domain: if hamiltonian.domain is not mean.target:
raise ValueError raise ValueError
if not isinstance(n_samples, int): if not isinstance(n_samples, int):
raise TypeError raise TypeError
......
...@@ -41,7 +41,9 @@ class MultiField(Operand): ...@@ -41,7 +41,9 @@ class MultiField(Operand):
raise ValueError("length mismatch") raise ValueError("length mismatch")
for d, v in zip(domain._domains, val): for d, v in zip(domain._domains, val):
if isinstance(v, Field): if isinstance(v, Field):
if v._domain != d: if v.target != d:
print(v.target)
print(d)
raise ValueError("domain mismatch") raise ValueError("domain mismatch")
else: else:
raise TypeError("bad entry in val (must be Field)") raise TypeError("bad entry in val (must be Field)")
...@@ -52,7 +54,7 @@ class MultiField(Operand): ...@@ -52,7 +54,7 @@ class MultiField(Operand):
def from_dict(dict, domain=None): def from_dict(dict, domain=None):
if domain is None: if domain is None:
for dd in dict.values(): for dd in dict.values():
if not isinstance(dd.domain, DomainTuple): if not isinstance(dd.target, DomainTuple):
raise TypeError('Values of dictionary need to be Fields ' raise TypeError('Values of dictionary need to be Fields '
'defined on DomainTuples.') 'defined on DomainTuples.')
domain = MultiDomain.make({key: v._domain domain = MultiDomain.make({key: v._domain
...@@ -81,7 +83,8 @@ class MultiField(Operand): ...@@ -81,7 +83,8 @@ class MultiField(Operand):
@property @property
def domain(self): def domain(self):
return self._domain raise NotImplementedError
return None #self._domain
@property @property
def target(self): def target(self):
...@@ -329,15 +332,15 @@ class MultiField(Operand): ...@@ -329,15 +332,15 @@ class MultiField(Operand):
for i in range(len(self._val)): for i in range(len(self._val)):
argstmp, kwargstmp = self._prep_args(args, kwargs, i) argstmp, kwargstmp = self._prep_args(args, kwargs, i)
tmp.append(self._val[i].ptw(op, *argstmp, **kwargstmp)) tmp.append(self._val[i].ptw(op, *argstmp, **kwargstmp))
return MultiField(self.domain, tuple(tmp)) return MultiField(self.target, tuple(tmp))
def ptw_with_deriv(self, op, *args, **kwargs): def ptw_with_deriv(self, op, *args, **kwargs):
tmp = [] tmp = []
for i in range(len(self._val)): for i in range(len(self._val)):
argstmp, kwargstmp = self._prep_args(args, kwargs, i) argstmp, kwargstmp = self._prep_args(args, kwargs, i)
tmp.append(self._val[i].ptw_with_deriv(op, *argstmp, **kwargstmp)) tmp.append(self._val[i].ptw_with_deriv(op, *argstmp, **kwargstmp))
return (MultiField(self.domain, tuple(v[0] for v in tmp)), return (MultiField(self.target, tuple(v[0] for v in tmp)),
MultiField(self.domain, tuple(v[1] for v in tmp))) MultiField(self.target, tuple(v[1] for v in tmp)))
def _binary_op(self, other, op): def _binary_op(self, other, op):
f = getattr(Field, op) f = getattr(Field, op)
......
...@@ -34,7 +34,7 @@ class Adder(Operator): ...@@ -34,7 +34,7 @@ class Adder(Operator):
def __init__(self, a, neg=False, domain=None): def __init__(self, a, neg=False, domain=None):
self._a = a self._a = a
if isinstance(a, (Field, MultiField)): if isinstance(a, (Field, MultiField)):
dom = a.domain dom = a.target
elif np.isscalar(a): elif np.isscalar(a):
dom = makeDomain(domain) dom = makeDomain(domain)
else: else:
......
...@@ -62,14 +62,14 @@ def FuncConvolutionOperator(domain, func, space=None): ...@@ -62,14 +62,14 @@ def FuncConvolutionOperator(domain, func, space=None):
def _ConvolutionOperator(domain, kernel, space=None): def _ConvolutionOperator(domain, kernel, space=None):
domain = DomainTuple.make(domain) domain = DomainTuple.make(domain)
space = utilities.infer_space(domain, space) space = utilities.infer_space(domain, space)
if len(kernel.domain) != 1: if len(kernel.target) != 1:
raise ValueError("kernel needs exactly one domain") raise ValueError("kernel needs exactly one domain")
if not isinstance(domain[space], (HPSpace, GLSpace, RGSpace)): if not isinstance(domain[space], (HPSpace, GLSpace, RGSpace)):
raise TypeError("need RGSpace, HPSpace, or GLSpace") raise TypeError("need RGSpace, HPSpace, or GLSpace")
lm = [d for d in domain] lm = [d for d in domain]
lm[space] = lm[space].get_default_codomain() lm[space] = lm[space].get_default_codomain()
lm = DomainTuple.make(lm) lm = DomainTuple.make(lm)
if lm[space] != kernel.domain[0]: if lm[space] != kernel.target[0]:
raise ValueError("Input domain and kernel are incompatible") raise ValueError("Input domain and kernel are incompatible")
HT = HarmonicTransformOperator(lm, domain[space], space) HT = HarmonicTransformOperator(lm, domain[space], space)
diag = DiagonalOperator(kernel*domain[space].total_volume, lm, (space,)) diag = DiagonalOperator(kernel*domain[space].total_volume, lm, (space,))
......
...@@ -56,19 +56,19 @@ class DiagonalOperator(EndomorphicOperator): ...@@ -56,19 +56,19 @@ class DiagonalOperator(EndomorphicOperator):
if not isinstance(diagonal, Field): if not isinstance(diagonal, Field):
raise TypeError("Field object required") raise TypeError("Field object required")
if domain is None: if domain is None:
self._domain = diagonal.domain self._domain = diagonal.target
else: else:
self._domain = DomainTuple.make(domain) self._domain = DomainTuple.make(domain)
if spaces is None: if spaces is None:
self._spaces = None self._spaces = None
if diagonal.domain != self._domain: if diagonal.target != self._domain:
raise ValueError("domain mismatch") raise ValueError("domain mismatch")
else: else:
self._spaces = utilities.parse_spaces(spaces, len(self._domain)) self._spaces = utilities.parse_spaces(spaces, len(self._domain))
if len(self._spaces) != len(diagonal.domain): if len(self._spaces) != len(diagonal.target):
raise ValueError("spaces and domain must have the same length") raise ValueError("spaces and domain must have the same length")
for i, j in enumerate(self._spaces): for i, j in enumerate(self._spaces):
if diagonal.domain[i] != self._domain[j]: if diagonal.target[i] != self._domain[j]:
raise ValueError("domain mismatch") raise ValueError("domain mismatch")
if self._spaces == tuple(range(len(self._domain))): if self._spaces == tuple(range(len(self._domain))):
self._spaces = None # shortcut self._spaces = None # shortcut
...@@ -130,15 +130,15 @@ class DiagonalOperator(EndomorphicOperator): ...@@ -130,15 +130,15 @@ class DiagonalOperator(EndomorphicOperator):
self._check_input(x, mode) self._check_input(x, mode)
# shortcut for most common cases # shortcut for most common cases
if mode == 1 or (not self._complex and mode == 2): if mode == 1 or (not self._complex and mode == 2):
return Field(x.domain, x.val*self._ldiag) return Field(x.target, x.val*self._ldiag)
xdiag = self._ldiag xdiag = self._ldiag
if self._complex and (mode & 10): # adjoint or inverse adjoint if self._complex and (mode & 10): # adjoint or inverse adjoint
xdiag = xdiag.conj() xdiag = xdiag.conj()
if mode & 3: if mode & 3:
return Field(x.domain, x.val*xdiag) return Field(x.target, x.val*xdiag)
return Field(x.domain, x.val/xdiag) return Field(x.target, x.val/xdiag)
def _flip_modes(self, trafo): def _flip_modes(self, trafo):
if trafo == self.ADJOINT_BIT and not self._complex: # shortcut if trafo == self.ADJOINT_BIT and not self._complex: # shortcut
......
...@@ -51,17 +51,17 @@ class DOFDistributor(LinearOperator): ...@@ -51,17 +51,17 @@ class DOFDistributor(LinearOperator):
def __init__(self, dofdex, target=None, space=None): def __init__(self, dofdex, target=None, space=None):
if target is None: if target is None:
target = dofdex.domain target = dofdex.target
self._target = DomainTuple.make(target) self._target = DomainTuple.make(target)
space = infer_space(self._target, space) space = infer_space(self._target, space)
partner = self._target[space] partner = self._target[space]
if not isinstance(dofdex, Field): if not isinstance(dofdex, Field):
raise TypeError("dofdex must be a Field") raise TypeError("dofdex must be a Field")
if not len(dofdex.domain) == 1: if not len(dofdex.target) == 1:
raise ValueError("dofdex must be defined on exactly one Space") raise ValueError("dofdex must be defined on exactly one Space")
if not np.issubdtype(dofdex.dtype, np.integer): if not np.issubdtype(dofdex.dtype, np.integer):
raise TypeError("dofdex must contain integer numbers") raise TypeError("dofdex must contain integer numbers")
if partner != dofdex.domain[0]: if partner != dofdex.target[0]:
raise ValueError("incorrect dofdex domain") raise ValueError("incorrect dofdex domain")
ldat = dofdex.val ldat = dofdex.val
......
...@@ -60,5 +60,5 @@ class EndomorphicOperator(LinearOperator): ...@@ -60,5 +60,5 @@ class EndomorphicOperator(LinearOperator):
def _check_input(self, x, mode): def _check_input(self, x, mode):
self._check_mode(mode) self._check_mode(mode)
if self.domain != x.domain: if self.domain != x.target:
raise ValueError("The operator's and field's domains don't match.") raise ValueError("The operator's and field's domains don't match.")
...@@ -166,7 +166,7 @@ class GaussianEnergy(EnergyOperator): ...@@ -166,7 +166,7 @@ class GaussianEnergy(EnergyOperator):
self._domain = None self._domain = None
if mean is not None: if mean is not None:
self._checkEquivalence(mean.domain) self._checkEquivalence(mean.target)
if inverse_covariance is not None: if inverse_covariance is not None:
self._checkEquivalence(inverse_covariance.domain) self._checkEquivalence(inverse_covariance.domain)
if domain is not None: if domain is not None:
...@@ -223,7 +223,7 @@ class PoissonianEnergy(EnergyOperator): ...@@ -223,7 +223,7 @@ class PoissonianEnergy(EnergyOperator):
if np.any(d.val < 0): if np.any(d.val < 0):
raise ValueError raise ValueError
self._d = d self._d = d
self._domain = DomainTuple.make(d.domain) self._domain = DomainTuple.make(d.target)
def apply(self, x): def apply(self, x):
self._check_input(x) self._check_input(x)
...@@ -257,10 +257,10 @@ class InverseGammaLikelihood(EnergyOperator): ...@@ -257,10 +257,10 @@ class InverseGammaLikelihood(EnergyOperator):
def __init__(self, beta, alpha=-0.5): def __init__(self, beta, alpha=-0.5):
if not isinstance(beta, Field): if not isinstance(beta, Field):
raise TypeError raise TypeError
self._domain = DomainTuple.make(beta.domain) self._domain = DomainTuple.make(beta.target)
self._beta = beta self._beta = beta
if np.isscalar(alpha): if np.isscalar(alpha):
alpha = Field(beta.domain, np.full(beta.shape, alpha)) alpha = Field(beta.target, np.full(beta.shape, alpha))
elif not isinstance(alpha, Field): elif not isinstance(alpha, Field):
raise TypeError raise TypeError
self._alphap1 = alpha+1 self._alphap1 = alpha+1
...@@ -311,7 +311,7 @@ class BernoulliEnergy(EnergyOperator): ...@@ -311,7 +311,7 @@ class BernoulliEnergy(EnergyOperator):
E(f) = -\\log \\text{Bernoulli}(d|f) E(f) = -\\log \\text{Bernoulli}(d|f)
= -d^\\dagger \\log f - (1-d)^\\dagger \\log(1-f), = -d^\\dagger \\log f - (1-d)^\\dagger \\log(1-f),
where f is a field defined on `d.domain` with the expected where f is a field defined on `d.target` with the expected
frequencies of events. frequencies of events.