Module garoupa.algebra.product.product
Expand source code
# Copyright (c) 2021. Davi Pereira dos Santos
#
# Function based on Gabriel Dalforno code:
# order_hist_mul
#
# This file is part of the garoupa project.
# Please respect the license - more about this in the section (*) below.
#
# garoupa 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.
#
# garoupa 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 garoupa. If not, see <http://www.gnu.org/licenses/>.
#
# (*) Removing authorship by any means, e.g. by distribution of derived
# works or verbatim, obfuscated, compiled or rewritten versions of any
# part of this work is a crime and is unethical regarding the effort and
# time spent here.
import operator
from datetime import datetime
from functools import reduce
from itertools import product, cycle
from garoupa.algebra.matrix.group import Group
from garoupa.algebra.product.tuple import Tuple
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()))
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.
Expand source code
@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()))
Instance variables
var 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)
var 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}
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
Methods
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}
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()))
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