def mz_init(prestate): prestate.adjacency_directions_rc = { i: CoordRC(a.y, a.x) for i, a in prestate.adjacency_directions.items() } prestate = wfc.wfc_utilities.find_pattern_center(prestate) wfc_state = types.SimpleNamespace(wfc_ns=prestate) wfc_state.result = None wfc_state.adjacency_relations = adjacency_extraction_consistent( wfc_state.wfc_ns, wfc_state.wfc_ns.patterns) wfc_state.patterns = np.array(list( wfc_state.wfc_ns.pattern_catalog.keys())) wfc_state.pattern_translations = list( wfc_state.wfc_ns.pattern_catalog.values()) wfc_state.number_of_patterns = wfc_state.patterns.size wfc_state.number_of_directions = len(wfc_state.wfc_ns.adjacency_directions) wfc_state.propagator_matrix = np.zeros( ( wfc_state.number_of_directions, wfc_state.number_of_patterns, wfc_state.number_of_patterns, ), dtype=np.bool_, ) for d, p1, p2 in wfc_state.adjacency_relations: wfc_state.propagator_matrix[(d, p1, p2)] = True wfc_state.mz_dzn = { "w": wfc_state.wfc_ns.generated_size[0], "h": wfc_state.wfc_ns.generated_size[1], "pattern_count": wfc_state.number_of_patterns, "direction_count": wfc_state.number_of_directions, "adjacency_count": len(wfc_state.adjacency_relations), "pattern_names": list(wfc_state.patterns, zip(range(wfc_state.number_of_patterns))), "relation_matrix": calculate_adjacency_grid(wfc_state.wfc_ns.generated_size, wfc_state.wfc_ns.adjacency_directions), "adjaceny_table": [], "adjacency_matrix": [], }
def find_random_unresolved(wfc_state, random_variation): noise_level = 1e-6 entropy_map = random_variation * noise_level entropy_map = entropy_map.flatten() + wfc_state.entropies.flatten() minimum_cell = np.argmin(entropy_map) if np.any(np.count_nonzero(wfc_state.wave_table, axis=2) == 0): WFC_LOGGER.warning("Solver FAIL") WFC_LOGGER.debug( f"previous decisions: {len(wfc_state.previous_decisions)}") return WFC_FAILURE if np.count_nonzero(wfc_state.wave_table, axis=2).flatten()[minimum_cell] == 0: WFC_LOGGER.debug(f"previous decisions: {wfc_state}") return WFC_FAILURE return None higher_than_threshold = np.ma.MaskedArray( entropy_map, np.count_nonzero(wfc_state.wave_table, axis=2).flatten() <= 1) minimum_cell = higher_than_threshold.argmin(fill_value=999999.9) maximum_cell = higher_than_threshold.argmax(fill_value=0.0) chosen_cell = maximum_cell if np.any(np.count_nonzero(wfc_state.wave_table, axis=2) == 0): WFC_LOGGER.debug("A zero-state node has been found.") if wfc_parameters.overflow_check: if np.any(np.count_nonzero(wfc_state.wave_table, axis=2) > 65534): WFC_LOGGER.error("Overflow A") WFC_LOGGER.error(np.count_nonzero(wfc_state.wave_table, axis=2)) pdb.set_trace() assert False if np.all(np.count_nonzero(wfc_state.wave_table, axis=2) == 1): WFC_LOGGER.info("DETECTED FINISH") WFC_LOGGER.info( f"nonzero count: {np.count_nonzero(wfc_state.wave_table, axis=2)}") cell_index = np.unravel_index( chosen_cell, [ np.count_nonzero(wfc_state.wave_table, axis=2).shape[0], np.count_nonzero(wfc_state.wave_table, axis=2).shape[1], ], ) return CoordRC(row=cell_index[0], column=cell_index[1])
def wrap_coords(wfc_parameters, cell_coords): r = (cell_coords.row + wfc_parameters.wfc_ns.generated_size[0]) % ( wfc_parameters.wfc_ns.generated_size[0]) c = (cell_coords.column + wfc_parameters.wfc_ns.generated_size[1]) % ( wfc_parameters.wfc_ns.generated_size[1]) return CoordRC(row=r, column=c)
def wfc_init(prestate): """ Initialize the WFC solver, returning the fixed and mutable data structures needed. """ prestate.adjacency_directions_rc = { i: CoordRC(a.y, a.x) for i, a in prestate.adjacency_directions.items() } # prestate = wfc.wfc_utilities.find_pattern_center(prestate) parameters = types.SimpleNamespace(wfc_ns=prestate) state = types.SimpleNamespace() state.result = None parameters.heuristic = 0 # TODO: Implement control code to choose between heuristics parameters.adjacency_relations = adjacency_extraction_consistent( parameters.wfc_ns, parameters.wfc_ns.patterns) parameters.patterns = np.array( list(parameters.wfc_ns.pattern_catalog.keys())) parameters.pattern_translations = list( parameters.wfc_ns.pattern_catalog.values()) parameters.number_of_patterns = parameters.patterns.size parameters.number_of_directions = len( parameters.wfc_ns.adjacency_directions) # The Propagator is a data structure that holds the adjacency information # for the patterns, i.e. given a direction, which patterns are allowed to # be placed next to the pattern that we're currently concerned with. # This won't change over the course of using the solver, so the important # thing here is fast lookup. parameters.propagator_matrix = np.zeros( (parameters.number_of_directions, parameters.number_of_patterns, parameters.number_of_patterns), dtype=np.bool_) for direction, pattern_one, pattern_two in parameters.adjacency_relations: parameters.propagator_matrix[(direction, pattern_one, pattern_two)] = True output = types.SimpleNamespace() # The Wave Table is the boolean expression table of which patterns are allowed # in which cells of the solution we are calculating. parameters.rows = parameters.wfc_ns.generated_size[0] parameters.columns = parameters.wfc_ns.generated_size[1] output.solving_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.propagation_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) parameters.wave_shape = [ parameters.rows, parameters.columns, parameters.number_of_patterns ] state.wave_table = np.full(parameters.wave_shape, True, dtype=np.bool_) # The compatible_count is a running count of the number of patterns that # are still allowed to be next to this cell in a particular direction. compatible_shape = [ parameters.rows, parameters.columns, parameters.number_of_patterns, parameters.number_of_directions ] WFC_LOGGER.debug(f"compatible shape:{compatible_shape}") state.compatible_count = np.full( compatible_shape, parameters.number_of_patterns, dtype=np.int16) # assumes that there are less than 65536 patterns # The weights are how we manage the probabilities when we choose the next # pattern to place. Rather than recalculating them from scratch each time, # these let us incrementally update their values. state.weights = np.array(list(parameters.wfc_ns.pattern_weights.values())) state.weight_log_weights = np.vectorize(weight_log)(state.weights) state.sum_of_weights = np.sum(state.weights) state.sum_of_weight_log_weights = np.sum(state.weight_log_weights) state.starting_entropy = math.log(state.sum_of_weights) - ( state.sum_of_weight_log_weights / state.sum_of_weights) state.entropies = np.zeros([parameters.rows, parameters.columns], dtype=np.float64) state.sums_of_weights = np.zeros([parameters.rows, parameters.columns], dtype=np.float64) # Instead of updating all of the cells for every propagation, we use a queue # that marks the dirty tiles to update. state.observation_stack = collections.deque() output.output_grid = np.full([parameters.rows, parameters.columns], WFC_NULL_VALUE, dtype=np.int64) output.partial_output_grid = np.full( [parameters.rows, parameters.columns, parameters.number_of_patterns], -9, dtype=np.int64) output.current_iteration_count_observation = 0 output.current_iteration_count_propagation = 0 output.current_iteration_count_last_touch = 0 output.current_iteration_count_crystal = 0 output.solving_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.ones_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.propagation_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.touch_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.crystal_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.method_time = np.full((parameters.rows, parameters.columns), 0, dtype=np.int32) output.choices_recording = np.full((parameters.rows, parameters.columns), 0, dtype=np.float32) output.stats_tracking = prestate.stats_tracking.copy() return parameters, state, output