Skip to content

Commit

Permalink
naive way lol
Browse files Browse the repository at this point in the history
  • Loading branch information
SHoltzen committed Feb 24, 2019
1 parent 86c7df9 commit f04847f
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 90 deletions.
73 changes: 73 additions & 0 deletions my_graphs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from sage.all import *
import itertools

def findsubsets(S,m):
return set(itertools.combinations(S, m))

### generates a friends and smokers graph with n people
def gen_friends_smokers(n):
g = Graph(sparse=True)
# make n smoker vertices
smokers = [x for x in range(0,n)]
# connect all the smokers
smokeredges = findsubsets(smokers, 2)
# make friends
friends = []
friendedges = []
count = n
for (s1,s2) in findsubsets(smokers, 2):
friends += [count]
friendedges += [(s1, count), (s2, count)]
count += 1

g.add_vertices(smokers)
g.add_vertices(friends)
g.add_edges(friendedges)
g.add_edges(smokeredges)
return g

# generates a graph which is fully-connected in m and connected across n
def gen_pigeonhole(n,m):
g = Graph()
v = []
e = []
# generate vertices
for x in range(0,n):
for y in range(0,m):
v += [x*m + y]

# generate edges
# generate fully connected graph in n
for x in range(0,n):
for y in findsubsets(range(0, m), 2):
e += [(x*m + y[0], x*m + y[1])]

# connect between n and m
for x in range(0,n):
for y in range (0,m):
e += [(x*m + y, ((x + 1) % n)*m + y)]

g.add_vertices(v)
g.add_edges(e)
return g

# generates a complete graph with n vertices with some extra nodes on each
# vertex
def gen_complete_extra(n):
g = Graph()
v = []
e = []
# generate complete graph
for x in range(0,n):
v += [x]
for x in findsubsets(range(0, n), 2):
e += [(x[0], x[1])]

# now generate an extra vertex for each point and add it
for x in range(0,n):
v += [n + x]
e += [(x, n + x)]

g.add_vertices(v)
g.add_edges(e)
return g
202 changes: 114 additions & 88 deletions orbitgen.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from sage.all import *
from my_graphs import *
import cProfile, pstats, StringIO
import itertools
from sage.groups.perm_gps.partn_ref.refinement_graphs import isomorphic

def findsubsets(S,m):
return set(itertools.combinations(S, m))
from collections import deque

### foldrep: applies f to each canonical representative, maintains an
### accumulator
Expand All @@ -14,16 +12,13 @@ def findsubsets(S,m):
### colors[1]: False vertices
### colors[3] is reserved for the algorithm
### fold: apply a fold method to each orbit
def foldrep(graph, colors, acc, f, level=0, debug=False):
def foldrep(graph, colors, acc, f):
# TODO: reduce number of isomorphism calls if possible
acc = f(acc, graph, colors)
if len(colors[0]) >= (len(graph.vertices()) / 2):
return acc
A, orbits = graph.automorphism_group(partition=colors, orbits=True, algorithm="bliss")
# print("%sorbits: %s" % ("\t"*level, orbits))
A, orbits = graph.automorphism_group(partition=colors, orbits=True)
for o in orbits:
# print("")
# print("%scurrent orbit: %s" % ("\t"*level, o))
e = o[0] # pick the first element in the orbit arbitrarily
# check if this orbit is already true
if e in colors[0]:
Expand All @@ -34,14 +29,14 @@ def foldrep(graph, colors, acc, f, level=0, debug=False):

Gpcolor = [colors[0] + [e], [c for c in colors[1] if c != e]]
Gprime, c = graph.canonical_label(partition=Gpcolor,
certificate=True, algorithm="bliss")
certificate=True)

# print("%smap: %s" % ("\t"*level, c))
# c is a map from old vertices to new vertices; we need to invert it
inv_c = {v: k for k, v in c.iteritems()}

# find lexically first vertex whose value is true in the canonical Gprime;
# default to vertex 0
# compute the canonical deletion of Gprime
# find lexically first vertex whose value is true in the canonical
# Gprime; default to vertex 0.
first_t = None
for v in Gprime.vertices(sort=True):
if inv_c[v] in Gpcolor[0]:
Expand All @@ -53,89 +48,111 @@ def foldrep(graph, colors, acc, f, level=0, debug=False):
canoncolor = [[x for x in Gpcolor[0] if x != first_t],
Gpcolor[1],
[first_t]]
# print("%scanon color: %s, Dcolor: %s" % ("\t"*level, canoncolor, Dcolor))
Dcanon = graph.canonical_label(partition=canoncolor, algorithm="bliss")
assert(set(Dcolor[0] + Dcolor[2]) == set(canoncolor[0] + canoncolor[2]) == set(Gpcolor[0]))
Dcanon = graph.canonical_label(partition=canoncolor)
if Dcanon == D:
# print("%saccepted %s" % ("\t"*level,Gpcolor))
acc = foldrep(graph, Gpcolor, acc, f, level=level+1)
# else:
# print("%srejected coloring: %s" % ("\t"*level, Gpcolor))
acc = foldrep(graph, Gpcolor, acc, f)
return acc

def bfs_foldrep(graph, colors, acc, f):
# queue of colorings yet to be considered
queue = deque([colors])
# set of representative graph colorings
reps = set()
while len(queue) != 0:
c = queue.popleft()
# print("----")
# print("Current color: %s" % c)
gcanon, cert = graph.canonical_label(partition=c, certificate=True)
G_immut = Graph(gcanon, immutable=True)

# convert the colors to their coloring in the canonical graph
c_canon = [[], []]
for c1 in c[0]:
c_canon[0].append(cert[c1])
for c1 in c[1]:
c_canon[1].append(cert[c1])

c_canon[0].sort()
c_canon[1].sort()

if (G_immut, (tuple(c_canon[0]), tuple(c_canon[1]))) in reps:
continue
acc = f(acc, graph, c)
reps.add((G_immut, (tuple(c_canon[0]), tuple(c_canon[1]))))
# print("added rep: %s" % reps)

# print(c)
if len(c_canon[0]) + 1 <= len(graph.vertices()) / 2.0:
# if we can add more colors, try to
A, orbits = graph.automorphism_group(partition=c, orbits=True)
# print("orbits: %s" % orbits)
# expand this node and add it to the queue
for o in orbits:
# print("Considering orbit %s" % o)
e = o[0] # pick the first element in the orbit arbitrarily
# check if this orbit is already true
if e in c[0]:
continue
# we know this is an uncolored vertex
newcolor = [c[0] + [e], [x for x in c[1] if x != e]]
queue.append(newcolor)
return acc

### genrep: generates a representative of each orbit class of a graph
def genrep(G):
colors = [[], [i for i in G.vertices()]]
# folding function
def add_vertex(acc, g, color):
# check if inversion is isomorphic to the current graph
if len(color[0]) < len(g.vertices()) / 2.0:
return acc + [color] + [color[::-1]]

# use orbit-stabilizer to see if this is a unique orbit or not
# A, orbits = g.automorphism_group(partition=color, orbits=True)
# check to make sure each orbit has at most a single color
# intersect = False
# print("colors: %s, orbits: %s" % (color, orbits))
# for o in orbits:
# if len(set(o).intersection(set(color[0])).intersection(set(color[1]))) > 0:
# intersect = True
# break

# if not intersect:
# print("non-overlapping orbits: %s, colors: %s" % (orbits, color))
# return acc + [color] + [color[::-1]]
# else:
# # inversion are isomorphic, return one
return acc + [color]
return foldrep(G, colors, [], add_vertex)

### generates a friends and smokers graph with n people
def gen_friends_smokers(n):
g = Graph(sparse=True)
# make n smoker vertices
smokers = [x for x in range(0,n)]
# connect all the smokers
smokeredges = findsubsets(smokers, 2)
# make friends
friends = []
friendedges = []
count = n
for (s1,s2) in findsubsets(smokers, 2):
friends += [count]
friendedges += [(s1, count), (s2, count)]
count += 1

g.add_vertices(smokers)
g.add_vertices(friends)
g.add_edges(friendedges)
g.add_edges(smokeredges)
return g

# generates a graph which is fully-connected in m and connected across n
def gen_pigeonhole(n,m):
g = Graph()
v = []
e = []
# generate vertices
for x in range(0,n):
for y in range(0,m):
v += [x*m + y]

# generate edges
# generate fully connected graph in n
for x in range(0,n):
for y in findsubsets(range(0, m), 2):
e += [(x*m + y[0], x*m + y[1])]

# connect between n and m
for x in range(0,n):
for y in range (0,m):
e += [(x*m + y, ((x + 1) % n)*m + y)]

g.add_vertices(v)
g.add_edges(e)
return g
def genrep_bfs(G):
colors = [[], [i for i in G.vertices()]]
# folding function
def add_vertex(acc, g, color):
if len(color[0]) < len(g.vertices()) / 2.0:
return acc + [color] + [color[::-1]]

return acc + [color]
return bfs_foldrep(G, colors, [], add_vertex)


### partition_function
### computes the partition of a fully symmetric MLN the specified parameter
###
### potential: a function which maps variable truth assignments to
### probabilities. This function *must* be invariant wrt. the automorphism
### group of the graph.
def partition(G, potential):
colors = [[], [i for i in G.vertices()]]
# folding function
def sum_prob(acc, g, color):
# TODO: avoid recomputing these two automorphism groups
g_order = g.automorphism_group().order()
aut_order = g.automorphism_group(partition=color).order()
orbit_sz = g_order / aut_order # yay orbit stabilizer theorem!
if len(color[0]) < len(g.vertices()) / 2.0:
return acc + (orbit_sz * potential(color)) + (orbit_sz * potential(color[::-1]))

return acc + (orbit_sz * potential(color))
return foldrep(G, colors, 0.0, sum_prob)

def bruteforce_partition(G, potential):
# evaluate the potential on every assignment to variables
def recurse(c1, c2):
print(c1)
if len(c1) == 0:
return 0
tot = potential([c1, c2])
for x in c1:
new_c1 = c1[:]
new_c1.remove(x)
tot += recurse(new_c1, c2 + [x])
return tot
return recurse(G.vertices(), [])

# given a graph g, count the number of distinct 2-colorings using Polya's
# theorem.
Expand All @@ -144,16 +161,25 @@ def gen_pigeonhole(n,m):
def count_num_distinct(g):
return g.automorphism_group().cycle_index().expand(2)((1,1))

def main():

def compute_partition():
def partfun(assgn):
return len(assgn[0])
G = gen_complete_extra(3)
print("partition: %d" % (partition(G, partfun)))
print("brute forced: %d" % (bruteforce_partition(G, partfun)))

def find_representatives():
# n=3
# pr = cProfile.Profile()
# pr.enable()

# G = gen_friends_smokers(3)
G = graphs.CycleGraph(7)
# G = graphs.CompleteGraph(10)
# G = graphs.CompleteGraph(4)
G = gen_friends_smokers(10)
# G = graphs.CycleGraph(20)
# G = gen_complete_extra(3)
num_vert = len(G.vertices())
r = genrep(G)
r = genrep_bfs(G)
for x in r:
print(x)
print("Found configurations: %d" % len(r))
Expand All @@ -168,4 +194,4 @@ def main():


if __name__ == "__main__":
main()
find_representatives()
16 changes: 14 additions & 2 deletions test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from sage.all import *
import orbitgen
from my_graphs import *
import unittest

def count_num_generated(G):
colors = [[], [i for i in G.vertices()]]
num_vert = len(G.vertices())
l = orbitgen.genrep(G)
l = orbitgen.genrep_bfs(G)
return len(l)

class TestGen(unittest.TestCase):
Expand Down Expand Up @@ -45,9 +46,20 @@ def test_star(self):
count_num_generated(G))

def test_friends_smokers(self):
G = orbitgen.gen_friends_smokers(3)
G = gen_friends_smokers(3)
self.assertEqual(orbitgen.count_num_distinct(G),
count_num_generated(G))

def test_friends_smokers(self):
G = gen_friends_smokers(4)
self.assertEqual(orbitgen.count_num_distinct(G),
count_num_generated(G))

def test_aug_complete(self):
G = gen_complete_extra(8)
self.assertEqual(orbitgen.count_num_distinct(G),
count_num_generated(G))


if __name__ == '__main__':
unittest.main()

0 comments on commit f04847f

Please sign in to comment.