-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ed04302
Showing
46 changed files
with
2,397 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
|
||
name: Python selector | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Set up Python 3.8 | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: "3.8" | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi | ||
- name: Test with unittest | ||
run: | | ||
python -m unittest discover test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
|
||
# json files | ||
*.json | ||
|
||
# pyenv | ||
.python-version | ||
|
||
# SMAC files | ||
smac3_output/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
"""Generic algorithm configuration interface for unified planning.""" | ||
import unified_planning | ||
from unified_planning.environment import get_environment | ||
from unified_planning.shortcuts import * | ||
from utils.pcs_transform import transform_pcs | ||
from utils.ac_feedback import qaul_feedback, runtime_feedback | ||
import copy | ||
|
||
from ConfigSpace.read_and_write import pcs | ||
|
||
|
||
class GenericACInterface(): | ||
"""Generic AC interface.""" | ||
|
||
def __init__(self): | ||
"""Initialize generic interface.""" | ||
self.environment = get_environment() | ||
self.available_engines = self.get_available_engines() | ||
self.engine_param_spaces = {} | ||
self.engine_param_types = {} | ||
|
||
def get_available_engines(self): | ||
"""Get planning engines installed in up.""" | ||
factory = unified_planning.engines.factory.Factory(self.environment) | ||
|
||
return factory.engines | ||
|
||
def read_engine_pcs(self, engines, pcs_dir): | ||
"""Read in pcs file for engine. | ||
parameter engines: list of str, names of engines. | ||
parameter pcs_dir: str, path to directory with pcs files. | ||
""" | ||
if pcs_dir[-1] != '/': | ||
pcs_dir = pcs_dir + '/' | ||
|
||
for engine in engines: | ||
with open(pcs_dir + engine + '.pcs', 'r') as f: | ||
self.engine_param_spaces[engine] = pcs.read(f) | ||
|
||
with open(pcs_dir + engine + '.pcs', 'r') as f: | ||
lines = f.readlines() | ||
self.engine_param_types[engine] = {} | ||
for line in lines: | ||
if '# FLAGS #' in line: | ||
self.engine_param_types[engine]['-'+line.split(' ')[0]] = 'FLAGS' | ||
elif '# FLAG' in line: | ||
self.engine_param_types[engine]['-'+line.split(' ')[0]] = 'FLAG' | ||
|
||
def transform_conf_from_ac(self, ac_tool, engine, configuration): | ||
"""Transform configuration to up engine format. | ||
parameter ac_tool: str, name of AC tool in use. | ||
parameter engines: list of str, names of engines. | ||
parameter configuration: list of str, names of engines. | ||
return config: dict, configuration. | ||
""" | ||
if ac_tool == 'SMAC': | ||
config = transform_pcs(engine, configuration) | ||
if engine == 'lpg': | ||
del_list = [] | ||
add_list = [] | ||
for pname, pvalue in config.items(): | ||
if pname in self.engine_param_types: | ||
if self.engine_param_types[pname] == 'FLAGS': | ||
del_list.append(pname) | ||
flag_pname = pname + '=' + config[pname] | ||
add_list.append(flag_pname) | ||
elif self.engine_param_types[pname] == 'FLAG': | ||
if config[pname] == '1': | ||
config[pname] = '' | ||
else: | ||
del_list.append(pname) | ||
|
||
for dl in del_list: | ||
del config[dl] | ||
|
||
for al in add_list: | ||
config[al] = '' | ||
|
||
if engine == 'fast-downward': | ||
|
||
evals = ['eager_greedy', 'eager_wastar', | ||
'lazy_greedy', 'lazy_wastar'] | ||
open_eval = ['epsilon_greedy', 'single'] | ||
open_evals = ['pareto', 'tiebreaking', | ||
'type_based'] | ||
pruning = ['atom_centric_stubborn_sets'] | ||
|
||
search_option = config['fast_downward_search_config'] + '(' | ||
|
||
if 'evaluator' in config: | ||
if config['evaluator'] in evals: | ||
search_option += '[' + str(config['evaluator']) + '()], ' | ||
else: | ||
search_option += str(config['evaluator']) + '(), ' | ||
|
||
if 'open' in config: | ||
if config['open'] not in open_eval and \ | ||
config['open'] not in open_evals: | ||
search_option += '[' + str(config['open']) + '()], ' | ||
elif config['open'] in open_eval: | ||
search_option += '[' + str(config['open']) + '(' + str(config['open_list_evals']) + ')], ' | ||
elif config['open'] in open_evals: | ||
search_option += '[' + str(config['open']) + '([]' + str(config['open_list_evals']) + '])], ' | ||
|
||
if config['evaluator'] == 'ehc': | ||
search_option += 'preferred_usage=' + str(config['ehc_preferred_usage']) + ',' | ||
|
||
if 'reopen_closed' in config: | ||
search_option += 'reopen_closed=' + str(config['reopen_closed']) + ',' | ||
|
||
if 'randomize_successors' in config: | ||
search_option += 'randomize_successors=' + str(config['randomize_successors']) + ',' | ||
|
||
if 'pruning' in config: | ||
if config['pruning'] in pruning: | ||
search_option += 'pruning=' + str(config['pruning']) + '(use_sibling_shortcut=' \ | ||
+ config['atom_centric_stubborn_sets_use_sibling'] + \ | ||
',atom_selection_strategy=' + config['atom_selection_strategy'] + '(), ' | ||
else: | ||
search_option += 'pruning=' + str(config['pruning']) + '(),' | ||
|
||
search_option += 'cost_type=' + config['cost_type'] + ')' | ||
search_option = search_option.replace(" ", "") | ||
|
||
config = {'fast_downward_search_config': search_option} | ||
|
||
return config | ||
|
||
def transform_param_space(self, ac_tool, pcs): | ||
"""Transform configuration to AC tool format. | ||
parameter pcs: ConfigSpace object, parameter space. | ||
parameter ac_tool: str, AC tool in use. | ||
return param_space: transformed parameter space. | ||
""" | ||
if ac_tool == 'SMAC': # SMAC uses ConfigSpace | ||
param_space = pcs | ||
|
||
return param_space | ||
|
||
def get_feedback(self, engine, fbtype, result): | ||
"""Get feedback from planning engine after run. | ||
parameter engine: str, name of planning engine. | ||
parameter fbtype: str, type of feedback | ||
parameter result: object, planning result. | ||
""" | ||
if fbtype == 'quality': | ||
feedback = qaul_feedback(engine, result) | ||
if fbtype == 'runtime': | ||
feedback = runtime_feedback(engine, result) | ||
if fbtype == 'gray_box': | ||
feedback = None | ||
|
||
return feedback | ||
|
||
def run_engine_config(self, ac_tool, config, metric, engine, plantype, problem): | ||
"""Execute configurated engine run. | ||
paremer config: configuration of engine. | ||
parameter engine: str, engine name. | ||
parameter plantype: str, type of planning. | ||
return feedback: result from configurated engine run. | ||
""" | ||
if plantype == 'OneshotPlanner': | ||
config = self.transform_conf_from_ac(ac_tool, engine, config) | ||
with OneshotPlanner(name=engine, | ||
params=config) as planner: | ||
try: | ||
result = planner.solve(problem) | ||
if (result.status == | ||
up.engines.PlanGenerationResultStatus. | ||
SOLVED_SATISFICING): | ||
print("Result found.\n") | ||
else: | ||
print("No plan found.\n") | ||
feedback = self.get_feedback(engine, metric, result) | ||
except: | ||
print("No plan found.\n") | ||
feedback = None | ||
|
||
return feedback |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Algorithm Configuration for the AIPlan4EU Unified Planning |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
"""Functionalities for managing and calling configurators.""" | ||
from smac import Scenario | ||
from smac import AlgorithmConfigurationFacade | ||
|
||
from unified_planning.io import PDDLReader | ||
|
||
from AC_interface import * | ||
|
||
import json | ||
|
||
class Configurator(): | ||
"""Configurator functions.""" | ||
|
||
def __init__(self): | ||
"""Initialize generic interface.""" | ||
self.incumbent = None | ||
self.instance_features = {} | ||
self.train_set = {} | ||
self.test_set = {} | ||
self.reader = PDDLReader() | ||
self.metric = None | ||
self.crash_cost = 0 | ||
self.ac = None | ||
|
||
def get_instance_features(self, instance_features=None): | ||
self.instance_features = instance_features | ||
print('\nSetting instance features.\n') | ||
|
||
def set_training_instance_set(self, train_set): | ||
self.train_set = train_set | ||
print('\nSetting training instance set.\n') | ||
|
||
def set_test_instance_set(self, test_set): | ||
self.test_set = test_set | ||
print('\nSetting testing instance set.\n') | ||
|
||
def get_feedback_function(self, ac_tool, gaci, engine, metric, mode): | ||
self.metric = metric | ||
if ac_tool == 'SMAC': | ||
def planner_feedback(config, instance, seed=0): | ||
instance_p = f'{instance}' | ||
domain_path = instance_p.rsplit('/', 1)[0] | ||
out_file = instance_p.rsplit('/', 1)[1] | ||
domain = f'{domain_path}/domain.pddl' | ||
pddl_problem = self.reader.parse_problem(f'{domain}', | ||
f'{instance_p}') | ||
# print(pddl_problem) | ||
feedback = \ | ||
gaci.run_engine_config(ac_tool, | ||
config, | ||
metric, | ||
engine, | ||
mode, | ||
pddl_problem) | ||
if feedback is not None: | ||
# SMAC always minimizes | ||
if metric == 'quality': | ||
return -feedback | ||
elif metric == 'runtime': | ||
return feedback | ||
else: | ||
return None | ||
|
||
print('\nSMAC feedback function is generated.\n') | ||
|
||
return planner_feedback | ||
|
||
def set_scenario(self, ac_tool, param_space, configuration_time=120, | ||
n_trials=400, min_budget=1, max_budget=3, crash_cost=0, | ||
planner_timelimit=30, n_workers=1, instances=[], | ||
instance_features=None): | ||
if not instances: | ||
instances = self.train_set | ||
self.crash_cost = crash_cost | ||
if ac_tool == 'SMAC': | ||
scenario = Scenario( | ||
param_space, | ||
walltime_limit=configuration_time, # We want to optimize for <configuration_time> seconds | ||
n_trials=n_trials, # We want to try max <n_trials> different trials | ||
min_budget=min_budget, # Use min <min_budget> instance | ||
max_budget=max_budget, # Use max <max_budget> instances | ||
deterministic=True, # Not stochastic algorithm | ||
crash_cost=crash_cost, # Cost of algorithm crashing -> AC metric to evaluate configuration | ||
trial_walltime_limit=planner_timelimit, # Max time for algorithm to run | ||
use_default_config=True, # include default config | ||
n_workers=n_workers, # Number of parallel runs | ||
instances=instances, # List of training instances | ||
instance_features=instance_features # Dict of instance features | ||
) | ||
print('\nSMAC scenario is set.\n') | ||
|
||
self.scenario = scenario | ||
|
||
def optimize(self, ac_tool, feedback_function=None, gray_box=False): | ||
if ac_tool == 'SMAC': | ||
print('\nStarting Parameter optimization\n') | ||
ac = AlgorithmConfigurationFacade( | ||
self.scenario, | ||
feedback_function, | ||
overwrite=True, | ||
) | ||
|
||
self.incumbent = ac.optimize() | ||
|
||
print('\nBest Configuration found is:\n', self.incumbent) | ||
|
||
return self.incumbent, ac | ||
|
||
def evaluate(self, feedback_function, incumbent, instances=[]): | ||
if not instances: | ||
instances = self.test_set | ||
nr_inst = len(instances) | ||
avg_f = 0 | ||
for inst in instances: | ||
f = feedback_function(incumbent, inst, seed=0) | ||
if f is not None and self.metric == 'quality': | ||
f = -f | ||
print(f'\nFeedback on instance {inst}:\n\n', f, '\n') | ||
if f is not None: | ||
avg_f += f | ||
else: | ||
avg_f += self.crash_cost | ||
nr_inst -= 1 | ||
avg_f = avg_f / nr_inst | ||
print('\nAverage performance:', avg_f, '\n') | ||
|
||
def save_config(self, path, config, gaci, ac_tool, engine): | ||
config = gaci.transform_conf_from_ac(ac_tool, engine, config) | ||
with open(f'{path}/incumbent_{engine}.json', 'w') as f: | ||
json.dump(config, f) | ||
print(f'\nSaved best configuration in {path}/incumbent_{engine}.json\n') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
heuristic {hadd, hmax, blind} [hadd] | ||
search_algorithm {gbfs, astar, wastar} [gbfs] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
fast_downward_search_config {astar, eager, eager_greedy, eager_wastar, ehc, lazy, lazy_greedy, lazy_wastar} [astar] # SearchEngine | ||
|
||
# Options for SearchEngines | ||
evaluator {add, blind, cea, cegar, cg, ff, goalcount, hm, hmax, lmcut, const, g, pref, cpdbs, ipdb, pdb, zopdbs} [blind] | ||
|
||
ehc_preferred_usage {prune_by_preferred, rank_preferred_first} [prune_by_preferred] | ||
|
||
open {alt, epsilon_greedy, pareto, single, tiebreaking, type_based} [alt] | ||
open_list_evals {add, blind, cea, cegar, cg, ff, goalcount, hm, hmax, lmcut, const, g, pref, cpdbs, ipdb, pdb, zopdbs} [blind] | ||
|
||
reopen_closed {false, true} [false] | ||
randomize_successors {false, true} [false] | ||
|
||
cost_type {normal, one, plus_one} [normal] | ||
|
||
# Pruning Methods | ||
pruning {atom_centric_stubborn_sets, null, stubborn_sets_ec, stubborn_sets_simple} [null] | ||
atom_centric_stubborn_sets_use_sibling {false, true} [false] | ||
atom_selection_strategy {fast_downward, quick_skip, static_small, dynamic_small} [quick_skip] # Example: atom_centric_stubborn_sets(use_sibling_shortcut=true, atom_selection_strategy=quick_skip, verbosity=normal) | ||
|
||
# Conditional, <child name> | <parent name> in {<parent val1>, ..., <parent valK>}: child only active if parent takes specified values | ||
evaluator | fast_downward_search_config in {astar, eager_greedy, eager_wastar, ehc, lazy_greedy, lazy_wastar} | ||
ehc_preferred_usage | fast_downward_search_config in {ehc} | ||
reopen_closed | fast_downward_search_config in {eager, eager_wastar, lazy, lazy_greedy, lazy_wastar} | ||
randomize_successors | fast_downward_search_config in {lazy, lazy_greedy, lazy_wastar} | ||
open | fast_downward_search_config in {eager, lazy} | ||
open_list_evals | fast_downward_search_config in {eager, lazy} | ||
pruning | fast_downward_search_config in {astar, eager, eager_greedy, eager_wastar} | ||
atom_centric_stubborn_sets_use_sibling | pruning in {atom_centric_stubborn_sets} | ||
atom_selection_strategy | pruning in {atom_centric_stubborn_sets} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
search_algorithm {0} [0] | ||
heuristic {0, 1, 2, 3} [2] |
Oops, something went wrong.