Source code for nifty8.multi_domain
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright(C) 2013-2019 Max-Planck-Society
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.
from .domain_tuple import DomainTuple
from .utilities import check_object_identity, frozendict, indent
[docs]
class MultiDomain:
"""A tuple of domains corresponding to a direct sum.
This class is the domain of the direct sum of fields defined on (possibly
different) domains. To make an instance of this class, call
`MultiDomain.make(inp)`.
Notes
-----
For consistency and to be independent of the order of insertion, the keys
within a multi-domain are sorted. Hence, renaming a domain may result in it
being placed at a different index within a multi-domain. This is especially
important if a sequence of, e.g., random numbers is distributed sequentially
over a multi-domain. In this example, ordering keys differently will change
the resulting :class:`MultiField`.
"""
_domainCache = {}
[docs]
def __init__(self, dct, _callingfrommake=False):
if not _callingfrommake:
raise NotImplementedError(
'To create a MultiDomain call `MultiDomain.make()`.')
self._keys = tuple(sorted(dct.keys()))
self._domains = tuple(dct[key] for key in self._keys)
self._idx = frozendict({key: i for i, key in enumerate(self._keys)})
[docs]
@staticmethod
def make(inp):
"""Creates a MultiDomain object from a dictionary of names and domains
Parameters
----------
inp : MultiDomain or dict{name: DomainTuple}
The already built MultiDomain or a dictionary of DomainTuples
Returns
------
A MultiDomain with the input Domains as domains
"""
if isinstance(inp, MultiDomain):
return inp
if not isinstance(inp, dict):
raise TypeError("dict expected")
tmp = {}
for key, value in inp.items():
if not isinstance(key, str):
raise TypeError("keys must be strings")
tmp[key] = DomainTuple.make(value)
tmp = frozendict(tmp)
obj = MultiDomain._domainCache.get(tmp)
if obj is not None:
return obj
obj = MultiDomain(tmp, _callingfrommake=True)
MultiDomain._domainCache[tmp] = obj
return obj
[docs]
def keys(self):
return self._keys
[docs]
def values(self):
return self._domains
[docs]
def domains(self):
return self._domains
@property
def idx(self):
return self._idx
[docs]
def items(self):
return zip(self._keys, self._domains)
def __getitem__(self, key):
return self._domains[self._idx[key]]
def __len__(self):
return len(self._keys)
def __hash__(self):
return self._keys.__hash__() ^ self._domains.__hash__()
def __eq__(self, x):
if self is x:
return True
return isinstance(x, MultiDomain) and list(self.items()) == list(x.items())
def __ne__(self, x):
return not self.__eq__(x)
@property
def size(self):
return sum(dom.size for dom in self._domains)
def __str__(self):
res = "MultiDomain:"
for key, dom in zip(self._keys, self._domains):
for ll in f"{key}: {dom}".splitlines():
res += f"\n {ll}"
return res
[docs]
@staticmethod
def union(inp):
inp = set(inp)
if len(inp) == 1: # all domains are identical
return inp.pop()
res = {}
for dom in inp:
for key, subdom in zip(dom._keys, dom._domains):
if key in res:
check_object_identity(res[key], subdom)
else:
res[key] = subdom
return MultiDomain.make(res)
def __reduce__(self):
return (_unpickleMultiDomain, (dict(self),))
def __repr__(self):
return f"MultiDomain.make({dict(self)})"
def _unpickleMultiDomain(*args):
return MultiDomain.make(*args)