def tree_search(tree, instance_file, return_dict, time_limit, storing_vals): ''' Performs MCTS with time limit (seconds) ''' global m start_time = datetime.datetime.now().strftime("%d.%m.%Y_%H:%M:%S") graph_sols = False graph_root_ucb = False store_stats, store_root_ucb, store_root_wins, store_root_visits, store_every_n, folder_name, filename = storing_vals # GRAPHING plt.ion() plt.show() if graph_sols: plt.xlabel("Nb domain changes") plt.ylabel("Feasible solution") elif graph_root_ucb: plt.xlabel("UCB value") plt.ylabel("Root candidate") start = time.time() nb_dives = 0 # Problem with presolving when not freed before starting m.freeProb() m.readProblem(f"{instance_file}") # Stopped with thread timeout av_rollout_sols = [] all_sols = [] create_cols = True create_dfs = True while True: # Rollout + dive m.optimize() if create_dfs: if not os.path.exists(folder_name): os.makedirs(folder_name) colnames = tree.get_root().candidates create_dfs = False if store_stats: if store_root_ucb: df_root_ucb = pd.DataFrame(columns=colnames) if store_root_wins: df_root_wins = pd.DataFrame(columns=colnames) if store_root_visits: df_root_visits = pd.DataFrame(columns=colnames) # If already optimized if not tree.scip_has_branched: print("Problem already optimized") print(m.getObjVal()) break tree.rollout_tree_nodes.append(tree.curr_node) # Initiate solution sol = None # If feasible, add sol to list if m.getStatus() == 'optimal': sol = m.getObjVal() all_sols.append(sol) tree.rollout_sols[tree.rollout_nb - 1] = sol if sol < tree.curr_best_sol: # Add sol and brancher nb to list for graph tree.best_sol_list.append(sol) tree.nb_lp_solves.append(tree.ndomchgs) tree.curr_best_sol = sol tree.update_graph = True else: tree.increment_to_root(node=tree.curr_node, sol=sol) m.freeProb() m.readProblem(f"{instance_file}") utilities.init_scip_params(m, seed=seed, heuristics=False, presolving=False, separating=False, conflict=False) m.setIntParam('timing/clocktype', 1) # 1: CPU user seconds, 2: wall clock time m.setRealParam('limits/time', time_limit) tree.phase = 'rollout' if tree.created_node: print("CREATED NODE PROBLEM") tree.curr_node = tree.get_root() # print("NOT OPTIMAL") continue # GRAPHING SOL VS DOM CHANGES if graph_sols: if tree.ndomchgs_graph > 500 and tree.update_graph: plt.plot(tree.nb_lp_solves, tree.best_sol_list, 'red') plt.draw() plt.pause(0.001) tree.update_graph = False tree.ndomchgs_graph = 0 # GRAPHING ROOT CHILD UCB elif graph_root_ucb: if random.random() < 0.1: plt.bar(list(range(len(tree.root_node.child_ucb[:25]))), tree.root_node.child_ucb[:25], color='red') plt.draw() plt.title(f"{nb_dives} dives") plt.ylim(min(tree.root_node.child_ucb), max(tree.root_node.child_ucb)) plt.pause(0.001) # Increment nb of runs tree.increment_to_root(node=tree.curr_node, sol=sol) tree.phase = 'rollout' if nb_dives % store_every_n == 0: if store_stats: if store_root_ucb: df_root_ucb.loc[nb_dives] = pd.Series( tree.get_root_ucb(), index=df_root_ucb.columns) utilities.log_stats(df_root_ucb, folder_name, filename, 'ucb', start_time) if store_root_wins: df_root_wins.loc[nb_dives] = pd.Series( tree.get_root().child_nb_wins, index=df_root_wins.columns) utilities.log_stats(df_root_wins, folder_name, filename, 'wins', start_time) if store_root_visits: df_root_visits.loc[nb_dives] = pd.Series( tree.get_root().child_nb_visits, index=df_root_visits.columns) utilities.log_stats(df_root_visits, folder_name, filename, 'visits', start_time) nb_dives += 1 # Add new nodes if tree.rollout_nb == tree.max_rollout_nb: tree.rollout_nb = 1 if tree.adding_new_nodes == 'all': tree.add_all_nodes() elif tree.adding_new_nodes == 'best': tree.add_best_node() elif tree.adding_new_nodes == 'random': tree.add_rand_node() best_sol = min(tree.rollout_sols) av_rollout_sol = sum(tree.rollout_sols) / len(tree.rollout_sols) print( f"{tree.rollout_sols}, Best: {tree.get_best_sol()}, Av Sol: {av_rollout_sol}" ) best_node = tree.rollout_tree_nodes[ tree.rollout_sols.tolist().index(best_sol)] tree.increment_to_root(best_node, wins=True, sol=best_sol) # Reset lists tree.rollout_sols = np.ones(tree.max_rollout_nb) * np.inf tree.rollout_tree_nodes = [] time_elapsed = (time.time() - start) print( f"AV TIME PER DIVE: {round((time_elapsed/nb_dives),2)}, {nb_dives} dives, {utilities.get_mins_left(time_elapsed, time_limit)} left\n" ) tree.start_var = False av_rollout_sols.append(av_rollout_sol) else: # Only for the first n rollouts if nb_dives < tree.max_rollout_nb: tree.start_var = True tree.rollout_nb += 1 faulthandler.enable() # Restart problem m.freeProb() m.readProblem(f"{instance_file}") utilities.init_scip_params(m, seed=seed, heuristics=False, presolving=False, separating=False, conflict=False) m.setIntParam('timing/clocktype', 1) # 1: CPU user seconds, 2: wall clock time m.setRealParam('limits/time', time_limit) tree.curr_node = tree.get_root() return_dict['feas_sols_graph'], return_dict[ 'nb_lp_solves_graph'] = tree.get_graph_vals() return_dict['root_child_ucb'] = tree.get_root_ucb() return_dict['root_nb_wins'], return_dict[ 'root_nb_visits'] = tree.get_root_stats() return_dict['all_feas_sols'] = all_sols
m = scip.Model() # print(a) m.setIntParam('display/verblevel', 0) m.readProblem(f"{instance['path']}") m.includeEventhdlr(SolvingStatsRecorder(brancher.solving_stats), "SolvingStatsRecorder", "") # AGGRESSIVE MODE FOR INTERNAL BRANCHING if policy['type'] == 'internal': utilities.init_scip_params(m, seed=seed, heuristics='agg', presolving=False, separating=False, conflict=False) else: utilities.init_scip_params(m, seed=seed, heuristics=False, presolving=False, separating=False, conflict=False) # if policy['type'] == 'MCTS_coef_diving': # utilities.disable_all_but_coef(m) # elif policy['type'] == 'MCTS_fract_diving': # utilities.disable_all_but_fract(m)
] os.makedirs('results', exist_ok=True) with open(f"results/{result_file}", 'w', newline='') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for instance in instances: print(f"{instance['type']}: {instance['path']}...") for policy in branching_policies: tf.set_random_seed(policy['seed']) m = scip.Model() m.setIntParam('display/verblevel', 0) m.readProblem(f"{instance['path']}") utilities.init_scip_params(m, seed=policy['seed']) m.setIntParam('timing/clocktype', 1) # 1: CPU user seconds, 2: wall clock time m.setRealParam('limits/time', time_limit) brancher = PolicyBranching(policy) m.includeBranchrule(branchrule=brancher, name=f"{policy['type']}:{policy['name']}", desc=f"Custom PySCIPOpt branching policy.", priority=666666, maxdepth=-1, maxbounddist=1) walltime = time.perf_counter() proctime = time.process_time()
def exp_main(args): result_file = f"{args.problem}_{time.strftime('%Y%m%d-%H%M%S')}.csv" instances = [] seeds = [0, 1, 2, 3, 4] # seeds = range(5,20) gcnn_models = ['baseline'] # gcnn_models = [] # other_models = ['extratrees_gcnn_agg', 'lambdamart_khalil', 'svmrank_khalil'] # TODO other_models = [] # internal_branchers = ['relpscost'] internal_branchers = [] time_limit = 3600 if args.problem == 'setcover': # instances += [{'type': 'small', 'path': f"data/instances/setcover/transfer_500r_1000c_0.05d/instance_{i+1}.lp"} for i in range(20)] instances += [{ 'type': 'medium', 'path': f"data/instances/setcover/transfer_1000r_1000c_0.05d/instance_{i+1}.lp" } for i in range(20)] # instances += [{'type': 'big', 'path': f"data/instances/setcover/transfer_2000r_1000c_0.05d/instance_{i+1}.lp"} for i in range(20)] # gcnn_models += ['mean_convolution', 'no_prenorm'] elif args.problem == 'cauctions': instances += [{ 'type': 'small', 'path': f"data/instances/cauctions/transfer_100_500/instance_{i+1}.lp" } for i in range(20)] instances += [{ 'type': 'medium', 'path': f"data/instances/cauctions/transfer_200_1000/instance_{i+1}.lp" } for i in range(20)] instances += [{ 'type': 'big', 'path': f"data/instances/cauctions/transfer_300_1500/instance_{i+1}.lp" } for i in range(20)] elif args.problem == 'facilities': instances += [{ 'type': 'small', 'path': f"data/instances/facilities/transfer_100_100_5/instance_{i+1}.lp" } for i in range(20)] instances += [{ 'type': 'medium', 'path': f"data/instances/facilities/transfer_200_100_5/instance_{i+1}.lp" } for i in range(20)] # instances += [{'type': 'big', 'path': f"data/instances/facilities/transfer_400_100_5/instance_{i+1}.lp"} for i in range(20)] elif args.problem == 'indset': instances += [{ 'type': 'small', 'path': f"data/instances/indset/transfer_500_4/instance_{i+1}.lp" } for i in range(20)] instances += [{ 'type': 'medium', 'path': f"data/instances/indset/transfer_1000_4/instance_{i+1}.lp" } for i in range(20)] # instances += [{'type': 'big', 'path': f"data/instances/indset/transfer_1500_4/instance_{i+1}.lp"} for i in range(20)] else: raise NotImplementedError branching_policies = [] # SCIP internal brancher baselines for brancher in internal_branchers: for seed in seeds: branching_policies.append({ 'type': 'internal', 'name': brancher, 'seed': seed, 'sampling_strategy': NaN, }) # ML baselines for model in other_models: for seed in seeds: branching_policies.append({ 'type': 'ml-competitor', 'name': model, 'seed': seed, 'model': f'trained_models/{args.problem}/{model}/{seed}', }) # GCNN models for sampling_Strategy in args.sampling_strategies: for model in gcnn_models: for seed in seeds: branching_policies.append({ 'type': 'gcnn', 'name': model, 'seed': seed, 'sampling_strategy': sampling_Strategy, 'parameters': f'trained_models/{args.problem}/{sampling_Strategy}/ss{args.sample_seed}/ts{seed}/best_params.pkl' }) print(f"problem: {args.problem}") print(f"gpu: {args.gpu}") print(f"time limit: {time_limit} s") if args.gpu == -1: tf.config.set_visible_devices( tf.config.list_physical_devices('CPU')[0]) else: cpu_devices = tf.config.list_physical_devices('CPU') gpu_devices = tf.config.list_physical_devices('GPU') tf.config.set_visible_devices([cpu_devices[0], gpu_devices[args.gpu]]) tf.config.experimental.set_memory_growth(gpu_devices[args.gpu], True) # load and assign tensorflow models to policies (share models and update parameters) loaded_models = {} for policy in branching_policies: if policy['type'] == 'gcnn': if policy['name'] not in loaded_models: model_module = importlib.import_module(f'models.{model}.model') loaded_models[policy['name']] = model_module.GCNPolicy() policy['model'] = loaded_models[policy['name']] # load ml-competitor models for policy in branching_policies: if policy['type'] == 'ml-competitor': try: with open(f"{policy['model']}/normalization.pkl", 'rb') as f: policy['feat_shift'], policy['feat_scale'] = pickle.load(f) except: policy['feat_shift'], policy['feat_scale'] = 0, 1 with open(f"{policy['model']}/feat_specs.pkl", 'rb') as f: policy['feat_specs'] = pickle.load(f) if policy['name'].startswith('svmrank'): policy['model'] = svmrank.Model().read( f"{policy['model']}/model.txt") else: with open(f"{policy['model']}/model.pkl", 'rb') as f: policy['model'] = pickle.load(f) print("running SCIP...") fieldnames = [ 'policy', 'sampling_strategy', 'seed', 'type', 'instance', 'nnodes', 'nlps', 'stime', 'gap', 'status', 'ndomchgs', 'ncutoffs', 'walltime', 'proctime', ] os.makedirs('results', exist_ok=True) with open(f"results/{result_file}", 'w', newline='') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for instance in instances: print(f"{instance['type']}: {instance['path']}...") for policy in branching_policies: tf.random.set_seed(policy['seed']) m = scip.Model() m.setIntParam('display/verblevel', 0) m.readProblem(f"{instance['path']}") utilities.init_scip_params(m, seed=policy['seed']) m.setIntParam('timing/clocktype', 1) # 1: CPU user seconds, 2: wall clock time m.setRealParam('limits/time', time_limit) brancher = PolicyBranching(policy) m.includeBranchrule(branchrule=brancher, name=f"{policy['type']}:{policy['name']}", desc=f"Custom PySCIPOpt branching policy.", priority=666666, maxdepth=-1, maxbounddist=1) walltime = time.perf_counter() proctime = time.process_time() m.optimize() walltime = time.perf_counter() - walltime proctime = time.process_time() - proctime stime = m.getSolvingTime() nnodes = m.getNNodes() nlps = m.getNLPs() gap = m.getGap() status = m.getStatus() ndomchgs = brancher.ndomchgs ncutoffs = brancher.ncutoffs writer.writerow({ 'policy': f"{policy['type']}:{policy['name']}", 'sampling_strategy': policy['sampling_strategy'], 'seed': policy['seed'], 'type': instance['type'], 'instance': instance['path'], 'nnodes': nnodes, 'nlps': nlps, 'stime': stime, 'gap': gap, 'status': status, 'ndomchgs': ndomchgs, 'ncutoffs': ncutoffs, 'walltime': walltime, 'proctime': proctime, }) csvfile.flush() m.freeProb() print( f" {policy['type']}:{policy['name']}:{policy['sampling_strategy']} {policy['seed']} - {nnodes} ({nnodes+2*(ndomchgs+ncutoffs)}) nodes {nlps} lps {stime:.2f} ({walltime:.2f} wall {proctime:.2f} proc) s. {status}" )
def make_samples(in_queue, out_queue): """ Worker loop: fetch an instance, run an episode and record samples. Parameters ---------- in_queue : multiprocessing.Queue Input queue from which orders are received. out_queue : multiprocessing.Queue Output queue in which to send samples. """ while True: episode, instance, seed, time_limit, outdir, rng = in_queue.get() m = scip.Model() m.setIntParam('display/verblevel', 0) m.readProblem(f'{instance}') utilities.init_scip_params(m, seed=seed) m.setIntParam('timing/clocktype', 2) m.setRealParam('limits/time', time_limit) m.setLongintParam('limits/nodes', node_limit) branchrule = VanillaFullstrongBranchingDataCollector( rng, node_record_prob) m.includeBranchrule(branchrule=branchrule, name="Sampling branching rule", desc="", priority=666666, maxdepth=-1, maxbounddist=1) m.setBoolParam('branching/vanillafullstrong/integralcands', True) m.setBoolParam('branching/vanillafullstrong/scoreall', True) m.setBoolParam('branching/vanillafullstrong/collectscores', True) m.setBoolParam('branching/vanillafullstrong/donotbranch', True) m.setBoolParam('branching/vanillafullstrong/idempotent', True) out_queue.put({ "type": 'start', "episode": episode, "instance": instance, "seed": seed }) m.optimize() # data storage - root and node data are saved separately. # node data carries a reference to the root filename. if m.getNNodes() >= 1 and len(branchrule.obss) > 0: filenames = [] max_depth = max(x['depth'] for x in branchrule.obss_feats) stats = { 'nnodes': m.getNNodes(), 'time': m.getSolvingTime(), 'gap': m.getGap(), 'nobs': len(branchrule.obss) } # prepare root data sample_state, sample_khalil_state, root_obss = branchrule.state sample_cand_scores = branchrule.obss_feats[0]['scores'] sample_cands = np.where(sample_cand_scores != -1)[0] sample_cand_scores = sample_cand_scores[sample_cands] cand_choice = np.where(sample_cands == branchrule.targets[0])[0][0] root_filename = f"{outdir}/sample_root_0_{episode}.pkl" filenames.append(root_filename) with gzip.open(root_filename, 'wb') as f: pickle.dump( { 'type': 'root', 'episode': episode, 'instance': instance, 'seed': seed, 'stats': stats, 'root_state': [ sample_state, sample_khalil_state, sample_cands, cand_choice, sample_cand_scores ], 'obss': [ branchrule.obss[0], branchrule.targets[0], branchrule.obss_feats[0], None ], 'max_depth': max_depth }, f) # node data for i in range(1, len(branchrule.obss)): iteration_counter = branchrule.obss_feats[i]['iteration'] filenames.append( f"{outdir}/sample_node_{iteration_counter}_{episode}.pkl") with gzip.open(filenames[-1], 'wb') as f: pickle.dump( { 'type': 'node', 'episode': episode, 'instance': instance, 'seed': seed, 'stats': stats, 'root_state': f"{outdir}/sample_root_0_{episode}.pkl", 'obss': [ branchrule.obss[i], branchrule.targets[i], branchrule.obss_feats[i], None ], 'max_depth': max_depth }, f) out_queue.put({ "type": "done", "episode": episode, "instance": instance, "seed": seed, "filenames": filenames, "nnodes": len(filenames), }) m.freeProb()
def make_samples(in_queue, out_queue): """ Worker loop: fetch an instance, run an episode and record samples. Parameters ---------- in_queue : multiprocessing.Queue Input queue from which orders are received. out_queue : multiprocessing.Queue Output queue in which to send samples. """ while True: episode, instance, seed, exploration_policy, query_expert_prob, time_limit, out_dir = in_queue.get() print(f'[w {os.getpid()}] episode {episode}, seed {seed}, processing instance \'{instance}\'...') m = scip.Model() m.setIntParam('display/verblevel', 0) m.readProblem(f'{instance}') utilities.init_scip_params(m, seed=seed) m.setIntParam('timing/clocktype', 2) m.setRealParam('limits/time', time_limit) branchrule = SamplingAgent( episode=episode, instance=instance, seed=seed, out_queue=out_queue, exploration_policy=exploration_policy, query_expert_prob=query_expert_prob, out_dir=out_dir) m.includeBranchrule( branchrule=branchrule, name="Sampling branching rule", desc="", priority=666666, maxdepth=-1, maxbounddist=1) m.setBoolParam('branching/vanillafullstrong/integralcands', True) m.setBoolParam('branching/vanillafullstrong/scoreall', True) m.setBoolParam('branching/vanillafullstrong/collectscores', True) m.setBoolParam('branching/vanillafullstrong/donotbranch', True) m.setBoolParam('branching/vanillafullstrong/idempotent', True) out_queue.put({ 'type': 'start', 'episode': episode, 'instance': instance, 'seed': seed, }) m.optimize() m.freeProb() print(f"[w {os.getpid()}] episode {episode} done, {branchrule.sample_counter} samples") out_queue.put({ 'type': 'done', 'episode': episode, 'instance': instance, 'seed': seed, })