Skip to content

Commit

Permalink
revert previous changes + better README
Browse files Browse the repository at this point in the history
  • Loading branch information
ingambe committed May 15, 2022
1 parent 9dc8297 commit f245564
Show file tree
Hide file tree
Showing 13 changed files with 7,810 additions and 804 deletions.
5 changes: 3 additions & 2 deletions JSSEnv/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from gym.envs.registration import register


register(
id='JSSEnv-v1',
entry_point='JSSEnv.envs:JssEnv',
id="jss-v1",
entry_point="JSSEnv.envs:JssEnv",
)
2 changes: 1 addition & 1 deletion JSSEnv/envs/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from JSSEnv.envs.JssEnv import JssEnv
from JSSEnv.envs.jss_env import JssEnv
280 changes: 172 additions & 108 deletions JSSEnv/envs/JssEnv.py → JSSEnv/envs/jss_env.py

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion JSSEnv/envs/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

registry = EnvRegistry()


def register(id, **kwargs):
return registry.register(id, **kwargs)


def make(id, **kwargs):
return registry.make(id, **kwargs)


def spec(id):
return registry.spec(id)
return registry.spec(id)
30 changes: 15 additions & 15 deletions JSSEnv/utils.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import numpy as np


def assign_env_config(self, kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
if hasattr(self, 'env_config'):
# print(self.env_config)
if hasattr(self, "env_config"):
for key, value in self.env_config.items():
# Check types based on default settings
if hasattr(self, key):
if type(getattr(self,key)) == np.ndarray:
if type(getattr(self, key)) == np.ndarray:
setattr(self, key, value)
else:
setattr(self, key,
type(getattr(self, key))(value))
setattr(self, key, type(getattr(self, key))(value))
else:
setattr(self, key, value)



# Get Ray to work with gym registry
def create_env(config, *args, **kwargs):
if type(config) == dict:
env_name = config['env']
else:
env_name = config
if env_name == 'JSSEnv-v1':
from JSSEnv.envs.JssEnv import JssEnv as env
else:
raise NotImplementedError('Environment {} not recognized.'.format(env_name))
return env
if type(config) == dict:
env_name = config["env"]
else:
env_name = config
if env_name == "jss-v1":
from JSSEnv.envs.jss_env import JssEnv as env
else:
raise NotImplementedError("Environment {} not recognized.".format(env_name))
return env
3 changes: 0 additions & 3 deletions JSSEnv/version.py

This file was deleted.

35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,49 @@ Getting Started
This repository is available as a pip package:

```shell
pip install JSSEnv==1.0.0
pip install JSSEnv
```

Once installed, the environment will be available in your OpenAi's gym environment and can be used to train a reinforcement learning agent:

```python
import gym
env = gym.make('JSSEnv:jss-v1', env_config={'instance_path': 'INSTANCE_PATH'})
import JSSEnv # an ongoing issue with OpenAi's gym causes it to not import automatically external modules, see: https://github.com/openai/gym/issues/2809
# for older version of gym, you have to use
# env = gym.make('JSSEnv:jss-v1', env_config={'instance_path': 'INSTANCE_PATH'})
env = gym.make('jss-v1', env_config={'instance_path': 'INSTANCE_PATH'})
```

### Important: Your instance must follow [Taillard's specification](http://jobshop.jjvh.nl/explanation.php#taillard_def).


How To Use
------------

The observation provided by the environment contains both a boolean array indicating if the action is legal or not and the "real" observation

```python
self.observation_space = gym.spaces.Dict({
"action_mask": gym.spaces.Box(0, 1, shape=(self.jobs + 1,)),
"real_obs": gym.spaces.Box(low=0.0, high=1.0, shape=(self.jobs, 7), dtype=np.float),
})
```

A random agent would have to sample legal action from this `action_mask` array, otherwise, you might take illegal actions.
In theory, it is not possible to take the same action over and over again as the job will have one of his operations currently on a machine and might not be free for schedule.

For research purposes, I've made a random loop using RLLib: https://github.com/prosysscience/RL-Job-Shop-Scheduling/blob/0bbe0c0f2b8a742b75cbe67c5f6a825b8cfdf5eb/JSS/randomLoop/random_loop.py

If you don't want to use RLLib, you can write a simple random loop using `numpy.random.choice` function:

```python
import numpy as np
np.random.choice(len(legal_action), 1, p=(legal_action / legal_action.sum()))[0]
```

Where `legal_action` is the array of legal action (i.e., `action_mask`).
This line of code will randomly sample one legal action from the `action_mask`.

Project Organization
------------

Expand Down
120 changes: 0 additions & 120 deletions rllib-validate-env.py

This file was deleted.

52 changes: 29 additions & 23 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
from setuptools import setup, find_packages
import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'JSSEnv'))
from JSSEnv.version import VERSION

setup(name='JSSEnv',
version=VERSION,
author="Pierre Tassel",
author_email="[email protected]",
description="An optimized OpenAi gym's environment to simulate the Job-Shop Scheduling problem.",
url="https://github.com/prosysscience/JSSEnv",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
install_requires=['gym', 'pandas', 'numpy', 'plotly', 'imageio', 'psutil', 'requests', 'kaleido', 'pytest',
'codecov'],
include_package_data=True,
zip_safe=False
)
setup(
name="JSSEnv",
version="1.0.2",
author="Pierre Tassel",
author_email="[email protected]",
description="An optimized OpenAi gym's environment to simulate the Job-Shop Scheduling problem.",
url="https://github.com/prosysscience/JSSEnv",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires=">=3.6",
install_requires=[
"gym",
"pandas",
"numpy",
"plotly",
"imageio",
"psutil",
"requests",
"kaleido",
"pytest",
"codecov",
],
include_package_data=True,
zip_safe=False,
)
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import JSSEnv
36 changes: 27 additions & 9 deletions tests/test_rendering.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import gym
import unittest
import imageio
from pathlib import Path


class TestRendering(unittest.TestCase):

def test_optimum_ta01_gif(self):
# http://optimizizer.com/solution.php?name=ta01&UB=1231&problemclass=ta
env = gym.make('JSSEnv:jss-v1', env_config={'instance_path': '../JSSEnv/envs/instances/ta01'})
env = gym.make(
"jss-v1",
env_config={
"instance_path": f"{str(Path(__file__).parent.absolute())}/../JSSEnv/envs/instances/ta01"
},
)
env.reset()
self.assertEqual(env.current_time_step, 0)
# for every machine give the jobs to process in order for every machine
Expand All @@ -26,7 +31,7 @@ def test_optimum_ta01_gif(self):
[7, 9, 8, 5, 6, 0, 2, 3, 1, 13, 14, 12, 4, 10, 11],
[6, 0, 7, 11, 5, 14, 10, 2, 4, 13, 8, 9, 3, 12, 1],
[13, 10, 7, 9, 5, 3, 11, 1, 12, 14, 2, 4, 0, 6, 8],
[13, 11, 6, 8, 7, 4, 1, 5, 3, 10, 0, 14, 9, 2, 12]
[13, 11, 6, 8, 7, 4, 1, 5, 3, 10, 0, 14, 9, 2, 12],
]
done = False
job_nb = len(solution_sequence[0])
Expand All @@ -42,10 +47,18 @@ def test_optimum_ta01_gif(self):
break
if env.machine_legal[machine] and index_machine[machine] < job_nb:
action_to_do = solution_sequence[machine][index_machine[machine]]
if env.needed_machine_jobs[action_to_do] == machine and env.legal_actions[action_to_do]:
if (
env.needed_machine_jobs[action_to_do] == machine
and env.legal_actions[action_to_do]
):
no_op = False
self.assertTrue(env.legal_actions[action_to_do], "We don't perform illegal actions")
self.assertEqual(sum(env.legal_actions[:-1]), env.nb_legal_actions)
self.assertTrue(
env.legal_actions[action_to_do],
"We don't perform illegal actions",
)
self.assertEqual(
sum(env.legal_actions[:-1]), env.nb_legal_actions
)
state, reward, done, _ = env.step(action_to_do)
index_machine[machine] += 1
step_nb += 1
Expand All @@ -54,9 +67,14 @@ def test_optimum_ta01_gif(self):
if no_op and not done:
self.assertTrue(len(env.next_time_step) > 0, "step {}".format(step_nb))
previous_time_step = env.current_time_step
env._increase_time_step()
self.assertTrue(env.current_time_step > previous_time_step, "we increase the time step")
self.assertEqual(sum(index_machine), len(solution_sequence) * len(solution_sequence[0]))
env.increase_time_step()
self.assertTrue(
env.current_time_step > previous_time_step,
"we increase the time step",
)
self.assertEqual(
sum(index_machine), len(solution_sequence) * len(solution_sequence[0])
)
self.assertEqual(env.current_time_step, 1231)
imageio.mimsave("ta01.gif", images)
env.reset()
Expand Down
Loading

0 comments on commit f245564

Please sign in to comment.