Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
    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)
Пример #4
0
    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}")
Пример #5
0
    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)
Пример #6
0
    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)
Пример #7
0
    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)