Skip to content

Commit

Permalink
enhanced flow; added more pruning criterions
Browse files Browse the repository at this point in the history
  • Loading branch information
junipertcy committed Jul 30, 2020
1 parent 1341812 commit f3ad931
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 73 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pip install -r requirements.txt

Now, try:
```python
python is_simplicial.py degs.txt sizes.txt
python is_simplicial.py -k degs.txt -s sizes.txt
```

It should tell you that the joint degree sequence defined by `degs.txt` & `sizes.txt` is simplicial!
Expand Down
137 changes: 69 additions & 68 deletions SimplicialTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ def __init__(self):
self.pointer = 0
self.name2id = dict()
self.id2name = dict()
self.facet_sizes = np.array([], dtype=np.int_)
self.facet_size_per_id = np.array([], dtype=np.int_)

self.logbook = dict()

pass

def register(self, name):
Expand All @@ -24,11 +25,14 @@ def register(self, name):
self.name2id[name] = self.pointer
self.id2name[self.pointer] = name
self.pointer += 1
self.facet_sizes = np.append(self.facet_sizes, [len(name)])
self.facet_size_per_id = np.append(self.facet_size_per_id, [len(name)])
return name, self.name2id[name]

def log_forbidden(self, name):
self.logbook[tuple(name)] = False
def log_forbidden(self, name, reason_id):
self.logbook[tuple(name)] = {
"is_simplicial": False,
"reason": reason_id
}


class SimplicialTest(SimplexRegistrar):
Expand All @@ -44,6 +48,7 @@ class SimplicialTest(SimplexRegistrar):

def __init__(self, degree_list, size_list):
super().__init__()

self.random_seed = time.thread_time_ns()
random.seed(self.random_seed)

Expand All @@ -55,18 +60,16 @@ def __init__(self, degree_list, size_list):
self.n = len(degree_list)

self.sorted_s = sorted(size_list, reverse=True)
self._sorted_s = deepcopy(self.sorted_s)
self.sorted_d = sorted(degree_list, reverse=True)
self._sorted_d = deepcopy(self.sorted_d)
self._sorted_s = np.array(deepcopy(self.sorted_s), dtype=np.int_)
self._sorted_d = np.array(sorted(degree_list, reverse=True), dtype=np.int_)

self.deg_seq = np.zeros(self.n, dtype=np.int_)
self.symmetry_breaker = None
self.identifier = None

self._backtrack_steps = 0
self._global_hit_wall = 0

pass
self._counter = 0
self._len_logbook = 0

def update_deg_seq(self, facet, value):
if value not in [+1, -1]:
Expand All @@ -80,10 +83,9 @@ def checkpoint_1(self):
def _break_symmetry(self):
m = self.sorted_s.pop(0)
picked_facet, picked_facet_id = self.register(random.sample(range(self.n), k=m))

self.update_deg_seq(picked_facet, +1)
identifier = [picked_facet_id]
self.symmetry_breaker = 0 # used to check if the routine ends
self.symmetry_breaker = picked_facet_id # Actually, `picked_facet_id` is always 0 here.
return identifier

def ensure_valid_draw(self, identifier, size):
Expand All @@ -101,10 +103,15 @@ def ensure_valid_draw(self, identifier, size):
"""



pass

@staticmethod
def count_summary(seq):
d = defaultdict(int)
for s in seq:
d[s] += 1
return d

def sample_simplex(self, identifier, size):
"""
Expand All @@ -117,13 +124,20 @@ def sample_simplex(self, identifier, size):
-------
"""
# TODO: Fix things here!
explored_but_forbidden_ids = []
explored_but_forbidden_ids = [k[len(identifier):][0] for k in self.logbook.keys() if
len(k[len(identifier):]) == 1]
larger_selected_simplex_ids = [index for index, i in enumerate(self.facet_sizes) if
(index in identifier and i >= size) or index in explored_but_forbidden_ids]
larger_selected_simplex_ids = [index for index, i in enumerate(self.facet_sizes) if (index in identifier and i >= size)]
# Here, we have a good criterion! We may not need to explore further if...
deg_sequence = np.array(self.compute_joint_seq_from_identifier(identifier)[1])
deg_sequence_goal = self._sorted_d
if np.any(deg_sequence_goal - deg_sequence < 0):
self.log_forbidden(identifier, "NO need to explore further - 1")
self._backtrack_steps = 1
return list(), -1
if len(np.nonzero(deg_sequence_goal - deg_sequence)[0]) < size:
self.log_forbidden(identifier, "NO need to explore further - 2")
self._backtrack_steps = 1
return list(), -1

larger_selected_simplex_ids = [index for index, i in enumerate(self.facet_size_per_id) if
(index in identifier and i >= size)]

set_of_vertices = set(range(self.n))
picked_facet, picked_facet_id = self.register(random.sample(list(set_of_vertices), k=size))
Expand All @@ -132,51 +146,39 @@ def sample_simplex(self, identifier, size):
while not qualified_draw:
qualified_draw = True
for _id in larger_selected_simplex_ids:

if set(picked_facet).issubset(set(self.id2name[_id])):
self.log_forbidden(identifier + [picked_facet_id])
aaa = tuple(list(identifier) + [picked_facet_id])
self.log_forbidden(identifier + [picked_facet_id], 1)
picked_facet, picked_facet_id = self.register(random.sample(list(set_of_vertices), k=size))
qualified_draw = False
_ += 1
break
if _ > 10:
explored = [key for key in self.logbook.keys() if
key[:len(identifier)] == tuple(identifier) and len(key) == len(identifier) + 1]

self._global_hit_wall += 1
if self._global_hit_wall > 10:
self._global_hit_wall = 0
self._backtrack_steps = 2
else:
if len(explored) == comb(self.n, size):
if len(identifier) == 2:
self._backtrack_steps = 1
else:
self._backtrack_steps = 2
return list(), -1

[set_of_vertices.remove(i) for i in picked_facet]
if len(set_of_vertices) < size:
# This is an impossible path
self.log_forbidden(identifier)

self._backtrack_steps = 1
return list(), -1

pool = set()
pool_size = comb(len(set_of_vertices), size)
already_in_identifier = 0
while picked_facet_id in identifier or tuple(identifier + [picked_facet_id]) in self.logbook:
picked_facet, picked_facet_id = self.register(random.sample(list(set_of_vertices), k=size))
pool.add(tuple(identifier + [picked_facet_id]))
if len(pool) == pool_size - already_in_identifier:
if _ > 100: # TODO
self._backtrack_steps = 1
return list(), -1

# print(f"WE HAVE CHOSEN, IN SAMPLE_SIMPLEX, {picked_facet, picked_facet_id}")
return picked_facet, picked_facet_id

def is_simplicial(self):
if max(self.degree_list) > self.m:
print("1. This can never be simplicial.") # TODO.... why??
return False
if max(self.size_list) >= self.n:
print("2. This can not be simplicial.")
return False
if np.sum(self.degree_list) != np.sum(self.size_list):
print("2. This can never be simplicial.")
print("Failing the Gale–Ryser criterion (1957), the sequence is not bigraphic.")
return False
# TODO: there is a second part of the GR criterion, which is not coded yet.

identifier = self._break_symmetry()
if len(self.sorted_s) == 0:
Expand All @@ -186,23 +188,14 @@ def is_simplicial(self):
else:
return False

while True:
while self._counter < 1e4:
if len(self.logbook) == self._len_logbook:
self._counter += 1
s = self.sorted_s.pop(0)
picked_facet, picked_facet_id = self.sample_simplex(identifier, s)
if len(picked_facet) == 0:
if identifier == [self.symmetry_breaker]:
return False
self.sorted_s = [s] + self.sorted_s
unwanted_facet = self.id2name[identifier.pop(-1)]
self.sorted_s = [len(unwanted_facet)] + self.sorted_s
self.update_deg_seq(unwanted_facet, -1)
if self._backtrack_steps == 2:
unwanted_facet = self.id2name[identifier.pop(-1)]
self.sorted_s = [len(unwanted_facet)] + self.sorted_s
self.update_deg_seq(unwanted_facet, -1)
unwanted_facet = self.id2name[identifier.pop(-1)]
self.sorted_s = [len(unwanted_facet)] + self.sorted_s
self.update_deg_seq(unwanted_facet, -1)
self._pull_the_plug(identifier, self._backtrack_steps)
continue

self.update_deg_seq(picked_facet, +1)
Expand All @@ -211,26 +204,34 @@ def is_simplicial(self):
identifier += [picked_facet_id]
else:
# Backtrack
self.log_forbidden(tuple(list(identifier) + [picked_facet_id]))
aaa = tuple(list(identifier) + [picked_facet_id])
self.log_forbidden(tuple(list(identifier) + [picked_facet_id]), 2)
self.update_deg_seq(picked_facet, -1)
self.sorted_s.insert(0, len(self.id2name[picked_facet_id]))
continue

# Here, assuming our algorithm is all good, we want to check if we indeed find the simplicial complex
if len(self.sorted_s) == 0:
if sorted(self.deg_seq, reverse=True) == self._sorted_d: # TODO: start from identifier
if sorted(self.deg_seq, reverse=True) == self._sorted_d.tolist(): # TODO: start from identifier
self.identifier = identifier
return True
else:
# Backtrack
self.log_forbidden(identifier)
last_facet = self.id2name[identifier.pop(-1)]

self.sorted_s = [len(last_facet)] + self.sorted_s
self.update_deg_seq(last_facet, -1)
self.log_forbidden(identifier, 3)
self._pull_the_plug(identifier, 1)
if len(self.logbook) > self._len_logbook:
self._counter = 0
self._len_logbook = len(self.logbook)
return False

def _pull_the_plug(self, identifier, times=1):
if not len(identifier) > times:
raise ValueError("You cannot backtrack more times than the length of the identifier.")

for _ in range(times):
unwanted_facet = self.id2name[identifier.pop(-1)]
self.sorted_s = [len(unwanted_facet)] + self.sorted_s
self.update_deg_seq(unwanted_facet, -1)

def count_explored_branches(self, identifier):
s = set()
for _ in self.logbook.keys():
Expand Down
7 changes: 3 additions & 4 deletions is_simplicial.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@


@click.command()
@click.argument('degree_sequence', type=click.File('r'))
@click.argument('size_sequence', type=click.File('r'))
@click.option('-k', '--degree_seq_file', 'degree_sequence', type=click.File('r'), help='Path to degree sequence file.')
@click.option('-s', '--size_seq_file', 'size_sequence', type=click.File('r'), help='Path to size sequence file.')
def is_simplicial(degree_sequence, size_sequence):
degree_sequence = list(map(int, degree_sequence.read().replace("\n", "").split(" ")))
size_sequence = list(map(int, size_sequence.read().replace("\n", "").split(" ")))
st = SimplicialTest(degree_sequence, size_sequence)
result = st.is_simplicial()
if result is True:
print("Yes, it forms a simplicial complex.")
print(st.identifier2facets(st.identifier))
print(f"Yes, the joint sequence is simplicial. \nThe complex is: {st.identifier2facets(st.identifier)}")
else:
print("No, it cannot form a simplicial complex.")

Expand Down

0 comments on commit f3ad931

Please sign in to comment.