def __init__(self, axis: float = np.pi / 2, fibergroup: Group = None): r""" Describes reflectional symmetries of the plane :math:`\R^2`. Reflections are applied along the line through the origin with an angle ``axis`` degrees with respect to the *X*-axis. Args: axis (float, optional): the slope of the axis of the reflection (in radians). By default, the vertical axis is used (:math:`\pi/2`). fibergroup (Group, optional): use an already existing instance of the symmetry group Attributes: ~.axis (float): Angle with respect to the horizontal axis which defines the reflection axis. """ self.axis = axis if fibergroup is None: fibergroup = cyclic_group(2) else: assert isinstance(fibergroup, CyclicGroup) and fibergroup.order() == 2 name = 'Flips' super(Flip2dOnR2, self).__init__(fibergroup, name)
def __init__(self, N: int = None, maximum_frequency: int = None, fibergroup: Group = None): r""" Describes rotation symmetries of the plane :math:`\R^2`. If ``N > 1``, the class models *discrete* rotations by angles which are multiple of :math:`\frac{2\pi}{N}` (:class:`~e2cnn.group.CyclicGroup`). Otherwise, if ``N=-1``, the class models *continuous* planar rotations (:class:`~e2cnn.group.SO2`). In that case the parameter ``maximum_frequency`` is required to specify the maximum frequency of the irreps of :class:`~e2cnn.group.SO2` (see its documentation for more details) Args: N (int): number of discrete rotations (integer greater than 1) or ``-1`` for continuous rotations maximum_frequency (int): maximum frequency of :class:`~e2cnn.group.SO2`'s irreps if ``N = -1`` fibergroup (Group, optional): use an already existing instance of the symmetry group. In that case, the other parameters should not be provided. """ assert N is not None or fibergroup is not None, "Error! Either use the parameter `N` or the parameter `group`!" if fibergroup is not None: assert isinstance(fibergroup, CyclicGroup) or isinstance( fibergroup, SO2) assert maximum_frequency is None, "Maximum Frequency can't be set when the group is already provided in input" N = fibergroup.order() assert isinstance(N, int) if N > 1: assert maximum_frequency is None, "Maximum Frequency can't be set for finite cyclic groups" name = '{}-Rotations'.format(N) elif N == -1: name = 'Continuous-Rotations' else: raise ValueError( f'Error! "N" has to be an integer greater than 1 or -1, but got {N}' ) if fibergroup is None: if N > 1: fibergroup = cyclic_group(N) elif N == -1: fibergroup = so2_group(maximum_frequency) super(Rot2dOnR2, self).__init__(fibergroup, name)
def __init__(self, fibergroup: Group = None): r""" Describes the plane :math:`\R^2` without considering any origin-preserving symmetry. This is modeled by a choosing trivial fiber group :math:`\{e\}`. .. note :: This models the symmetries of conventional *Convolutional Neural Networks* which are not equivariant to origin preserving transformations such as rotations and reflections. Args: fibergroup (Group, optional): use an already existing instance of the symmetry group. By default, it builds a new instance of the trivial group. """ if fibergroup is None: fibergroup = cyclic_group(1) else: assert isinstance(fibergroup, CyclicGroup) and fibergroup.order() == 1 name = "Trivial" super(TrivialOnR2, self).__init__(fibergroup, name)
def check_group(self, group: Group): # def equal(x, y): # if type(x) != type(y): # return False # elif hasattr(x, "__iter__"): # if len(x) != len(y): # return False # else: # for a, b in zip(x, y): # if type(a) != type(b): # return False # elif isinstance(a, float): # return math.isclose(a, b, abs_tol=1e-15) # else: # return a == b # elif isinstance(x, float): # return math.isclose(x, y, abs_tol=1e-15) # else: # return x == y e = group.identity for a in group.testing_elements(): self.assertTrue(group.equal(group.combine(a, e), a)) self.assertTrue(group.equal(group.combine(e, a), a)) i = group.inverse(a) self.assertTrue(group.equal(group.combine(a, i), e)) self.assertTrue(group.equal(group.combine(i, a), e)) for b in group.testing_elements(): for c in group.testing_elements(): a_bc = group.combine(a, group.combine(b, c)) ab_c = group.combine(group.combine(a, b), c) self.assertTrue(group.equal(a_bc, ab_c), f"{a_bc} != {ab_c}")
def __init__( self, group: Group, in_irrep: Union[str, IrreducibleRepresentation, Tuple[int]], out_irrep: Union[str, IrreducibleRepresentation, Tuple[int, int]], axis: float = 0., ): assert isinstance(group, O2) assert isinstance(axis, float) self.axis = axis if isinstance(in_irrep, tuple): in_irrep = group.irrep(in_irrep[0], in_irrep[1]) elif isinstance(in_irrep, str): in_irrep = group.irreps[in_irrep] elif not isinstance(in_irrep, IrreducibleRepresentation): raise ValueError( f"'in_irrep' should be a non-negative integer, a string or an instance" f" of IrreducibleRepresentation but {in_irrep} found") if isinstance(out_irrep, tuple): out_irrep = group.irrep(out_irrep[0], out_irrep[1]) elif isinstance(out_irrep, str): out_irrep = group.irreps[out_irrep] elif not isinstance(out_irrep, IrreducibleRepresentation): raise ValueError( f"'out_irrep' should be a non-negative integer, a string or an instance" f" of IrreducibleRepresentation but {in_irrep} found") self.m = out_irrep.attributes['frequency'] self.n = in_irrep.attributes['frequency'] self.fi = in_irrep.attributes['flip_frequency'] self.fo = out_irrep.attributes['flip_frequency'] if in_irrep.size == 2 and out_irrep.size == 2: assert (self.m > 0 and self.n > 0 and self.fi == 1 and self.fo == 1) # m, n > 0 mus = [] ss = [] self.gamma = 0. for s in [0, 1]: mu = self.m - self.n * (-1)**s mus.append(mu) ss.append(s) self.mu = np.array(mus).reshape(-1, 1) self.s = np.array(ss).reshape(-1, 1) elif in_irrep.size == 2 and out_irrep.size == 1: assert self.m == 0 and self.fi == 1 # n > 0, m = 0 self.gamma = self.fo * np.pi / 2 mus = [] mu = self.n + self.m mus.append(mu) self.mu = np.array(mus).reshape(-1, 1) elif in_irrep.size == 1 and out_irrep.size == 2: assert self.n == 0 and self.fo == 1 # m > 0, n = 0 self.gamma = self.fi * np.pi / 2 mus = [] mu = self.n + self.m mus.append(mu) self.mu = np.array(mus).reshape(-1, 1) elif in_irrep.size == 1 and out_irrep.size == 1: assert self.n == 0 and self.m == 0 self.gamma = ((self.fi + self.fo) % 2) * np.pi / 2 mus = [] mu = self.m - self.n if mu > 0 or self.gamma == 0.: # don't add sin(0*theta) as a basis since it is zero everywhere mus.append(mu) self.mu = np.array(mus).reshape(-1, 1) self._non_zero_frequencies = self.mu != 0 self._has_non_zero_frequencies = np.any(self._non_zero_frequencies) dim = self.mu.shape[0] super(R2FlipsContinuousRotationsSolution, self).__init__(group, in_irrep, out_irrep, dim)
def __init__( self, group: Group, in_irrep: Union[str, IrreducibleRepresentation, int], out_irrep: Union[str, IrreducibleRepresentation, int], ): assert isinstance(group, SO2) if isinstance(in_irrep, int): in_irrep = group.irrep(in_irrep) elif isinstance(in_irrep, str): in_irrep = group.irreps[in_irrep] elif not isinstance(in_irrep, IrreducibleRepresentation): raise ValueError( f"'in_irrep' should be a non-negative integer, a string or an instance" f" of IrreducibleRepresentation but {in_irrep} found") self.n = in_irrep.attributes['frequency'] if isinstance(out_irrep, int): out_irrep = group.irrep(out_irrep) elif isinstance(out_irrep, str): out_irrep = group.irreps[out_irrep] elif not isinstance(out_irrep, IrreducibleRepresentation): raise ValueError( f"'out_irrep' should be a non-negative integer, a string or an instance" f" of IrreducibleRepresentation but {in_irrep} found") self.m = out_irrep.attributes['frequency'] if in_irrep.size == 2 and out_irrep.size == 2: # m, n > 0 gammas = [] mus = [] ss = [] for gamma in [0., np.pi / 2]: for s in [0, 1]: mu = self.m - self.n * (-1)**s gammas.append(gamma) mus.append(mu) ss.append(s) self.gamma = np.array(gammas).reshape(-1, 1) self.mu = np.array(mus).reshape(-1, 1) self.s = np.array(ss).reshape(-1, 1) elif in_irrep.size == 2 and out_irrep.size == 1: assert self.m == 0 # n > 0, m = 0 gammas = [] mus = [] for gamma in [0., np.pi / 2]: mu = self.n + self.m gammas.append(gamma) mus.append(mu) self.gamma = np.array(gammas).reshape(-1, 1) self.mu = np.array(mus).reshape(-1, 1) elif in_irrep.size == 1 and out_irrep.size == 2: assert self.n == 0 # m > 0, n = 0 gammas = [] mus = [] for gamma in [0., np.pi / 2]: mu = self.n + self.m gammas.append(gamma) mus.append(mu) self.gamma = np.array(gammas).reshape(-1, 1) self.mu = np.array(mus).reshape(-1, 1) elif in_irrep.size == 1 and out_irrep.size == 1: assert self.n == 0 and self.m == 0 gammas = [] mus = [] for gamma in [0., np.pi / 2]: mu = self.m - self.n if mu > 0 or gamma == 0.: # don't add sin(0*theta) as a basis since it is zero everywhere gammas.append(gamma) mus.append(mu) self.gamma = np.array(gammas).reshape(-1, 1) self.mu = np.array(mus).reshape(-1, 1) self._non_zero_frequencies = self.mu != 0 self._has_non_zero_frequencies = np.any(self._non_zero_frequencies) dim = self.gamma.shape[0] super(R2ContinuousRotationsSolution, self).__init__(group, in_irrep, out_irrep, dim)
def __init__( self, group: Group, in_irrep: Union[str, IrreducibleRepresentation, int], out_irrep: Union[str, IrreducibleRepresentation, int], axis: float, max_frequency: int = None, max_offset: int = None, ): if isinstance(group, int): group = cyclic_group(2) assert isinstance(group, CyclicGroup) and group.order() == 2 assert (max_frequency is not None or max_offset is not None), \ 'Error! Either the maximum frequency or the maximum offset for the frequencies must be set' self.max_frequency = max_frequency self.max_offset = max_offset assert max_frequency is None or (isinstance(max_frequency, int) and max_frequency >= 0) assert max_offset is None or (isinstance(max_offset, int) and max_offset >= 0) assert isinstance(axis, float) self.axis = axis if isinstance(in_irrep, int): in_irrep = group.irrep(in_irrep) elif isinstance(in_irrep, str): in_irrep = group.irreps[in_irrep] elif not isinstance(in_irrep, IrreducibleRepresentation): raise ValueError( f"'in_irrep' should be a non-negative integer, a string or an instance" f" of IrreducibleRepresentation but {in_irrep} found") if isinstance(out_irrep, int): out_irrep = group.irrep(out_irrep) elif isinstance(out_irrep, str): out_irrep = group.irreps[out_irrep] elif not isinstance(out_irrep, IrreducibleRepresentation): raise ValueError( f"'out_irrep' should be a non-negative integer, a string or an instance" f" of IrreducibleRepresentation but {in_irrep} found") self.N = 1 self.fi = in_irrep.attributes['frequency'] self.fo = out_irrep.attributes['frequency'] self.ts = [] self.gamma = ((self.fi + self.fo) % 2) * np.pi / 2 mus = [] # for each available frequency offset, build the corresponding basis vector for t in offset_iterator(0, 1, self.max_offset, self.max_frequency, non_negative=True): # the current shifted frequency mu = t if self.max_offset is not None: assert (math.fabs(t) <= self.max_offset), (t, self.max_offset) if self.max_frequency is not None: assert (math.fabs(mu) <= self.max_frequency), (t, mu, self.max_frequency) if mu > 0 or self.gamma == 0.: # don't add sin(0*theta) as a basis since it is zero everywhere mus.append(mu) self.ts.append(t) self.mu = np.array(mus).reshape(-1, 1) self._non_zero_frequencies = self.mu != 0 self._has_non_zero_frequencies = np.any(self._non_zero_frequencies) dim = self.mu.shape[0] super(R2FlipsSolution, self).__init__(group, in_irrep, out_irrep, dim)