def _test_super_graph(self, super_graph, proto, automorphism_generators): assert not super_graph.directed() assert not proto.directed() # graph properties ag = mp.ArchUniformSuperGraph(super_graph, proto) self.assertEqual(ag.num_processors(), proto.num_processors() * super_graph.num_processors()) self.assertEqual(ag.num_channels(), proto.num_channels() * super_graph.num_processors() + \ super_graph.num_channels() * (proto.num_processors()**2)) # automorphism properties ag_automs = ag.automorphisms() expected_automs = mp.PermGroup([ mp.Perm(ag.num_processors(), gen) for gen in automorphism_generators ]) self.assertEqual(ag_automs, expected_automs) self.assertEqual(ag_automs.degree(), ag.num_processors()) self.assertEqual(len(ag_automs), ag.num_automorphisms()) # representatives ag_automs_graph = mp.ArchGraphAutomorphisms(ag_automs) id_mapping = tuple(i for i in range(min(ag.num_processors(), 5))) for mapping in permutations(id_mapping): self.assertEqual(ag.representative(mapping), ag_automs_graph.representative(mapping))
def test_orbit(self): for orbit in [self.ag_orbit1, self.ag_orbit2]: self.assertCountEqual(list(self.ag.orbit(orbit[0])), orbit) def orbit_len(orb): return sum(1 for _ in orb) for n in range(3, 7): Sn = mp.PermGroup.symmetric(n) ag = mp.ArchGraphAutomorphisms(Sn) self.assertEqual(orbit_len(ag.orbit(range(n))), factorial(n))
def calculate_platform_symmetries(cfg): """Calculate the Automorphism Group of a Platform Graph This task expects three hydra parameters to be available. **Hydra Parameters**: * **platform:** the input platform. The task expects a configuration dict that can be instantiated to a :class:`~mocasin.common.platform.Platform` object. * **out:** the output file (extension will be added) * **mpsym:** a boolean value selecting mpsym as backend (and JSON as output) Otherwise it outputs plaintext from the python implementation. """ platform = hydra.utils.instantiate(cfg["platform"]) log.info("start converting platform to edge graph for automorphisms.") plat_graph = platform.to_adjacency_dict(include_proc_type_labels=True) use_mpsym = cfg["mpsym"] ( adjacency_dict, num_vertices, coloring, nodes_correspondence, ) = aut.to_labeled_edge_graph(plat_graph) log.info("done converting platform to edge graph for automorphisms.") # print(nodes_correspondence) # print(coloring) # print(len(coloring)) # print(str(edge_graph)) log.info( "start calculating the automorphism group of the (edge) graph with " + str(num_vertices) + " nodes using nauty.") nautygraph = pynauty.Graph(num_vertices, True, adjacency_dict, coloring) autgrp_edges = pynauty.autgrp(nautygraph) log.info( "done calculating the automorphism group of the (edge) graph using nauty." ) log.info("start coverting automorhpism of edges to nodes.") autgrp, new_nodes_correspondence = aut.edge_to_node_autgrp( autgrp_edges[0], nodes_correspondence) permutations_lists = map(aut.list_to_tuple_permutation, autgrp) # permutations = map(perm.Permutation,permutations_lists) # permgrp = perm.PermutationGroup(list(permutations)) # print(permgrp.point_orbit(0)) log.info("done coverting automorhpism of edges to nodes.") log.info("start writing to file.") if use_mpsym: try: mpsym except NameError: log.error( "Configured for mpsym output but could not load mpsym. Fallback to python implementation" ) use_mpsym = False if use_mpsym: out_filename = str(cfg["out_file"]) mpsym_autgrp = mpsym.ArchGraphAutomorphisms( [mpsym.Perm(g) for g in autgrp]) json_out = mpsym_autgrp.to_json() with open(out_filename, "w") as f: f.write(json_out) else: out_filename = cfg["out_file"] with open(out_filename, "w") as f: f.write("Platform Graph:") f.write(str(plat_graph)) # f.write("Edge Group with ~" + str(autgrp_edges[1]) + " * 10^" + str(autgrp_edges[2]) + " elements.\n") f.write("Symmetry group generators:") f.write(str(list(permutations_lists))) f.write("\nCorrespondence:") f.write(str(new_nodes_correspondence)) log.info("done writing to file.")
def __init__( self, graph, platform, channels=False, periodic_boundary_conditions=False, norm_p=2, canonical_operations=True, disable_mpsym=False, disable_symmetries_test=False, ): self._topologyGraph = platform.to_adjacency_dict( include_proc_type_labels=True) self.graph = graph self.platform = platform self._d = len(graph.processes()) init_app_ncs(self, graph) self._arch_nc_inv = {} self.channels = channels self.boundary_conditions = periodic_boundary_conditions self.p = norm_p com_mapper = ComFullMapper(graph, platform) self.list_mapper = ProcPartialMapper(graph, platform, com_mapper) self.canonical_operations = canonical_operations n = len(self.platform.processors()) correct = None if disable_mpsym: self.sym_library = False else: try: mpsym except NameError: self.sym_library = False else: self.sym_library = True if hasattr(platform, "ag"): self._ag = platform.ag log.info( "Symmetries initialized with mpsym: Platform Generator." ) elif hasattr(platform, "ag_json"): if exists(platform.ag_json): self._ag = mpsym.ArchGraphSystem.from_json_file( platform.ag_json) if disable_symmetries_test: log.warning( "Using symmetries JSON without testing.") correct = True else: try: correct = checkSymmetries( platform.to_adjacency_dict(), self._ag.automorphisms(), ) except Exception as e: log.warning( "An unknown error occurred while reading " "the embedding JSON file. Did you provide " "the correct file for the given platform? " f"({e})") correct = False if not correct: log.warning( "Symmetries json does not fit platform.") del self._ag else: log.info( "Symmetries initialized with mpsym: JSON file." ) else: log.warning( "Invalid symmetries JSON path (file does not exist)." ) if not hasattr(self, "_ag"): # only calculate this if not already present log.info("No pre-comupted mpsym symmetry group available." " Initalizing architecture graph...") ( adjacency_dict, num_vertices, coloring, self._arch_nc, ) = to_labeled_edge_graph(self._topologyGraph) nautygraph = pynauty.Graph(num_vertices, True, adjacency_dict, coloring) log.info("Architecture graph initialized. Calculating " "automorphism group using Nauty...") autgrp_edges = pynauty.autgrp(nautygraph) autgrp, _ = edge_to_node_autgrp(autgrp_edges[0], self._arch_nc) self._ag = mpsym.ArchGraphAutomorphisms( [mpsym.Perm(g) for g in autgrp]) for node in self._arch_nc: self._arch_nc_inv[self._arch_nc[node]] = node # TODO: ensure that nodes_correspondence fits simpleVec if not self.sym_library: log.info( "Using python symmetries: Initalizing architecture graph...") ( adjacency_dict, num_vertices, coloring, self._arch_nc, ) = to_labeled_edge_graph(self._topologyGraph) nautygraph = pynauty.Graph(num_vertices, True, adjacency_dict, coloring) log.info("Architecture graph initialized. Calculating " "automorphism group using Nauty...") autgrp_edges = pynauty.autgrp(nautygraph) autgrp, _ = edge_to_node_autgrp(autgrp_edges[0], self._arch_nc) permutations_lists = map(list_to_tuple_permutation, autgrp) permutations = [ Permutation.fromLists(p, n=n) for p in permutations_lists ] self._G = PermutationGroup(permutations) log.info("Initialized automorphism group with internal symmetries")