jsp-vis 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jsp_vis-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Alexander Nasuta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ # MANIFEST.in
jsp_vis-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.1
2
+ Name: jsp-vis
3
+ Version: 1.0.0
4
+ Summary: A flexible enviorment for job shop scheduling using the disjunctive graph apporach.
5
+ Author: Alexander Nasuta
6
+ Author-email: Alexander Nasuta <alexander.nasuta@ima.rwth-aachen.de>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2024 Alexander Nasuta
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+ Project-URL: Homepage, https://github.com/Alexander-Nasuta/pypitemplate
29
+ Platform: unix
30
+ Platform: linux
31
+ Platform: osx
32
+ Platform: cygwin
33
+ Platform: win32
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python
36
+ Classifier: Programming Language :: Python :: 3
37
+ Requires-Python: >=3.9
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: opencv-python
41
+ Requires-Dist: plotly
42
+ Requires-Dist: matplotlib
43
+ Requires-Dist: numpy
44
+ Requires-Dist: pandas
45
+ Requires-Dist: kaleido
46
+ Provides-Extra: dev
47
+ Requires-Dist: pip-tools; extra == "dev"
48
+ Requires-Dist: pytest; extra == "dev"
49
+ Requires-Dist: pytest-cov; extra == "dev"
50
+ Requires-Dist: tox; extra == "dev"
51
+ Requires-Dist: twine; extra == "dev"
52
+
53
+
54
+
55
+ <div id="top"></div>
56
+
57
+ <!-- PROJECT LOGO -->
58
+ <br />
59
+ <div align="center">
60
+ <!--
61
+ <a href="https://cybernetics-lab.de/">
62
+ <img src="https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/logo.png" alt="Logo" height="80">
63
+ </a>
64
+ -->
65
+
66
+ <h1 align="center">
67
+ Job Shop Scheduling Problem Visualisations
68
+ </h1>
69
+
70
+ <!--
71
+ <a>
72
+ <img src="https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/graph_jsp_tikz.png" alt="Logo" height="180">
73
+ </a>
74
+ -->
75
+
76
+ </div>
77
+
78
+
79
+ - **Github**: https://github.com/Alexander-Nasuta/jsp-vis
80
+
81
+ - **PyPi**:
82
+
83
+
84
+ # About The Project
85
+ Ths project provides visualisation for the Job Shop Scheduling Problem (JSP).
86
+ This is focused on Gantt charts. The input date for the visualisation is inspired by [plotly's Gantt chart api](https://plotly.com/python/gantt/).
87
+ `jsp-vis` is a standalone package and in designed to be used in combination with a JSP-reinforcement learning environments that follow the [Gymnasium Environment](https://gymnasium.farama.org/) standard.
88
+ The render function of the environment can be used to render the Gantt chart.
89
+ Typically the render function can implement different modes like `human`, `rgb_array` or `ansi` rendering.
90
+ The `jsp-vis` package offers three different visualisations: console visualisation, rgb_array visualisation and window visualisation.
91
+ The window visualisation is essentially only rendering the rgb_array visualisation in a window using OpenCV.
92
+ The console visualisation might be used for the `asni` mode of a render function, the rgb_array visualisation for the `rgb_array` mode and the window visualisation for the `human` mode.
93
+
94
+ # Installation
95
+
96
+ Install the package with pip:
97
+ ```
98
+ pip install todo
99
+ ```
100
+
101
+ # Minimal Working Example: console visualisation
102
+
103
+ ```python
104
+ import pandas
105
+ from jsp_vis.console import gantt_chart_console
106
+ import pandas as pd
107
+
108
+ df = pd.DataFrame([
109
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
110
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
111
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
112
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
113
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
114
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
115
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
116
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
117
+ ])
118
+ num_of_machines = 4
119
+
120
+ gantt_chart_console(df, num_of_machines)
121
+ ```
122
+ The code above will render the following Gantt chart in the console:
123
+
124
+ ![](https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/ft06_window_presi.gif)
125
+
126
+ # Minimal Working Example: console visualisation
127
+
128
+ ```python
129
+ from jsp_vis.cv2_window import render_gantt_in_window
130
+ import pandas as pd
131
+
132
+ df = pd.DataFrame([
133
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
134
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
135
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
136
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
137
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
138
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
139
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
140
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
141
+ ])
142
+ num_of_machines = 4
143
+
144
+ render_gantt_in_window(
145
+ df=df,
146
+ n_machines=num_of_machines,
147
+ wait=2000 # time in ms that the `cv2`-window is open.
148
+ # wait=None # ''None'' will keep the window open till a keyboard occurs.
149
+ )
150
+ ```
151
+
152
+ The code above will render the following Gantt chart in the console:
153
+
154
+ ![](https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/ft06_window_presi.gif)
155
+
156
+
157
+ # More Examples
158
+ For more examples you can have a look at the test files in the `tests` directory.
159
+ Every visualisation has its own test file and is tested on two different jsp instances defined in the `conftest.py`.
160
+
161
+ # License
162
+
163
+ Distributed under the MIT License. See `LICENSE.txt` for more information.
164
+
165
+ <!-- MARKDOWN LINKS & IMAGES todo: add Github, Linked in etc.-->
166
+ <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
167
+ [screenshot]: resources/readme_images/screenshot.png
168
+
169
+
@@ -0,0 +1,117 @@
1
+
2
+
3
+ <div id="top"></div>
4
+
5
+ <!-- PROJECT LOGO -->
6
+ <br />
7
+ <div align="center">
8
+ <!--
9
+ <a href="https://cybernetics-lab.de/">
10
+ <img src="https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/logo.png" alt="Logo" height="80">
11
+ </a>
12
+ -->
13
+
14
+ <h1 align="center">
15
+ Job Shop Scheduling Problem Visualisations
16
+ </h1>
17
+
18
+ <!--
19
+ <a>
20
+ <img src="https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/graph_jsp_tikz.png" alt="Logo" height="180">
21
+ </a>
22
+ -->
23
+
24
+ </div>
25
+
26
+
27
+ - **Github**: https://github.com/Alexander-Nasuta/jsp-vis
28
+
29
+ - **PyPi**:
30
+
31
+
32
+ # About The Project
33
+ Ths project provides visualisation for the Job Shop Scheduling Problem (JSP).
34
+ This is focused on Gantt charts. The input date for the visualisation is inspired by [plotly's Gantt chart api](https://plotly.com/python/gantt/).
35
+ `jsp-vis` is a standalone package and in designed to be used in combination with a JSP-reinforcement learning environments that follow the [Gymnasium Environment](https://gymnasium.farama.org/) standard.
36
+ The render function of the environment can be used to render the Gantt chart.
37
+ Typically the render function can implement different modes like `human`, `rgb_array` or `ansi` rendering.
38
+ The `jsp-vis` package offers three different visualisations: console visualisation, rgb_array visualisation and window visualisation.
39
+ The window visualisation is essentially only rendering the rgb_array visualisation in a window using OpenCV.
40
+ The console visualisation might be used for the `asni` mode of a render function, the rgb_array visualisation for the `rgb_array` mode and the window visualisation for the `human` mode.
41
+
42
+ # Installation
43
+
44
+ Install the package with pip:
45
+ ```
46
+ pip install todo
47
+ ```
48
+
49
+ # Minimal Working Example: console visualisation
50
+
51
+ ```python
52
+ import pandas
53
+ from jsp_vis.console import gantt_chart_console
54
+ import pandas as pd
55
+
56
+ df = pd.DataFrame([
57
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
58
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
59
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
60
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
61
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
62
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
63
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
64
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
65
+ ])
66
+ num_of_machines = 4
67
+
68
+ gantt_chart_console(df, num_of_machines)
69
+ ```
70
+ The code above will render the following Gantt chart in the console:
71
+
72
+ ![](https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/ft06_window_presi.gif)
73
+
74
+ # Minimal Working Example: console visualisation
75
+
76
+ ```python
77
+ from jsp_vis.cv2_window import render_gantt_in_window
78
+ import pandas as pd
79
+
80
+ df = pd.DataFrame([
81
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
82
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
83
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
84
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
85
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
86
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
87
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
88
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
89
+ ])
90
+ num_of_machines = 4
91
+
92
+ render_gantt_in_window(
93
+ df=df,
94
+ n_machines=num_of_machines,
95
+ wait=2000 # time in ms that the `cv2`-window is open.
96
+ # wait=None # ''None'' will keep the window open till a keyboard occurs.
97
+ )
98
+ ```
99
+
100
+ The code above will render the following Gantt chart in the console:
101
+
102
+ ![](https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/ft06_window_presi.gif)
103
+
104
+
105
+ # More Examples
106
+ For more examples you can have a look at the test files in the `tests` directory.
107
+ Every visualisation has its own test file and is tested on two different jsp instances defined in the `conftest.py`.
108
+
109
+ # License
110
+
111
+ Distributed under the MIT License. See `LICENSE.txt` for more information.
112
+
113
+ <!-- MARKDOWN LINKS & IMAGES todo: add Github, Linked in etc.-->
114
+ <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
115
+ [screenshot]: resources/readme_images/screenshot.png
116
+
117
+
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=65.5.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "jsp-vis"
7
+ version = "1.0.0"
8
+ description = "A flexible enviorment for job shop scheduling using the disjunctive graph apporach."
9
+ readme = "README.md"
10
+ authors = [{ name = "Alexander Nasuta", email = "alexander.nasuta@ima.rwth-aachen.de" }]
11
+ license = { file = "LICENSE" }
12
+ classifiers = [
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python",
15
+ "Programming Language :: Python :: 3",
16
+ ]
17
+ keywords = []
18
+ dependencies = [
19
+ "opencv-python",
20
+ "plotly",
21
+ # "networkx>=3",
22
+ "matplotlib",
23
+ "numpy",
24
+ "pandas",
25
+ "kaleido",
26
+ ]
27
+ requires-python = ">=3.9"
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pip-tools",
32
+ "pytest",
33
+ "pytest-cov",
34
+ "tox",
35
+ "twine"
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://github.com/Alexander-Nasuta/pypitemplate"
40
+
41
+ [tool.pytest.ini_options]
42
+ addopts = "--cov=jsp_vis -p no:warnings"
43
+ testpaths = [
44
+ "tests",
45
+ ]
@@ -0,0 +1,23 @@
1
+ [metadata]
2
+ name = jsp-vis
3
+ author = Alexander Nasuta
4
+ license = MIT
5
+ license_files = LICENSE
6
+ platforms = unix, linux, osx, cygwin, win32
7
+ classifiers =
8
+ Programming Language :: Python :: 3
9
+ Programming Language :: Python :: 3 :: Only
10
+ Programming Language :: Python :: 3.11
11
+
12
+ [options]
13
+ packages =
14
+ jsp_vis
15
+ python_requires = >=3.11
16
+ package_dir =
17
+ =src
18
+ zip_safe = no
19
+
20
+ [egg_info]
21
+ tag_build =
22
+ tag_date = 0
23
+
jsp_vis-1.0.0/setup.py ADDED
@@ -0,0 +1,4 @@
1
+ from setuptools import setup
2
+
3
+ if __name__ == '__main__':
4
+ setup()
File without changes
@@ -0,0 +1,209 @@
1
+ import itertools
2
+ import shutil
3
+
4
+ import matplotlib
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ import logging
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+ CEND = "\33[0m"
13
+
14
+
15
+ def rgb_color_sequence(r: int | float, g: int | float, b: int | float,
16
+ *, format_type: str = 'foreground') -> str:
17
+ """
18
+ generates a color-codes, that change the color of text in console outputs.
19
+
20
+ rgb values must be numbers between 0 and 255 or 0.0 and 1.0.
21
+
22
+ :param r: red value.
23
+ :param g: green value
24
+ :param b: blue value
25
+
26
+ :param format_type: specifies weather the foreground-color or the background-color shall be adjusted.
27
+ valid options: 'foreground','background'
28
+ :return: a string that contains the color-codes.
29
+ """
30
+ # type: ignore # noqa: F401
31
+ if format_type == 'foreground':
32
+ f = '\033[38;2;{};{};{}m'.format # font rgb format
33
+ elif format_type == 'background':
34
+ f = '\033[48;2;{};{};{}m'.format # font background rgb format
35
+ else:
36
+ raise ValueError(f"format {format_type} is not defined. Use 'foreground' or 'background'.")
37
+ rgb = [r, g, b]
38
+
39
+ if isinstance(r, int) and isinstance(g, int) and isinstance(b, int):
40
+ if min(rgb) < 0 and max(rgb) > 255:
41
+ raise ValueError("rgb values must be numbers between 0 and 255 or 0.0 and 1.0")
42
+ return f(r, g, b)
43
+ if isinstance(r, float) and isinstance(g, float) and isinstance(b, float):
44
+ if min(rgb) < 0 and max(rgb) > 1:
45
+ raise ValueError("rgb values must be numbers between 0 and 255 or 0.0 and 1.0")
46
+ return f(*[int(n * 255) for n in [r, g, b]])
47
+
48
+
49
+ def wrap_with_color_codes(s: object, /, r: int | float, g: int | float, b: int | float, **kwargs) \
50
+ -> str:
51
+ """
52
+ stringify an object and wrap it with console color codes. It adds the color control sequence in front and one
53
+ at the end that resolves the color again.
54
+
55
+ rgb values must be numbers between 0 and 255 or 0.0 and 1.0.
56
+
57
+ :param s: the object to stringify and wrap
58
+ :param r: red value.
59
+ :param g: green value.
60
+ :param b: blue value.
61
+ :param kwargs: additional argument for the 'DisjunctiveGraphJspVisualizer.rgb_color_sequence'-method.
62
+ :return:
63
+ """
64
+ return f"{rgb_color_sequence(r, g, b, **kwargs)}" \
65
+ f"{s}" \
66
+ f"{CEND}"
67
+
68
+
69
+ def gantt_chart_console(df: pd.DataFrame, n_machines: int, c_map="rainbow") -> None:
70
+ """
71
+ console version of the `gantt_chart_rgb_array`-method. prints a gant chart to the console.
72
+ the parameters need to follow the plotly specification.
73
+ see: https://plotly.com/python/gantt/ or `gantt_chart_rgb_array`
74
+
75
+ :param df: dataframe according to `plotly` specification (https://plotly.com/python/gantt/).
76
+
77
+ :return: a `plotly` gantt chart as rgb array.
78
+
79
+ color example
80
+
81
+ import numpy as np
82
+
83
+ c_map = plt.cm.get_cmap("jet") # select the desired cmap
84
+ arr = np.linspace(0, 1, 10) # create a list with numbers from 0 to 1 with n items
85
+ colors = {resource: c_map(val) for resource, val in enumerate(arr)}
86
+ """
87
+ w, h = shutil.get_terminal_size((80, 20)) # enable emulate output in terminal ...
88
+
89
+ c_map = matplotlib.colormaps.get_cmap(c_map) # select the desired cmap
90
+ arr = np.linspace(0, 1, n_machines) # create a list with numbers from 0 to 1 with n items
91
+ machine_colors = {m_id: c_map(val) for m_id, val in enumerate(arr)}
92
+ colors = {f"Machine {m_id}": (r, g, b) for m_id, (r, g, b, a) in machine_colors.items()}
93
+
94
+ if len(df) > 0:
95
+ machines = sorted(df['Resource'].unique())
96
+ jobs = df['Task'].unique()
97
+ jobs.sort()
98
+ else:
99
+ jobs, machines = None, None
100
+
101
+ len_prefix = 10
102
+ len_suffix = 15
103
+
104
+ x_pixels = w - len_prefix - len_suffix
105
+ x_max = df['Finish'].max() + 1 if len(df) > 0 else x_pixels
106
+ if x_pixels < 0:
107
+ log.warn("terminal window to small")
108
+ return
109
+
110
+ x_axis_tick_small = "╤════"
111
+ x_axis_tick_big = "╦════"
112
+ len_tick = len(x_axis_tick_big)
113
+ num_hole_ticks = x_pixels // len_tick
114
+ len_last_tick = x_pixels % len_tick
115
+ x_axis = "".join([
116
+ f"{'':<{len_prefix - 1}}╚",
117
+ *[x_axis_tick_big if i % 5 == 0 else x_axis_tick_small for i in range(num_hole_ticks)],
118
+ "═" * len_last_tick + "╝"
119
+ ])
120
+ x_chart_frame_top = "".join([
121
+ f"{'':<{len_prefix - 1}}╔",
122
+ "═" * x_pixels,
123
+ "╗"
124
+ ])
125
+
126
+ x_interval_increment5 = x_max / num_hole_ticks
127
+ x_interval_increment1 = x_max / x_pixels
128
+
129
+ x_axis_label = "".join([
130
+ f"{'':<{len_prefix}}",
131
+ *[
132
+ f"{f'{i * x_interval_increment5:.1f}':<5}" if i % 5 == 0 else f"{'':<5}"
133
+ for i in range(num_hole_ticks)
134
+ ]
135
+ ])
136
+
137
+ rows = []
138
+ if len(df) > 0:
139
+ for j, m in itertools.zip_longest(jobs, machines):
140
+ matching_tasks = df.loc[df['Task'] == j].iterrows()
141
+ chart_str = [i * x_interval_increment1 for i in range(x_pixels)]
142
+ for _, (_, start, finish, resource) in matching_tasks:
143
+ chart_str = [
144
+ f"{rgb_color_sequence(*colors[resource])}█"
145
+ if not isinstance(v, str) and start <= v <= finish else v for v in chart_str
146
+ ]
147
+ prefix = f"{f'{j}':<{len_prefix - 1}}║" if j else f"{'':<{len_prefix - 1}}║"
148
+ colored_block = wrap_with_color_codes("█", *colors[m]) if m else None
149
+ suffix = f"{f'║ {m}':<{len_suffix - 1}}" + f"{colored_block}" if m else f"{f'║':<{len_suffix}}"
150
+
151
+ chart_str = [" " if not isinstance(v, str) else v for v in chart_str]
152
+ chart_str = "".join(chart_str)
153
+ rows.append(f"{prefix}{chart_str}{CEND}{suffix}")
154
+ else:
155
+ rows = ["".join([f"{f'':<{len_prefix - 1}}║", " " * x_pixels, "║"])]
156
+
157
+ gant_str = "\n".join([
158
+ x_chart_frame_top,
159
+ *rows,
160
+ x_axis,
161
+ x_axis_label
162
+ ])
163
+ print(gant_str)
164
+
165
+
166
+ def graph_console(df: pd.DataFrame, jsp_instance: np.ndarray, c_map="rainbow"):
167
+ w, _ = shutil.get_terminal_size((80, 20))
168
+ _, n_jobs, n_machines = jsp_instance.shape
169
+
170
+ len_prefix = 10
171
+ len_suffix = 15
172
+
173
+ machine_order = jsp_instance[0]
174
+
175
+ if w < 2 * n_jobs + len_prefix + len_suffix:
176
+ log.warning("terminal window to small")
177
+ return
178
+
179
+ c_map = matplotlib.colormaps.get_cmap(c_map) # select the desired cmap
180
+ arr = np.linspace(0, 1, n_machines) # create a list with numbers from 0 to 1 with n items
181
+ machine_colors = {m_id: c_map(val) for m_id, val in enumerate(arr)}
182
+ colors = {f"Machine {m_id}": (r, g, b) for m_id, (r, g, b, a) in machine_colors.items()}
183
+
184
+ machine_strings = [
185
+ f"{m :>{len_suffix - 5}} {wrap_with_color_codes('█', r, g, b)}"
186
+ for m, (r, g, b) in colors.items()
187
+ ]
188
+
189
+ def task_is_in_df(job: int, machine: int):
190
+ return any((df['Task'] == f"Job {job}") & (df['Resource'] == f"Machine {machine}"))
191
+ pass
192
+
193
+ for j, m_str in itertools.zip_longest(range(n_jobs), machine_strings):
194
+ row = f"Job {j}" if j is not None else ""
195
+ row = f"{row:<{len_prefix}}"
196
+ for task_in_job in range(n_machines):
197
+
198
+ if j is not None:
199
+ machine_of_task = machine_order[j][task_in_job]
200
+ node_str = "●" if task_is_in_df(job=j, machine=machine_of_task) else "◯"
201
+ node_str = wrap_with_color_codes(node_str, *colors[f"Machine {machine_of_task}"])
202
+ if task_in_job < n_machines - 1:
203
+ node_str += "-"
204
+ else:
205
+ node_str = " "
206
+ if task_in_job < n_machines - 1:
207
+ node_str += " "
208
+ row += node_str
209
+ print("".join([row, " " * 4, m_str if m_str is not None else ""]))
@@ -0,0 +1,69 @@
1
+ import cv2
2
+ import signal
3
+
4
+ import pandas as pd
5
+ import numpy.typing as npt
6
+
7
+ from jsp_vis.rgb_array import gantt_chart_rgb_array
8
+
9
+
10
+ def handler_stop_signals(*_) -> None:
11
+ """
12
+ closes all `cv2`-windows when the process is killed
13
+ """
14
+ cv2.destroyAllWindows()
15
+
16
+
17
+ signal.signal(signal.SIGINT, handler_stop_signals)
18
+ signal.signal(signal.SIGTERM, handler_stop_signals)
19
+
20
+
21
+ def render_rgb_array(vis: npt.NDArray, *, window_title: str = "Job Shop Scheduling", wait: int = 1) -> None:
22
+ """
23
+ renders a rgb-array in an `cv2` window.
24
+ the window will remain open for `:param wait:` ms or till the user presses any key.
25
+
26
+ :param vis: the rgb-array to render.
27
+ :param window_title: the title of the `cv2`-window
28
+ :param wait: time in ms that the `cv2`-window is open.
29
+ if `None`, then the window will remain open till a keyboard occurs.
30
+
31
+ :return:
32
+ """
33
+ vis = cv2.cvtColor(vis, cv2.COLOR_RGB2BGR)
34
+ cv2.imshow(window_title, vis)
35
+ # https://stackoverflow.com/questions/64061721/opencv-to-close-the-window-on-a-specific-key
36
+ k = cv2.waitKey(wait) & 0xFF
37
+ if k == 27: # wait for ESC key to exit
38
+ cv2.destroyAllWindows()
39
+
40
+
41
+ def render_gantt_in_window(df: pd.DataFrame, *, n_machines: int, gantt_chart_rgb_array_kwargs: dict | None =None,
42
+ **render_kwargs: dict) -> None:
43
+ """
44
+ wrapper for the `gantt_chart_rgb_array`- and `render_rgb_array`-methods
45
+
46
+ :param df: parameter for `gantt_chart_rgb_array`
47
+ :param n_machines: parameter for `gantt_chart_rgb_array`
48
+ :param render_kwargs: additional parameters for `render_rgb_array`
49
+
50
+ :return: None
51
+ """
52
+ if gantt_chart_rgb_array_kwargs is None:
53
+ gantt_chart_rgb_array_kwargs = {}
54
+ vis = gantt_chart_rgb_array(df=df, n_machines=n_machines, **gantt_chart_rgb_array_kwargs)
55
+ render_rgb_array(vis, **render_kwargs)
56
+
57
+
58
+ if __name__ == '__main__':
59
+ df = [
60
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
61
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
62
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
63
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
64
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
65
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
66
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
67
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
68
+ ]
69
+ render_gantt_in_window(pd.DataFrame(df), n_machines=4, wait=None)
@@ -0,0 +1,49 @@
1
+ import cv2
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+ import matplotlib.pyplot as plt
7
+ import matplotlib as mpl
8
+ import plotly.figure_factory as ff
9
+
10
+
11
+ def gantt_chart_rgb_array(df: pd.DataFrame, n_machines: int, *,
12
+ c_map="rainbow",
13
+ dpi=80,
14
+ width=7.5,
15
+ height=5,
16
+ show_colorbar=True,
17
+ index_col='Resource',
18
+ group_tasks=True,
19
+ xaxis_type='linear') -> np.ndarray:
20
+ c_map = mpl.colormaps.get_cmap(c_map) # select the desired cmap
21
+ arr = np.linspace(0, 1, n_machines) # create a list with numbers from 0 to 1 with n items
22
+ machine_colors = {m_id: c_map(val) for m_id, val in enumerate(arr)}
23
+ colors = {f"Machine {m_id}": (r, g, b) for m_id, (r, g, b, a) in machine_colors.items()}
24
+
25
+ plt.figure(dpi=dpi)
26
+ plt.axis("off")
27
+ plt.tight_layout()
28
+
29
+ fig = mpl.pyplot.gcf()
30
+ fig.set_size_inches(width, height)
31
+
32
+ # Gantt chart
33
+ width, height = fig.canvas.get_width_height()
34
+ if not len(df):
35
+ df = pd.DataFrame([{"Task": "Job 0", "Start": 0, "Finish": 0, "Resource": "Machine 0"}])
36
+ fig = ff.create_gantt(df=df, show_colorbar=show_colorbar, index_col=index_col, group_tasks=group_tasks,
37
+ colors=colors)
38
+ fig.update_layout(xaxis_type=xaxis_type)
39
+
40
+ img_str = fig.to_image(format="jpg", width=width, height=height)
41
+
42
+ nparr = np.frombuffer(img_str, np.uint8)
43
+ img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # cv2.IMREAD_COLOR in OpenCV 3.1
44
+ img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
45
+
46
+ # clear current frame
47
+ plt.clf()
48
+ plt.close('all')
49
+ return img
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.1
2
+ Name: jsp-vis
3
+ Version: 1.0.0
4
+ Summary: A flexible enviorment for job shop scheduling using the disjunctive graph apporach.
5
+ Author: Alexander Nasuta
6
+ Author-email: Alexander Nasuta <alexander.nasuta@ima.rwth-aachen.de>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2024 Alexander Nasuta
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+ Project-URL: Homepage, https://github.com/Alexander-Nasuta/pypitemplate
29
+ Platform: unix
30
+ Platform: linux
31
+ Platform: osx
32
+ Platform: cygwin
33
+ Platform: win32
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python
36
+ Classifier: Programming Language :: Python :: 3
37
+ Requires-Python: >=3.9
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: opencv-python
41
+ Requires-Dist: plotly
42
+ Requires-Dist: matplotlib
43
+ Requires-Dist: numpy
44
+ Requires-Dist: pandas
45
+ Requires-Dist: kaleido
46
+ Provides-Extra: dev
47
+ Requires-Dist: pip-tools; extra == "dev"
48
+ Requires-Dist: pytest; extra == "dev"
49
+ Requires-Dist: pytest-cov; extra == "dev"
50
+ Requires-Dist: tox; extra == "dev"
51
+ Requires-Dist: twine; extra == "dev"
52
+
53
+
54
+
55
+ <div id="top"></div>
56
+
57
+ <!-- PROJECT LOGO -->
58
+ <br />
59
+ <div align="center">
60
+ <!--
61
+ <a href="https://cybernetics-lab.de/">
62
+ <img src="https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/logo.png" alt="Logo" height="80">
63
+ </a>
64
+ -->
65
+
66
+ <h1 align="center">
67
+ Job Shop Scheduling Problem Visualisations
68
+ </h1>
69
+
70
+ <!--
71
+ <a>
72
+ <img src="https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/graph_jsp_tikz.png" alt="Logo" height="180">
73
+ </a>
74
+ -->
75
+
76
+ </div>
77
+
78
+
79
+ - **Github**: https://github.com/Alexander-Nasuta/jsp-vis
80
+
81
+ - **PyPi**:
82
+
83
+
84
+ # About The Project
85
+ Ths project provides visualisation for the Job Shop Scheduling Problem (JSP).
86
+ This is focused on Gantt charts. The input date for the visualisation is inspired by [plotly's Gantt chart api](https://plotly.com/python/gantt/).
87
+ `jsp-vis` is a standalone package and in designed to be used in combination with a JSP-reinforcement learning environments that follow the [Gymnasium Environment](https://gymnasium.farama.org/) standard.
88
+ The render function of the environment can be used to render the Gantt chart.
89
+ Typically the render function can implement different modes like `human`, `rgb_array` or `ansi` rendering.
90
+ The `jsp-vis` package offers three different visualisations: console visualisation, rgb_array visualisation and window visualisation.
91
+ The window visualisation is essentially only rendering the rgb_array visualisation in a window using OpenCV.
92
+ The console visualisation might be used for the `asni` mode of a render function, the rgb_array visualisation for the `rgb_array` mode and the window visualisation for the `human` mode.
93
+
94
+ # Installation
95
+
96
+ Install the package with pip:
97
+ ```
98
+ pip install todo
99
+ ```
100
+
101
+ # Minimal Working Example: console visualisation
102
+
103
+ ```python
104
+ import pandas
105
+ from jsp_vis.console import gantt_chart_console
106
+ import pandas as pd
107
+
108
+ df = pd.DataFrame([
109
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
110
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
111
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
112
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
113
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
114
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
115
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
116
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
117
+ ])
118
+ num_of_machines = 4
119
+
120
+ gantt_chart_console(df, num_of_machines)
121
+ ```
122
+ The code above will render the following Gantt chart in the console:
123
+
124
+ ![](https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/ft06_window_presi.gif)
125
+
126
+ # Minimal Working Example: console visualisation
127
+
128
+ ```python
129
+ from jsp_vis.cv2_window import render_gantt_in_window
130
+ import pandas as pd
131
+
132
+ df = pd.DataFrame([
133
+ {'Task': 'Job 0', 'Start': 5, 'Finish': 16, 'Resource': 'Machine 0'},
134
+ {'Task': 'Job 0', 'Start': 28, 'Finish': 31, 'Resource': 'Machine 1'},
135
+ {'Task': 'Job 0', 'Start': 31, 'Finish': 34, 'Resource': 'Machine 2'},
136
+ {'Task': 'Job 0', 'Start': 34, 'Finish': 46, 'Resource': 'Machine 3'},
137
+ {'Task': 'Job 1', 'Start': 0, 'Finish': 5, 'Resource': 'Machine 0'},
138
+ {'Task': 'Job 1', 'Start': 5, 'Finish': 21, 'Resource': 'Machine 2'},
139
+ {'Task': 'Job 1', 'Start': 21, 'Finish': 28, 'Resource': 'Machine 1'},
140
+ {'Task': 'Job 1', 'Start': 28, 'Finish': 32, 'Resource': 'Machine 3'}
141
+ ])
142
+ num_of_machines = 4
143
+
144
+ render_gantt_in_window(
145
+ df=df,
146
+ n_machines=num_of_machines,
147
+ wait=2000 # time in ms that the `cv2`-window is open.
148
+ # wait=None # ''None'' will keep the window open till a keyboard occurs.
149
+ )
150
+ ```
151
+
152
+ The code above will render the following Gantt chart in the console:
153
+
154
+ ![](https://github.com/Alexander-Nasuta/graph-jsp-env/raw/master/resources/readme_images/ft06_window_presi.gif)
155
+
156
+
157
+ # More Examples
158
+ For more examples you can have a look at the test files in the `tests` directory.
159
+ Every visualisation has its own test file and is tested on two different jsp instances defined in the `conftest.py`.
160
+
161
+ # License
162
+
163
+ Distributed under the MIT License. See `LICENSE.txt` for more information.
164
+
165
+ <!-- MARKDOWN LINKS & IMAGES todo: add Github, Linked in etc.-->
166
+ <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
167
+ [screenshot]: resources/readme_images/screenshot.png
168
+
169
+
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ setup.cfg
6
+ setup.py
7
+ src/jsp_vis/__init__.py
8
+ src/jsp_vis/console.py
9
+ src/jsp_vis/cv2_window.py
10
+ src/jsp_vis/rgb_array.py
11
+ src/jsp_vis.egg-info/PKG-INFO
12
+ src/jsp_vis.egg-info/SOURCES.txt
13
+ src/jsp_vis.egg-info/dependency_links.txt
14
+ src/jsp_vis.egg-info/not-zip-safe
15
+ src/jsp_vis.egg-info/requires.txt
16
+ src/jsp_vis.egg-info/top_level.txt
17
+ tests/test_render_console.py
18
+ tests/test_render_cv2_window.py
19
+ tests/test_render_rgb_array.py
@@ -0,0 +1,13 @@
1
+ opencv-python
2
+ plotly
3
+ matplotlib
4
+ numpy
5
+ pandas
6
+ kaleido
7
+
8
+ [dev]
9
+ pip-tools
10
+ pytest
11
+ pytest-cov
12
+ tox
13
+ twine
@@ -0,0 +1 @@
1
+ jsp_vis
@@ -0,0 +1,25 @@
1
+ import pandas as pd
2
+
3
+ from jsp_vis.console import gantt_chart_console, graph_console
4
+
5
+
6
+ def test_gantt_console(custom_jsp_instance_df, custom_jsp_n_machines):
7
+ gantt_chart_console(pd.DataFrame(custom_jsp_instance_df), custom_jsp_n_machines)
8
+
9
+
10
+ def test_gantt_console_ft06(ft06_df, ft06_n_machines):
11
+ gantt_chart_console(pd.DataFrame(ft06_df), ft06_n_machines)
12
+
13
+
14
+ def test_graph_console(custom_jsp_instance, custom_jsp_instance_df):
15
+ graph_console(
16
+ df=pd.DataFrame(custom_jsp_instance_df),
17
+ jsp_instance=custom_jsp_instance,
18
+ )
19
+
20
+
21
+ def test_graph_console_ft06(ft06, ft06_df):
22
+ graph_console(
23
+ df=pd.DataFrame(ft06_df),
24
+ jsp_instance=ft06,
25
+ )
@@ -0,0 +1,30 @@
1
+ import sys
2
+
3
+ import pytest
4
+
5
+
6
+ @pytest.mark.skipif(sys.platform == 'linux',
7
+ reason="the Github Actions runner is configured to run on Linux. "
8
+ "The runner does not have a UI, so the test will fail. ")
9
+ def test_render_cv2_window(custom_jsp_instance_df, custom_jsp_n_machines):
10
+ from jsp_vis.cv2_window import render_gantt_in_window
11
+
12
+ render_gantt_in_window(
13
+ df=custom_jsp_instance_df,
14
+ n_machines=custom_jsp_n_machines,
15
+ wait=1 # time in ms that the `cv2`-window is open.
16
+ # wait=None # ''None'' will keep the window open till a keyboard occurs.
17
+ )
18
+
19
+ @pytest.mark.skipif(sys.platform == 'linux',
20
+ reason="the Github Actions runner is configured to run on Linux. "
21
+ "The runner does not have a UI, so the test will fail. ")
22
+ def test_render_cv2_window(ft06_df, ft06_n_machines):
23
+ from jsp_vis.cv2_window import render_gantt_in_window
24
+
25
+ render_gantt_in_window(
26
+ df=ft06_df,
27
+ n_machines=ft06_n_machines,
28
+ wait=1 # time in ms that the `cv2`-window is open.
29
+ # wait=None # ''None'' will keep the window open till a keyboard occurs.
30
+ )
@@ -0,0 +1,17 @@
1
+ import numpy as np
2
+
3
+
4
+ def test_rgb_array_custom_jsp_instance(custom_jsp_instance_df, custom_jsp_n_machines):
5
+ from jsp_vis.rgb_array import gantt_chart_rgb_array
6
+
7
+ vis = gantt_chart_rgb_array(custom_jsp_instance_df, custom_jsp_n_machines)
8
+ assert type(vis) == type(np.array([]))
9
+ assert vis.shape == (400, 600, 3)
10
+
11
+
12
+ def test_rgb_array_ft06(ft06_df, ft06_n_machines):
13
+ from jsp_vis.rgb_array import gantt_chart_rgb_array
14
+
15
+ vis = gantt_chart_rgb_array(ft06_df, ft06_n_machines)
16
+ assert type(vis) == type(np.array([]))
17
+ assert vis.shape == (400, 600, 3)