Skip to content

Commit

Permalink
Refactored agents-key in configuration files (#396)
Browse files Browse the repository at this point in the history
* changed jsons to new format

* changed test-data

* Adapted to new agent format in json

* Adapted to new agent-structure in json

* Fixed tests

* Adapted to new agents-config format in json

* Adapted to new agents-format in json

* Adapted to new agents-format, upload still not working

* Fixed typo

* Fixed merge for agents

* Fixed tests

* Small refactoring

* Updated docstring

* Added comment

* Added migration

* Recommerce :)

* Updated comment

* Changed interval for updating API indicator

Co-authored-by: Judith <[email protected]>
  • Loading branch information
NikkelM and felix-20 authored Apr 6, 2022
1 parent c4fef70 commit 01da9ac
Show file tree
Hide file tree
Showing 25 changed files with 396 additions and 437 deletions.
26 changes: 15 additions & 11 deletions recommerce/configuration/config_validation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# This file contains logic used by the webserver to validate configuration files

from recommerce.configuration.environment_config import EnvironmentConfig
from recommerce.configuration.hyperparameter_config import HyperparameterConfig

Expand All @@ -11,11 +13,10 @@ def validate_config(config: dict, config_is_final: bool) -> tuple:
config_is_final (bool): Whether or not the config must contain all required keys.
Returns:
triple: success: A status (True) and the split hyperparameter_config and environment_config dictionaries as a tuple.
tuple: success: A status (True) and the split hyperparameter_config and environment_config dictionaries as a tuple.
failure: A status (False) and the errormessage as a string.
"""
try:
# validate the config using `recommerce` validation logic
# first check if the environment and hyperparameter parts are already split up
if 'environment' in config and 'hyperparameter' in config:
assert len(config) == 2, 'Your config should not contain keys other than "environment" and "hyperparameter"'
Expand Down Expand Up @@ -54,11 +55,11 @@ def validate_sub_keys(config_class: HyperparameterConfig or EnvironmentConfig, c
AssertionError: If the given config contains a key that is invalid.
"""
for key, _ in config.items():
# TODO: Remove this workaround with the agent-rework in the config files
# we need to separately check agents, since it is a list of dictionaries
if key == 'agents':
for agent_key in config['agents'].keys():
assert all(this_key in {'agent_class', 'argument'} for this_key in config['agents'][agent_key]), \
f'an invalid key for agents was provided: {config["agents"][agent_key].keys()}'
for agent in config['agents']:
assert all(agent_key in {'name', 'agent_class', 'argument'} for agent_key in agent.keys()), \
f'an invalid key for agents was provided: {agent.keys()}'
# the key is key of a dictionary in the config
elif top_level_keys[key]:
assert isinstance(config[key], dict), f'The value of this key must be of type dict: {key}, but was {type(config[key])}'
Expand Down Expand Up @@ -128,7 +129,8 @@ def check_config_types(hyperparameter_config: dict, environment_config: dict, mu

# check types for environment_config
task = environment_config['task'] if must_contain else 'None'
EnvironmentConfig.check_types(environment_config, task, must_contain)
single_agent = task in ['training', 'exampleprinter']
EnvironmentConfig.check_types(environment_config, task, single_agent, must_contain)


if __name__ == '__main__': # pragma: no cover
Expand All @@ -152,16 +154,18 @@ def check_config_types(hyperparameter_config: dict, environment_config: dict, mu
'storage_cost_per_product': 0.1
},
'episodes': 5,
'agents': {
'CE Rebuy Agent (QLearning)': {
'agents': [
{
'name': 'CE Rebuy Agent (QLearning)',
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''
},
'CE Rebuy Agent (QLearaning)': {
{
'name': 'CE Rebuy Agent (QLearaning)',
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''
}
}
]
}
hyper, env = split_combined_config(test_config)
check_config_types(hyper, env)
221 changes: 69 additions & 152 deletions recommerce/configuration/environment_config.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
"episodes": 500,
"plot_interval": 100,
"marketplace": "recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario",
"agents": {
"Rule_Based Agent": {
"agents": [
{
"name": "Rule_Based Agent",
"agent_class": "recommerce.market.circular.circular_vendors.RuleBasedCERebuyAgent",
"argument": ""
},
"CE Rebuy Agent (QLearning)": {
{
"name": "CE Rebuy Agent (QLearning)",
"agent_class": "recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent",
"argument": "CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"task": "exampleprinter",
"marketplace": "recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceOneCompetitor",
"agents": {
"CE Rebuy Agent (QLearning)": {
"agents": [
{
"name": "CE Rebuy Agent (QLearning)",
"agent_class": "recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent",
"argument": "CircularEconomyRebuyPriceOneCompetitor_QLearningCERebuyAgent.dat"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"task": "training",
"marketplace": "recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario",
"agents": {
"CE Rebuy Agent (QLearning)": {
"agents": [
{
"name": "CE Rebuy Agent (QLearning)",
"agent_class": "recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent",
"argument": ""
}
}
]
}
9 changes: 5 additions & 4 deletions recommerce/monitoring/exampleprinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,18 @@ def main(): # pragma: no cover
printer = ExamplePrinter()

config: ExampleprinterEnvironmentConfig = EnvironmentConfigLoader.load('environment_config_exampleprinter')
# TODO: Theoretically, the name of the agent is saved in config['name'], but we don't use it yet.
marketplace = config.marketplace()

# QLearningAgents need more initialization
if issubclass(config.agent[0], QLearningAgent):
if issubclass(config.agent['agent_class'], QLearningAgent):
printer.setup_exampleprinter(marketplace=marketplace,
agent=config.agent[0](
agent=config.agent['agent_class'](
n_observations=marketplace.observation_space.shape[0],
n_actions=marketplace.get_n_actions(),
load_path=os.path.abspath(os.path.join(PathManager.data_path, config.agent[1]))))
load_path=os.path.abspath(os.path.join(PathManager.data_path, config.agent['argument']))))
else:
printer.setup_exampleprinter(marketplace=marketplace, agent=config.agent[0]())
printer.setup_exampleprinter(marketplace=marketplace, agent=config.agent['agent_class']())

print(f'The final profit was: {printer.run_example()}')

Expand Down
5 changes: 2 additions & 3 deletions recommerce/rl/training_scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ def train_from_config():
Use the `environment_config_training.json` file to decide on the training parameters.
"""
config: TrainingEnvironmentConfig = EnvironmentConfigLoader.load('environment_config_training')
# Since we store a tuple (class, None) in config.agent, we just want the first component
# Since we store an one-element list [class] in config.marketplace, we just want the stored element
run_training_session(config.marketplace, config.agent)
# TODO: Theoretically, the name of the agent is saved in config['name'], but we don't use it yet.
run_training_session(config.marketplace, config.agent['agent_class'])


def main():
Expand Down
10 changes: 6 additions & 4 deletions tests/test_data/environment_config_agent_monitoring.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
"episodes": 50,
"plot_interval": 25,
"marketplace": "recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario",
"agents": {
"Rule_Based Agent": {
"agents": [
{
"name": "Rule_Based Agent",
"agent_class": "recommerce.market.circular.circular_vendors.RuleBasedCERebuyAgent",
"argument": ""
},
"CE Rebuy Agent (QLearning)": {
{
"name": "CE Rebuy Agent (QLearning)",
"agent_class": "recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent",
"argument": "CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat"
}
}
]
}
7 changes: 4 additions & 3 deletions tests/test_data/environment_config_exampleprinter.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"task": "exampleprinter",
"marketplace": "recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceOneCompetitor",
"agents": {
"CE Rebuy Agent (QLearning)": {
"agents": [
{
"name": "CE Rebuy Agent (QLearning)",
"agent_class": "recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent",
"argument": "CircularEconomyRebuyPriceOneCompetitor_QLearningCERebuyAgent.dat"
}
}
]
}
7 changes: 4 additions & 3 deletions tests/test_data/environment_config_training.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"task": "training",
"marketplace": "recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario",
"agents": {
"CE Rebuy Agent (QLearning)": {
"agents": [
{
"name": "CE Rebuy Agent (QLearning)",
"agent_class": "recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent",
"argument": ""
}
}
]
}
129 changes: 59 additions & 70 deletions tests/test_environment_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,68 +9,6 @@
from recommerce.market.circular.circular_vendors import RuleBasedCERebuyAgent
from recommerce.rl.q_learning.q_learning_agent import QLearningCERebuyAgent

valid_training_dict = {
'task': 'training',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': {
'CE Rebuy Agent (QLearning)': {
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''
}
}
}

valid_agent_monitoring_dict = {
'task': 'agent_monitoring',
'enable_live_draw': False,
'episodes': 10,
'plot_interval': 5,
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': {
'Rule_Based Agent': {
'agent_class': 'recommerce.market.circular.circular_vendors.RuleBasedCERebuyAgent',
'argument': ''
},
'CE Rebuy Agent (QLearning)': {
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': 'CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat'
}
}
}

valid_exampleprinter_dict = {
'task': 'exampleprinter',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': {
'CE Rebuy Agent (QLearning)': {
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': 'CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat'
}
}
}

invalid_agent_dict = {
'task': 'exampleprinter',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': {
'Agent_name': {
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''
}
}
}

invalid_task_dict = {
'task': 'not_existing_test_task',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': {
'Agent_name': {
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': 'CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat'
}
}
}


def test_abstract_parent_class():
with pytest.raises(TypeError) as error_message:
Expand All @@ -79,10 +17,22 @@ def test_abstract_parent_class():


def test_str_representation():
config = env_config.TrainingEnvironmentConfig(valid_training_dict)
test_dict = {
'task': 'training',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': [
{
'name': 'CE Rebuy Agent (QLearning)',
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''
}
]
}
config = env_config.TrainingEnvironmentConfig(test_dict)
assert str(config) == ("TrainingEnvironmentConfig: {'task': 'training', "
"'agent': <class 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent'>, "
"'marketplace': <class 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario'>}")
"'marketplace': <class 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario'>, "
"'agent': {'name': 'CE Rebuy Agent (QLearning)', "
"'agent_class': <class 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent'>, 'argument': ''}}")


get_class_testcases = [
Expand Down Expand Up @@ -138,9 +88,47 @@ def test_get_task(tested_class: env_config.EnvironmentConfig, expected_task):


valid_ConfigLoader_validate_testcases = [
valid_training_dict,
valid_agent_monitoring_dict,
valid_exampleprinter_dict
{
'task': 'training',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': [
{
'name': 'CE Rebuy Agent (QLearning)',
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''
}
]
},
{
'task': 'agent_monitoring',
'enable_live_draw': False,
'episodes': 10,
'plot_interval': 5,
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': [
{
'name': 'Rule_Based Agent',
'agent_class': 'recommerce.market.circular.circular_vendors.RuleBasedCERebuyAgent',
'argument': ''
},
{
'name': 'CE Rebuy Agent (QLearning)',
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': 'CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat'
}
]
},
{
'task': 'exampleprinter',
'marketplace': 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
'agents': [
{
'name': 'CE Rebuy Agent (QLearning)',
'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': 'CircularEconomyRebuyPriceMonopolyScenario_QLearningCERebuyAgent.dat'
}
]
}
]


Expand All @@ -152,9 +140,10 @@ def test_valid_ConfigLoader_validate(config):
valid_ConfigLoader_load_training_testcases = [
# TODO: Currently no testcases for ActorCriticAgents
('training', 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceMonopolyScenario',
{'CE Rebuy Agent (QLearning)': {'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent', 'argument': ''}}),
[{'name': 'CE Rebuy Agent (QLearning)', 'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCERebuyAgent',
'argument': ''}]),
('training', 'recommerce.market.circular.circular_sim_market.CircularEconomyRebuyPriceOneCompetitor',
{'CE Rebuy Agent (QLearning)': {'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCEAgent', 'argument': ''}})
[{'name': 'CE Rebuy Agent (QLearning)', 'agent_class': 'recommerce.rl.q_learning.q_learning_agent.QLearningCEAgent', 'argument': ''}])
]


Expand Down
13 changes: 4 additions & 9 deletions webserver/alpha_business_app/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,9 @@ def _start(self) -> HttpResponse:
# convert post request to normal dict
post_request = dict(self.request.POST.lists())

try:
config_dict = ConfigFlatDictParser().flat_dict_to_hierarchical_config_dict(post_request)
except AssertionError:
self.message = ['error', 'Could not create config: Please eliminate identical Agent names']
return self._decide_rendering()
config_dict = ConfigFlatDictParser().flat_dict_to_hierarchical_config_dict(post_request)

validate_status, validate_data = validate_config(config_dict, True)
validate_status, validate_data = validate_config(config=config_dict, config_is_final=True)
if not validate_status:
self.message = ['error', validate_data]
return self._decide_rendering()
Expand All @@ -311,16 +307,15 @@ def _start(self) -> HttpResponse:
if Container.objects.filter(id=response['id']).exists():
# we will kindly ask the user to try it again and stop the container
# TODO insert better handling here
print('the new container has the same id, as another container')
self.message = ['error', 'please try again']
self.message = ['error', 'The new container has the same id as an already existing container, please try again.']
return self._remove()
# get all necessary parameters for container object
container_name = self.request.POST['experiment_name']
container_name = container_name if container_name != '' else response['id'][:10]
config_object = ConfigModelParser().parse_config(copy.deepcopy(config_dict))
command = config_object.environment.task
Container.objects.create(id=response['id'], config=config_object, name=container_name, command=command)
config_object.name = f'used for {container_name}'
config_object.name = f'Config for {container_name}'
config_object.save()
return redirect('/observe', {'success': 'You successfully launched an experiment'})
else:
Expand Down
Loading

0 comments on commit 01da9ac

Please sign in to comment.