def validate_isomorphisms(tmplt, world, candidates, unspec_cover): """ Validate that at least one isomorphism exists""" global n_iterations n_iterations += 1 if len(unspec_cover) == 0: return validate_alldiff_solns(tmplt, world, candidates) unspec_idx = unspec_cover[0] unspec_cands = np.argwhere(candidates[unspec_idx]).flat for cand_idx in unspec_cands: # Make a copy to avoid messing up candidate sets during recursion candidates_copy = candidates.copy() candidates_copy[unspec_idx, :] = uclasm.one_hot( cand_idx, world.n_nodes) # rerun filters after picking an assignment for the next unspec node _, new_world, new_candidates = uclasm.run_filters( tmplt, world, candidates=candidates_copy, filters=uclasm.cheap_filters, init_changed_cands=uclasm.one_hot(unspec_idx, tmplt.n_nodes)) # if any node has no cands due to the current assignment, skip if not new_candidates.any(axis=1).all(): continue if validate_isomorphisms(tmplt, new_world, new_candidates, unspec_cover[1:]): return True return False
def process_fn(tmplt, world, result_queue=None, label=None, count_isomorphisms=False): result = {} result["label"] = label # For identifying results afterwards start_time = default_timer() tmplt, world, candidates = uclasm.run_filters(tmplt, world, candidates=tmplt.is_cand, filters=uclasm.cheap_filters, verbose=False) filter_time = default_timer()-start_time # print("Time taken for filters: {}".format(filter_time)) # filter_times.append(filter_time) result["filter_time"] = filter_time # start_time = default_timer() # from filters.validation_filter import validation_filter # validation_filter(tmplt, world, candidates=candidates, in_signal_only=False, # verbose=False) # print("Time taken for validation: {}".format(default_timer()-start_time)) # validation_times += [default_timer()-start_time] # # tmplt.candidate_sets = {x: set(world.nodes[candidates[idx,:]]) for idx, x in enumerate(tmplt.nodes)} if count_isomorphisms: # # print("Starting isomorphism count") start_time = default_timer() count, n_iterations = uclasm.count_isomorphisms(tmplt, world, candidates=candidates, verbose=False, count_iterations=True) # print("Counted {} isomorphisms in {} seconds".format(count, default_timer()-start_time)) iso_count_time = default_timer() - start_time # iso_counts += [count] # iso_count_times += [default_timer()-start_time] result["n_isomorphisms"] = count result["iso_count_time"] = iso_count_time result["has_iso"] = count > 0 else: start_time = default_timer() from uclasm.counting.has_isomorphism import has_isomorphism has_iso, n_iterations = has_isomorphism(tmplt, world, candidates=candidates, verbose=False, count_iterations=True) # if has_iso: # print("Has isomorphism") # else: # print("No isomorphism") iso_check_time = default_timer() - start_time # print("Isomorphism checked in {} seconds".format(iso_check_time)) # iso_check_times.append(iso_check_time) result["iso_check_time"] = iso_check_time result["has_iso"] = has_iso result["n_iterations"] = n_iterations if result_queue is not None: result_queue.put(result) else: return result
def validate_isomorphisms(tmplt, world, candidates, unspec_cover, marked, in_signal_only, node_to_marked_col_idx): """ Validate that at least one isomorphism exists and unmark it """ if len(unspec_cover) == 0: return validate_alldiff_solns(tmplt, world, candidates, marked, in_signal_only, node_to_marked_col_idx) unspec_idx = unspec_cover[0] unspec_cands = np.argwhere(candidates[unspec_idx]).flat # TODO: is this actually an effective heuristic? Compare with random order # Order unspec_cands to have marked nodes first unspec_cands = sorted(unspec_cands, key=lambda cand_idx: marked[unspec_idx, cand_idx], reverse=True) for cand_idx in unspec_cands: # Make a copy to avoid messing up candidate sets during recursion candidates_copy = candidates.copy() candidates_copy[unspec_idx, :] = uclasm.one_hot( cand_idx, world.n_nodes) # rerun filters after picking an assignment for the next unspec node _, new_world, new_candidates = uclasm.run_filters( tmplt, world, candidates=candidates_copy, filters=uclasm.cheap_filters, init_changed_cands=uclasm.one_hot(unspec_idx, tmplt.n_nodes)) # if any node has no cands due to the current assignment, skip if not new_candidates.any(axis=1).all(): continue if validate_isomorphisms(tmplt, new_world, new_candidates, unspec_cover[1:], marked, in_signal_only, node_to_marked_col_idx): marked_col_idx = node_to_marked_col_idx[world.nodes[cand_idx]] if in_signal_only: # Unmark all pairs for the found candidate marked[:, marked_col_idx] = False else: # Unmark the found pair marked[unspec_idx, marked_col_idx] = False return True return False
def has_isomorphism(tmplt, world, *, candidates=None, verbose=False, count_iterations=False, **kwargs): """ Searches for an isomorphism and returns true if one is found, else returns false """ global n_iterations n_iterations = 0 if candidates is None: tmplt, world, candidates = uclasm.run_filters( tmplt, world, filters=uclasm.all_filters, candidates=np.ones((tmplt.n_nodes, world.n_nodes), dtype=np.bool), **kwargs) # TODO: only recompute unspec_cover when necessary or not at all # Get node cover for unspecified nodes cand_counts = candidates.sum(axis=1) unspec_subgraph = tmplt.subgraph(cand_counts > 1) unspec_cover = uclasm.get_node_cover(unspec_subgraph) unspec_cover = np.array( [tmplt.node_idxs[unspec_subgraph.nodes[idx]] for idx in unspec_cover], dtype=np.int) # TODO: pass arguments as keywords to avoid bugs when changes are made if validate_isomorphisms(tmplt, world, candidates, unspec_cover): if count_iterations: return True, n_iterations return True if count_iterations: return False, n_iterations return False
import uclasm from load_data_combo import tmplt, world tmplt, world, candidates = uclasm.run_filters(tmplt, world, filters=uclasm.all_filters, verbose=True) n_isomorphisms = uclasm.count_isomorphisms(tmplt, world, candidates=candidates, verbose=False) print("\nFound", n_isomorphisms, "isomorphisms")
# link_types = ["R","C","S"] # update_node_candidates(tmplt, world,letter+str(row)+link_types[k], set(world_nodes[k*size*size+(digit-1)*size:k*size*size+(digit-1)*size+size])) # changed_nodes[tmplt.node_idxs[letter+str(row)+link_types[k]]] = True # row += 1 # tmplt.summarize_candidate_sets() # print("Time to create world and template: {}".format(default_timer()-start_time)) # tmplt.candidate_sets = {x: set(world.nodes[tmplt.is_cand[idx,:]]) for idx, x in enumerate(tmplt.nodes)} # display_sudoku2(tmplt, show_cands=False) start_time = default_timer() tmplt, world, candidates = uclasm.run_filters( tmplt, world, candidates=tmplt.is_cand, init_changed_cands=changed_nodes, filters=uclasm.all_filters, verbose=False) print("Time taken for filters: {}".format(default_timer() - start_time)) filter_times += [default_timer() - start_time] start_time = default_timer() from filters.validation_filter import validation_filter validation_filter(tmplt, world, candidates=candidates, in_signal_only=False, verbose=False) print("Time taken for validation: {}".format(default_timer() - start_time))
def validation_filter(tmplt, world, *, candidates=None, in_signal_only=False, verbose=False, **kwargs): """ This filter finds the minimum candidate set for each template node by identifying one isomorphism for each candidate-template node pair in_signal_only: Rather than checking pairs, if this option is True, only check that each candidate participates in at least one signal, ignoring which template node it corresponds to """ if candidates is None: tmplt, world, candidates = uclasm.run_filters( tmplt, world, filters=uclasm.all_filters, candidates=np.ones((tmplt.n_nodes, world.n_nodes), dtype=np.bool), **kwargs) # Start by marking every current candidate-template node pair to be checked # A zero entry here means that we have already checked whether or not the # candidate corresponds to the template node in any signals. marked = candidates.copy() node_to_marked_col_idx = { node: idx for idx, node in enumerate(world.nodes) } while marked.any(): if verbose: print(marked.sum(), "marks remaining") candidates_copy = candidates.copy() # TODO: only recompute unspec_cover when necessary or not at all # Get node cover for unspecified nodes cand_counts = candidates.sum(axis=1) unspec_subgraph = tmplt.subgraph(cand_counts > 1) unspec_cover = uclasm.get_node_cover(unspec_subgraph) unspec_cover = np.array([ tmplt.node_idxs[unspec_subgraph.nodes[idx]] for idx in unspec_cover ], dtype=np.int) # Find a marked template node idx and a cand to pair together # Pick any pair with a mark marked_tmplt_idx, marked_cand_idx = np.argwhere(marked)[0] # unspecified template nodes which have any marks marked_unspecs = marked[unspec_cover].any(axis=1) # If there is a node in the unspec cover with a mark, prioritize it if marked_unspecs.any(): # Pick the first node in the unspec cover that has a mark marked_tmplt_idx = unspec_cover[marked_unspecs][0] # Set a candidate for the marked template node as the marked cand marked_cand_idx = np.argwhere(marked[marked_tmplt_idx])[0, 0] candidates_copy[marked_tmplt_idx, :] = uclasm.one_hot( marked_cand_idx, world.n_nodes) # TODO: pass arguments as keywords to avoid bugs when changes are made if not validate_isomorphisms(tmplt, world, candidates_copy, unspec_cover, marked, in_signal_only, node_to_marked_col_idx): # No valid isomorphisms: remove from is_cand candidates[marked_tmplt_idx, marked_cand_idx] = False # Unmark the pair that was checked marked[marked_tmplt_idx, marked_cand_idx] = False # TODO: run cheap filters to propagate change of candidates # TODO: reduce world to cands elif in_signal_only: # Unmark all pairs for the candidate that was found marked[:, marked_cand_idx] = False else: # Unmark the pair that was found marked[marked_tmplt_idx, marked_cand_idx] = False return tmplt, world, candidates
import sys sys.path.append("..") import uclasm from load_data_combo import tmplt, world uclasm.run_filters(tmplt, world, uclasm.all_filters, verbose=True) n_isomorphisms = uclasm.count_isomorphisms(tmplt, world, verbose=False) print("\nFound", n_isomorphisms, "isomorphisms")
def plot_barcharts(tmplt, world, results_dir, data_name, neighborhood=True, elimination=True, validation=False): """ Plot (1) number of candidates of each template node (2) number of template nodes that each world node is the candidate of """ filters = [uclasm.stats_filter] tmplt, world, candidates = uclasm.run_filters(tmplt, world, filters=filters, verbose=False, reduce_world=True) cands_stats = candidates.sum(axis=1) cinvs_stats = candidates.sum(axis=0) filters.append(uclasm.topology_filter) tmplt, world, candidates = uclasm.run_filters(tmplt, world, candidates=candidates, filters=filters, verbose=False, reduce_world=True) cands_topo = candidates.sum(axis=1) cinvs_topo = candidates.sum(axis=0) if neighborhood: filters.append(uclasm.neighborhood_filter) tmplt, world, candidates = uclasm.run_filters(tmplt, world, candidates=candidates, filters=filters, verbose=False, reduce_world=False) cands_nbhd = candidates.sum(axis=1) cinvs_nbhd = candidates.sum(axis=0) if elimination: tmplt, world, candidates = uclasm.run_filters( tmplt, world, candidates=candidates, filters=uclasm.all_filters, verbose=False, reduce_world=False) cands_elim = candidates.sum(axis=1) cinvs_elim = candidates.sum(axis=0) if validation: tmplt, world, candidates = validation_filter(tmplt, world, candidates=candidates, verbose=False, reduce_world=False) cands_valid = candidates.sum(axis=1) cinvs_valid = candidates.sum(axis=0) # Sort by # of candidates left after elim, topo, stats if validation: order_tmplt = sorted(range(len(tmplt.nodes)), \ key=lambda idx: (cands_valid[idx], cands_elim[idx], cands_nbhd[idx],\ cands_topo[idx], cands_stats[idx])) order_world = sorted(range(len(world.nodes)), \ key=lambda idx: (cinvs_valid[idx], cinvs_elim[idx], cinvs_nbhd[idx],\ cinvs_topo[idx], cinvs_stats[idx])) elif elimination: order_tmplt = sorted(range(len(tmplt.nodes)), \ key=lambda idx: (cands_elim[idx], \ cands_topo[idx], cands_stats[idx])) order_world = sorted(range(len(world.nodes)), \ key=lambda idx: (cinvs_elim[idx], \ cinvs_topo[idx], cinvs_stats[idx])) else: order_tmplt = sorted(range(len(tmplt.nodes)), \ key=lambda idx: (cands_nbhd[idx], \ cands_topo[idx], cands_stats[idx])) order_world = sorted(range(len(world.nodes)), \ key=lambda idx: (cinvs_nbhd[idx], \ cinvs_topo[idx], cinvs_stats[idx])) # Reorganize the candidates cands_stats = np.array([cands_stats[i] for i in order_tmplt]) cands_topo = np.array([cands_topo[i] for i in order_tmplt]) cinvs_stats = np.array([cinvs_stats[i] for i in order_world]) cinvs_topo = np.array([cinvs_topo[i] for i in order_world]) # Keep only the world nodes that are still candidates to at least one # tmplt node after topology filter world_to_keep = np.nonzero(cinvs_topo)[0] cinvs_stats = cinvs_stats[world_to_keep] cinvs_topo = cinvs_topo[world_to_keep] if neighborhood: cands_nbhd = np.array([cands_nbhd[i] for i in order_tmplt]) cinvs_nbhd = np.array([cinvs_nbhd[i] for i in order_world]) cinvs_nbhd = cinvs_nbhd[world_to_keep] if elimination: cands_elim = np.array([cands_elim[i] for i in order_tmplt]) cinvs_elim = np.array([cinvs_elim[i] for i in order_world]) cinvs_elim = cinvs_elim[world_to_keep] if validation: cands_valid = np.array([cands_valid[i] for i in order_tmplt]) cinvs_valid = np.array([cinvs_valid[i] for i in order_world]) cinvs_valid = cinvs_valid[world_to_keep] y1 = np.arange(len(tmplt.nodes)) y2 = np.arange(len(world_to_keep)) plt.rcParams.update({'font.size': 20}) plt.rcParams["font.family"] = "DejaVu Serif" plt.rcParams['figure.figsize'] = (15, 12) _, color1, color2, color3, color4, color5 = sns.color_palette("Blues", n_colors=6) ax1 = plt.subplot(211) # Plot tmplt bars # plt.figure() plt.bar(y1, height=cands_stats, align='center', color=color1, \ alpha=1, width=1, label='After Statistics') plt.bar(y1, height=cands_topo, align='center', color=color2, \ alpha=1, width=1, label='After Topology') if neighborhood: plt.bar(y1, height=cands_nbhd, align='center', color=color3, \ alpha=1, width=1, label='After Neighborhood') if elimination: plt.bar(y1, height=cands_elim, align='center', color=color4, \ alpha=1, width=1, label='After Elimination') if validation: plt.bar(y1, height=cands_valid, align='center', color=color5, \ alpha=1, width=1, label='After Validation') plt.yscale('log') plt.xlabel('Template Node No.', fontsize=20) plt.ylabel('Number of Candidates', fontsize=20) # plt.ylim(0, 2500) # plt.legend(loc='upper center', bbox_to_anchor=(0.5, 1.4), ncol=1, fancybox=True, shadow=True) # plt.legend(loc='center left', bbox_to_anchor=(1, 0.5)) # plt.savefig('{}/{}_tmplt_bars_new.png'.format(results_dir, data_name), bbox_inches='tight') # plt.close() ax2 = plt.subplot(212) # Plot world bars # plt.figure() plt.bar(y2, height=cinvs_stats, align='center', color=color1, \ alpha=1, width=1, label='After Statistics') plt.bar(y2, height=cinvs_topo, align='center',color=color2, \ alpha=1, width=1, label='After Topology') if neighborhood: plt.bar(y2, height=cinvs_nbhd, align='center', color=color3, \ alpha=1, width=1, label='After Neighborhood') if elimination: plt.bar(y2, height=cinvs_elim, align='center', color=color4, \ alpha=1, width=1, label='After Elimination') if validation: plt.bar(y2, height=cinvs_valid, align='center', color=color5, \ alpha=1, width=1,label='After Validation') plt.xlabel('World Node No.', fontsize=20) plt.ylabel('Number of Template Nodes', fontsize=20) plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.2), fancybox=True, shadow=True, ncol=2) plt.ylim(0, 40) # plt.savefig('{}/{}_world_bars_new.png'.format(results_dir, data_name),bbox_inches='tight') plt.savefig('{}/{}_bars.png'.format(results_dir, data_name), bbox_inches='tight') plt.close()