Module garoupa.algebra.product.product

Classes

class Product (*groups, seed=None)
Expand source code
class Product(Group):
    def __init__(self, *groups, seed=None):
        self.groups = groups
        identity = Tuple(*(g.identity for g in self.groups))
        sorted = lambda: (Tuple(*es) for es in product(*(g.sorted() for g in self.groups)))
        super().__init__(identity, sorted, seed)

    @property
    def comm_degree(self):
        comms = [g.comm_degree for g in self.groups]
        if any(comm is None for comm in comms):
            return
        return reduce(operator.mul, comms)

    def __iter__(self):
        its = [cycle(iter(g)) for g in self.groups]
        while True:
            yield Tuple(*(next(it) for it in its))

    def __repr__(self):
        return "×".join([str(g) for g in self.groups])

    def __mul__(self, other):
        if isinstance(other, Product):
            return Product(*self.groups, *other.groups)
        return Product(*self.groups, other)

    def replace(self, *args, **kwargs):
        dic = {"seed": self.seed}
        dic.update(kwargs)
        groups = args or self.groups
        return self.__class__(*groups, **dic)

    @classmethod
    def order_hist_mul(cls, hista, histb):
        """Histogram of element orders for a product of 2 groups.

        Usage:

        >>> from garoupa.algebra.dihedral import D
        >>> Product.order_hist_mul(D(5).order_hist, D(7).order_hist)
        {1: 1, 2: 47, 5: 4, 7: 6, 10: 28, 14: 30, 35: 24}

        Based on Gabriel Dalforno code."""
        hist = {}
        for k1 in hista.keys():
            for k2 in histb.keys():
                key = cls.lcm(k1, k2)
                if key not in hist.keys():
                    hist[key] = hista[k1] * histb[k2]
                else:
                    hist[key] += hista[k1] * histb[k2]
        return dict(sorted(hist.items()))

    @property
    def order_hist(self):
        """Sorted histogram of element orders.

        Usage:

        >>> from garoupa.algebra.dihedral import D
        >>> (D(3) * D(5) * D(7)).order_hist
        {1: 1, 2: 191, 3: 2, 5: 4, 6: 94, 7: 6, 10: 124, 14: 138, 15: 8, 21: 12, 30: 56, 35: 24, 42: 60, 70: 72, 105: 48}
        """
        if self._order_hist is None:
            self._order_hist = dict(sorted(reduce(self.order_hist_mul, (G.order_hist for G in self.groups)).items()))
        return self._order_hist

    def compact_order_hist_lowmem(self, max_histsize, preserve_upto, initial_binsize=1, show_timestamp=True):
        """Memory-friendly histogram of element orders in a direct product

        Compact largest intermediate histograms during calculation to avoid memory exhaustion.
        Nested products will also be processed through this method.
        Final and temporary hist may exceed max_histsize by a factor of 2 at most.

        Usage:

        >>> from garoupa.algebra.dihedral import D
        >>> Product.order_hist_mul(D(7).order_hist, D(19).order_hist)
        {1: 1, 2: 159, 7: 6, 14: 114, 19: 18, 38: 126, 133: 108}
        >>> G = D(3) * D(5) * D(7) * D(9)
        >>> G.compact_order_hist(binsize=20)
        {9: 7936, 29: 1064, 42: 1176, 69: 1044, 90: 1080, 105: 192, 126: 1188, 210: 576, 315: 432, 630: 432}
        >>> G.compact_order_hist_lowmem(max_histsize=5, preserve_upto=0, show_timestamp=False)  # doctest: +NORMALIZE_WHITESPACE
        Pi: 0.28944444444444445         Hist size: 7     False  D3*D5 [1] [1]
        Pi: 0.1997278911564626  Hist size: 10    False  D3*D5*D7 [2] [1]
        Pi: 0.09817901234567901         Hist size: 12    False  D3*D5*D7*D9 [4] [1]
        {3: 1134, 6: 3402, 9: 2268, 10: 2020, 30: 1076, 35: 84, 70: 1476, 90: 1548, 105: 312, 210: 576, 315: 792, 630: 432}
        >>> G.compact_order_hist_lowmem(max_histsize=5, preserve_upto=10, show_timestamp=False)  # doctest: +NORMALIZE_WHITESPACE
        Pi: 0.28944444444444445         Hist size: 7     False  D3*D5 [1] [1]
        Pi: 0.17061791383219957         Hist size: 15    False  D3*D5*D7 [2] [1]
        Pi: 0.11385896951373144         Hist size: 24    False  D3*D5*D7*D9 [4] [1]
        {1: 1, 2: 1919, 3: 8, 5: 4, 6: 1600, 9: 18, 10: 36, 12: 3240, 15: 8, 18: 1746, 21: 36, 30: 672, 35: 24, 36: 1620, 42: 828, 45: 24, 63: 72, 70: 936, 90: 336, 105: 192, 126: 360, 210: 576, 315: 432, 630: 432}
        """

        # if max_histsize <= preserve_upto:  errado
        #     raise Exception(f"Cannot preserve up to order {preserve_upto} limited by {max_histsize} at the same time.")

        def hists():
            for g in self.groups:
                if isinstance(g, Product):
                    yield str(g), g.compact_order_hist_lowmem(max_histsize, preserve_upto, initial_binsize)
                else:
                    yield str(g), g.order_hist

        def compact(hist, binsize):
            while True:
                hist = self.compact_order_hist(
                    binsize=binsize[0], preserve_upto=preserve_upto, max_histsize=max_histsize, hist=hist
                )
                if 0 in hist:
                    del hist[0]
                    binsize[0] = max(1, int(binsize[0] ** 1 / 2))
                    break
                if len(hist) <= max_histsize or binsize[0] > len(hist) // 2:
                    binsize[0] = max(1, int(binsize[0] ** 1 / 2))
                    break
                binsize[0] *= 2
            return hist

        binsizea = [initial_binsize]
        binsizeb = [initial_binsize]

        def mul(tupa, tupb):
            ga, hista = tupa
            gb, histb = tupb
            hista = compact(hista, binsizea)
            histb = compact(histb, binsizeb)
            hist = self.order_hist_mul(hista, histb)
            prod = f"{ga}*{gb}"
            print(
                "Pi:",
                self._pi_core(hist),
                f"\tHist size: {len(hist)}\t",
                show_timestamp and datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
                f"\t{prod}",
                binsizea,
                binsizeb,
                flush=True,
            )
            return prod, hist

        return dict(sorted(reduce(mul, hists())[1].items()))

Ancestors

Static methods

def order_hist_mul(hista, histb)

Histogram of element orders for a product of 2 groups.

Usage:

>>> from garoupa.algebra.dihedral import D
>>> Product.order_hist_mul(D(5).order_hist, D(7).order_hist)
{1: 1, 2: 47, 5: 4, 7: 6, 10: 28, 14: 30, 35: 24}

Based on Gabriel Dalforno code.

Instance variables

prop comm_degree
Expand source code
@property
def comm_degree(self):
    comms = [g.comm_degree for g in self.groups]
    if any(comm is None for comm in comms):
        return
    return reduce(operator.mul, comms)
prop order_hist
Expand source code
@property
def order_hist(self):
    """Sorted histogram of element orders.

    Usage:

    >>> from garoupa.algebra.dihedral import D
    >>> (D(3) * D(5) * D(7)).order_hist
    {1: 1, 2: 191, 3: 2, 5: 4, 6: 94, 7: 6, 10: 124, 14: 138, 15: 8, 21: 12, 30: 56, 35: 24, 42: 60, 70: 72, 105: 48}
    """
    if self._order_hist is None:
        self._order_hist = dict(sorted(reduce(self.order_hist_mul, (G.order_hist for G in self.groups)).items()))
    return self._order_hist

Sorted histogram of element orders.

Usage:

>>> from garoupa.algebra.dihedral import D
>>> (D(3) * D(5) * D(7)).order_hist
{1: 1, 2: 191, 3: 2, 5: 4, 6: 94, 7: 6, 10: 124, 14: 138, 15: 8, 21: 12, 30: 56, 35: 24, 42: 60, 70: 72, 105: 48}

Methods

def compact_order_hist_lowmem(self, max_histsize, preserve_upto, initial_binsize=1, show_timestamp=True)
Expand source code
def compact_order_hist_lowmem(self, max_histsize, preserve_upto, initial_binsize=1, show_timestamp=True):
    """Memory-friendly histogram of element orders in a direct product

    Compact largest intermediate histograms during calculation to avoid memory exhaustion.
    Nested products will also be processed through this method.
    Final and temporary hist may exceed max_histsize by a factor of 2 at most.

    Usage:

    >>> from garoupa.algebra.dihedral import D
    >>> Product.order_hist_mul(D(7).order_hist, D(19).order_hist)
    {1: 1, 2: 159, 7: 6, 14: 114, 19: 18, 38: 126, 133: 108}
    >>> G = D(3) * D(5) * D(7) * D(9)
    >>> G.compact_order_hist(binsize=20)
    {9: 7936, 29: 1064, 42: 1176, 69: 1044, 90: 1080, 105: 192, 126: 1188, 210: 576, 315: 432, 630: 432}
    >>> G.compact_order_hist_lowmem(max_histsize=5, preserve_upto=0, show_timestamp=False)  # doctest: +NORMALIZE_WHITESPACE
    Pi: 0.28944444444444445         Hist size: 7     False  D3*D5 [1] [1]
    Pi: 0.1997278911564626  Hist size: 10    False  D3*D5*D7 [2] [1]
    Pi: 0.09817901234567901         Hist size: 12    False  D3*D5*D7*D9 [4] [1]
    {3: 1134, 6: 3402, 9: 2268, 10: 2020, 30: 1076, 35: 84, 70: 1476, 90: 1548, 105: 312, 210: 576, 315: 792, 630: 432}
    >>> G.compact_order_hist_lowmem(max_histsize=5, preserve_upto=10, show_timestamp=False)  # doctest: +NORMALIZE_WHITESPACE
    Pi: 0.28944444444444445         Hist size: 7     False  D3*D5 [1] [1]
    Pi: 0.17061791383219957         Hist size: 15    False  D3*D5*D7 [2] [1]
    Pi: 0.11385896951373144         Hist size: 24    False  D3*D5*D7*D9 [4] [1]
    {1: 1, 2: 1919, 3: 8, 5: 4, 6: 1600, 9: 18, 10: 36, 12: 3240, 15: 8, 18: 1746, 21: 36, 30: 672, 35: 24, 36: 1620, 42: 828, 45: 24, 63: 72, 70: 936, 90: 336, 105: 192, 126: 360, 210: 576, 315: 432, 630: 432}
    """

    # if max_histsize <= preserve_upto:  errado
    #     raise Exception(f"Cannot preserve up to order {preserve_upto} limited by {max_histsize} at the same time.")

    def hists():
        for g in self.groups:
            if isinstance(g, Product):
                yield str(g), g.compact_order_hist_lowmem(max_histsize, preserve_upto, initial_binsize)
            else:
                yield str(g), g.order_hist

    def compact(hist, binsize):
        while True:
            hist = self.compact_order_hist(
                binsize=binsize[0], preserve_upto=preserve_upto, max_histsize=max_histsize, hist=hist
            )
            if 0 in hist:
                del hist[0]
                binsize[0] = max(1, int(binsize[0] ** 1 / 2))
                break
            if len(hist) <= max_histsize or binsize[0] > len(hist) // 2:
                binsize[0] = max(1, int(binsize[0] ** 1 / 2))
                break
            binsize[0] *= 2
        return hist

    binsizea = [initial_binsize]
    binsizeb = [initial_binsize]

    def mul(tupa, tupb):
        ga, hista = tupa
        gb, histb = tupb
        hista = compact(hista, binsizea)
        histb = compact(histb, binsizeb)
        hist = self.order_hist_mul(hista, histb)
        prod = f"{ga}*{gb}"
        print(
            "Pi:",
            self._pi_core(hist),
            f"\tHist size: {len(hist)}\t",
            show_timestamp and datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
            f"\t{prod}",
            binsizea,
            binsizeb,
            flush=True,
        )
        return prod, hist

    return dict(sorted(reduce(mul, hists())[1].items()))

Memory-friendly histogram of element orders in a direct product

Compact largest intermediate histograms during calculation to avoid memory exhaustion. Nested products will also be processed through this method. Final and temporary hist may exceed max_histsize by a factor of 2 at most.

Usage:

>>> from garoupa.algebra.dihedral import D
>>> Product.order_hist_mul(D(7).order_hist, D(19).order_hist)
{1: 1, 2: 159, 7: 6, 14: 114, 19: 18, 38: 126, 133: 108}
>>> G = D(3) * D(5) * D(7) * D(9)
>>> G.compact_order_hist(binsize=20)
{9: 7936, 29: 1064, 42: 1176, 69: 1044, 90: 1080, 105: 192, 126: 1188, 210: 576, 315: 432, 630: 432}
>>> G.compact_order_hist_lowmem(max_histsize=5, preserve_upto=0, show_timestamp=False)  # doctest: +NORMALIZE_WHITESPACE
Pi: 0.28944444444444445         Hist size: 7     False  D3*D5 [1] [1]
Pi: 0.1997278911564626  Hist size: 10    False  D3*D5*D7 [2] [1]
Pi: 0.09817901234567901         Hist size: 12    False  D3*D5*D7*D9 [4] [1]
{3: 1134, 6: 3402, 9: 2268, 10: 2020, 30: 1076, 35: 84, 70: 1476, 90: 1548, 105: 312, 210: 576, 315: 792, 630: 432}
>>> G.compact_order_hist_lowmem(max_histsize=5, preserve_upto=10, show_timestamp=False)  # doctest: +NORMALIZE_WHITESPACE
Pi: 0.28944444444444445         Hist size: 7     False  D3*D5 [1] [1]
Pi: 0.17061791383219957         Hist size: 15    False  D3*D5*D7 [2] [1]
Pi: 0.11385896951373144         Hist size: 24    False  D3*D5*D7*D9 [4] [1]
{1: 1, 2: 1919, 3: 8, 5: 4, 6: 1600, 9: 18, 10: 36, 12: 3240, 15: 8, 18: 1746, 21: 36, 30: 672, 35: 24, 36: 1620, 42: 828, 45: 24, 63: 72, 70: 936, 90: 336, 105: 192, 126: 360, 210: 576, 315: 432, 630: 432}
def replace(self, *args, **kwargs)
Expand source code
def replace(self, *args, **kwargs):
    dic = {"seed": self.seed}
    dic.update(kwargs)
    groups = args or self.groups
    return self.__class__(*groups, **dic)

Inherited members