class primitive: # pylint: disable=R0903,C0103 """Namespace for primitive actions and their corresponding models""" alg_formulas = [[a] for a in cube.ACTIONS] actions = alg_formulas models = [ cube.Cube().apply(sequence=a).summarize_effects() for a in actions ]
class expert: # pylint: disable=R0903,C0103 """Namespace for expert macro-actions and their corresponding models""" alg_formulas = [ formula.R_PERMUTATION, formula.SWAP_3_EDGES_FACE, formula.SWAP_3_EDGES_MID, formula.SWAP_3_CORNERS, formula.ORIENT_2_EDGES, formula.ORIENT_2_CORNERS, ] macros = [ variation for f in alg_formulas for variation in formula.variations(f) ] models = [ cube.Cube().apply(sequence=macro).summarize_effects() for macro in macros ]
def load_learned_macros(): """Load the set of learned macro-actions""" results_dir = 'results/macros/cube/' filename = results_dir + 'clean_macros.pickle' try: with open(filename, 'rb') as file: _macros = pickle.load(file) except FileNotFoundError: warnings.warn( 'Failed to load learned macros from file {}'.format(filename)) _macros = [] _models = [ cube.Cube().apply(sequence=macro).summarize_effects() for macro in _macros ] global learned # pylint: disable=W0601,C0103 learned.macros = _macros learned.models = _models
def generate_random_macro_set(seed): """Generate a new set of random macro-actions using the given random seed""" old_state = pyrandom.getstate() pyrandom.seed(seed) random_formulas = [ formula.random_formula(len(alg)) for alg in expert.alg_formulas ] pyrandom.setstate(old_state) _macros = [ variation for formula_ in random_formulas for variation in formula.variations(formula.simplify(formula_)) ] _models = [ cube.Cube().apply(sequence=macro).summarize_effects() for macro in _macros ] global random # pylint: disable=W0601,C0103 random.seed = seed random.alg_formulas = random_formulas random.macros = _macros random.models = _models
def test(): """Test search functionality with Cube primitive actions and expert macro-actions""" # Set up the scramble newcube = cube.Cube() # Primitive action search print('Running primitive action search...') start = copy.deepcopy(newcube) start.apply(pattern.SCRAMBLE_1) macros_ = macros.primitive.actions models = macros.primitive.models is_goal = lambda node: node.state == newcube heuristic = lambda cube: len(cube.summarize_effects()) max_transitions = 3e3 def get_successors(cube_): return [(copy.deepcopy(cube_).apply(swap_list=model), macro) for (macro, model) in zip(macros_, models)] search_results = search.astar(start=start, is_goal=is_goal, step_cost=len, heuristic=heuristic, get_successors=get_successors, max_transitions=max_transitions) actions = search_results[1] testcube = copy.deepcopy(newcube) testcube.apply(pattern.SCRAMBLE_1) n_starting_errors = len(testcube.summarize_effects()) for action in actions: testcube.apply(action) n_remaining_errors_primitive = len(testcube.summarize_effects()) assert n_remaining_errors_primitive < n_starting_errors # Expert macro-action search print('Running expert macro-action search...') start = copy.deepcopy(newcube) start.apply(pattern.SCRAMBLE_1) macros_ = macros.primitive.actions + macros.expert.macros models = macros.primitive.models + macros.expert.models search_results = search.astar(start=start, is_goal=is_goal, step_cost=lambda _: 1, heuristic=heuristic, get_successors=get_successors, max_transitions=max_transitions) actions = search_results[1] testcube = copy.deepcopy(newcube) testcube.apply(pattern.SCRAMBLE_1) n_starting_errors = len(testcube.summarize_effects()) for action in actions: testcube.apply(action) n_remaining_errors_expert = len(testcube.summarize_effects()) assert n_remaining_errors_expert < n_remaining_errors_primitive print('All tests passed.')
def solve(): """Instantiate a Rubik's cube and solve with the specified macro-actions and search algorithm""" args = parse_args() # Set up the scramble if args.buchner2018: scramble = pattern.buchner2018pattern(args.seed) else: scramble = pattern.scramble(args.seed) start = cube.Cube() start.apply(sequence=scramble) print('Using scramble: {:03d}'.format(args.seed)) print(' '.join(scramble)) start.render() if args.random_goal: goal = cube.Cube().apply(sequence=pattern.scramble(args.seed + 1000)) print('Using goal pattern: {:03d}'.format(args.seed + 1000)) print(' '.join(pattern.scramble(args.seed + 1000))) else: goal = cube.Cube() # Define the macros and models if args.macro_type == 'random': macros.generate_random_macro_set(args.seed) macro_namespace = { 'primitive': SimpleNamespace(macros=[], models=[]), 'expert': macros.expert, 'random': macros.random, 'learned': macros.learned, }[args.macro_type] macro_list = macros.primitive.actions + macro_namespace.macros model_list = macros.primitive.models + macro_namespace.models # Set up the search problem search_fn = { 'astar': search.astar, 'gbfs': search.gbfs, 'weighted_astar': search.weighted_astar, 'bfws_r0': bfws.bfws, 'bfws_rg': bfws.bfws, }[args.search_alg] def get_successors(cube_): return [(copy.deepcopy(cube_).apply(swap_list=model), macro) for (macro, model) in zip(macro_list, model_list)] search_dict = { 'start': start, 'is_goal': lambda node: node.state == goal, 'step_cost': lambda macro: len(macro) if args.cost_mode == 'per-action' else 1, 'heuristic': lambda cube_: len(cube_.summarize_effects(baseline=goal)), 'get_successors': get_successors, 'max_transitions': args.max_transitions, } if args.search_alg == 'weighted_astar': assert (args.g_weight is not None and args.h_weight is not None), 'Must specify weights if using weighted A*.' gh_weights = (args.g_weight, args.h_weight) search_dict['gh_weights'] = gh_weights if 'bfws' in args.search_alg: search_dict['precision'] = args.bfws_precision if args.search_alg == 'bfws_rg': goal_fns = [(lambda x, i=i: x.state[i] == goal[i]) for i, _ in enumerate(goal)] relevant_atoms = iw.iw(1, start, get_successors, goal_fns) if not relevant_atoms: relevant_atoms = iw.iw(2, start, get_successors, goal_fns) if not relevant_atoms: relevant_atoms = start.all_atoms() search_dict['R'] = relevant_atoms #%% Run the search search_results = search_fn(**search_dict) #%% Save the results tag = args.macro_type if args.random_goal: tag = 'random_goal/' + tag else: tag = 'default_goal/' + tag if args.search_alg == 'weighted_astar': args.search_alg += '-g_{}-h_{}'.format(*gh_weights) problem_name = 'cube' if not args.buchner2018 else 'cube-buchner2018' results_dir = 'results/{}/{}/{}/'.format(problem_name, args.search_alg, tag) os.makedirs(results_dir, exist_ok=True) with open(results_dir + '/seed-{:03d}.pickle'.format(args.seed), 'wb') as file: pickle.dump(search_results, file)
def load_and_plot_macros(): """Load and plot Rubik's cube macros""" plt.rcParams.update({'font.size': 12}) _, ax = plt.subplots(figsize=(4,3)) macro_names = [ 'Random', 'Primitive', 'Focused', 'Expert', ] macros.generate_random_macro_set(14) macro_types = [ macros.random, macros.primitive, macros.learned, macros.expert, ] marker_styles = [ '^', 'o', '+', 'x', ] blue, orange, green, red, purple, brown, pink, gray, yellow, teal = sns.color_palette('deep', n_colors=10) colors = [ teal, # random blue, # primitive red, # focused orange, # expert ] for i, macro_type in enumerate(macro_types): if macro_names[i] == 'Random': for j in range(1,11): macros.generate_random_macro_set(j) lengths = [len(macro) for macro in macros.random.macros] effects = [len(cube.Cube().apply(swap_list=model).summarize_effects()) for model in macros.random.models] plt.scatter(lengths, effects, c=colors[i], marker='^', facecolor='none', s=75, linewidths=1) macros.generate_random_macro_set(0) try: lengths = [len(macro) for macro in macro_type.macros] except AttributeError: lengths = [len(a) for a in macro_type.actions] effects = [len(cube.Cube().apply(swap_list=model).summarize_effects()) for model in macro_type.models] label = macro_names[i] + ' Macros' if macro_names[i] != 'Primitive' else 'Base Actions' # if macro_names[i] == 'primitive': # jx, jy = None, None # else: # jx, jy = 0.05, 0.25 jx, jy = None, None plt.scatter(jitter(lengths,jx), jitter(effects,jy), c=colors[i], marker=marker_styles[i], facecolor='none', s=75, label=label, linewidths=1) plt.hlines(48, 0, 26, linestyles='dashed', linewidths=1) handles, labels = ax.get_legend_handles_labels() # handles = [handles[0], handles[1], handles[2], handles[3]] # labels = [labels[0], labels[1], labels[2], labels[3]] leg = plt.legend(handles, labels) leg.set_draggable(True) plt.ylim([0,50]) plt.xlim([0,26]) plt.xticks(range(1,26,4)) plt.xlabel('Length of Action Sequence') plt.ylabel('Stickers Modified') plt.tight_layout() plt.subplots_adjust(top = .96, bottom = .19, right = .95, left = 0.14, hspace = 0, wspace = 0) os.makedirs('results/plots/cube-buchner2018/', exist_ok=True) # plt.savefig('results/plots/cube-buchner2018/cube_entanglement.png') plt.show()
class CPUTimer: def __enter__(self): self.start = time.time() self.end = self.start self.duration = 0.0 return self def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: self.end = time.time() self.duration = self.end - self.start n_steps = 1000 with CPUTimer() as timer: puzzle = cube.Cube() puzzle.scramble(length=n_steps) print('Cube FPS:', n_steps / timer.duration) with CPUTimer() as timer: puzzle = npuzzle.NPuzzle() puzzle.scramble(n_steps=n_steps) print('15-puzzle FPS:', n_steps / timer.duration) with CPUTimer() as timer: env_name = "hanoi_operator_actions" problem_index = 1 render = False verbose = False seed = 0 env = gym.make("PDDLEnv{}-v0".format(env_name.capitalize()))
def load_and_plot_macros(): """Load and plot Rubik's cube macros""" plt.rcParams.update({'font.size': 20}) _, ax = plt.subplots(figsize=(8,6)) macro_names = [ 'random', 'primitive', 'focused', 'expert', ] macros.generate_random_macro_set(14) macro_types = [ macros.random, macros.primitive, macros.learned, macros.expert, ] marker_styles = [ '^', 'o', '+', 'x', ] colors = [ 'C2',#r 'C0',#p 'C3',#l 'C1',#e ] for i, macro_type in enumerate(macro_types): if macro_names[i] == 'random': for j in range(1,11): macros.generate_random_macro_set(j) lengths = [len(macro) for macro in macros.random.macros] effects = [len(cube.Cube().apply(swap_list=model).summarize_effects()) for model in macros.random.models] plt.scatter(lengths, effects, c=colors[i], marker='^', facecolor='none', s=150, linewidths=1) macros.generate_random_macro_set(0) try: lengths = [len(macro) for macro in macro_type.macros] except AttributeError: lengths = [len(a) for a in macro_type.actions] effects = [len(cube.Cube().apply(swap_list=model).summarize_effects()) for model in macro_type.models] label = macro_names[i] # if macro_names[i] == 'primitive': # jx, jy = None, None # else: # jx, jy = 0.05, 0.25 jx, jy = None, None plt.scatter(jitter(lengths,jx), jitter(effects,jy), c=colors[i], marker=marker_styles[i], facecolor='none', s=150, label=label, linewidths=1) plt.hlines(48, 0, 26, linestyles='dashed', linewidths=2) handles, labels = ax.get_legend_handles_labels() # handles = [handles[0], handles[1], handles[2], handles[3]] # labels = [labels[0], labels[1], labels[2], labels[3]] plt.legend(handles, labels) plt.ylim([0,50]) plt.xlim([0,26]) plt.xticks(range(1,26,4)) plt.xlabel('Macro-action length') plt.ylabel('Effect size') plt.tight_layout() plt.subplots_adjust(top = .96, bottom = .12, right = .95, left = 0.11, hspace = 0, wspace = 0) os.makedirs('results/plots/cube-buchner2018/', exist_ok=True) plt.savefig('results/plots/cube-buchner2018/cube_entanglement.png') plt.show()