job-shop-lib 1.0.0b2__py3-none-any.whl → 1.0.0b4__py3-none-any.whl

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.
job_shop_lib/__init__.py CHANGED
@@ -19,7 +19,7 @@ from job_shop_lib._schedule import Schedule
19
19
  from job_shop_lib._base_solver import BaseSolver, Solver
20
20
 
21
21
 
22
- __version__ = "1.0.0-b.2"
22
+ __version__ = "1.0.0-b.4"
23
23
 
24
24
  __all__ = [
25
25
  "Operation",
@@ -8,9 +8,9 @@ The main classes and functions available in this package are:
8
8
  NodeType
9
9
  build_disjunctive_graph
10
10
  build_solved_disjunctive_graph
11
- build_agent_task_graph
12
- build_complete_agent_task_graph
13
- build_agent_task_graph_with_jobs
11
+ build_resource_task_graph
12
+ build_complete_resource_task_graph
13
+ build_resource_task_graph_with_jobs
14
14
 
15
15
  """
16
16
 
@@ -12,6 +12,9 @@
12
12
  IdleTimeReward
13
13
  RenderConfig
14
14
  add_padding
15
+ create_edge_type_dict
16
+ ResourceTaskGraphObservation
17
+ ResourceTaskGraphObservationDict
15
18
 
16
19
  """
17
20
 
@@ -27,7 +30,11 @@ from job_shop_lib.reinforcement_learning._reward_observers import (
27
30
  IdleTimeReward,
28
31
  )
29
32
 
30
- from job_shop_lib.reinforcement_learning._utils import add_padding
33
+ from job_shop_lib.reinforcement_learning._utils import (
34
+ add_padding,
35
+ create_edge_type_dict,
36
+ map_values,
37
+ )
31
38
 
32
39
  from job_shop_lib.reinforcement_learning._single_job_shop_graph_env import (
33
40
  SingleJobShopGraphEnv,
@@ -35,6 +42,9 @@ from job_shop_lib.reinforcement_learning._single_job_shop_graph_env import (
35
42
  from job_shop_lib.reinforcement_learning._multi_job_shop_graph_env import (
36
43
  MultiJobShopGraphEnv,
37
44
  )
45
+ from ._resource_task_graph_observation import (
46
+ ResourceTaskGraphObservation, ResourceTaskGraphObservationDict
47
+ )
38
48
 
39
49
 
40
50
  __all__ = [
@@ -47,4 +57,8 @@ __all__ = [
47
57
  "ObservationDict",
48
58
  "add_padding",
49
59
  "MultiJobShopGraphEnv",
60
+ "create_edge_type_dict",
61
+ "ResourceTaskGraphObservation",
62
+ "map_values",
63
+ "ResourceTaskGraphObservationDict",
50
64
  ]
@@ -235,13 +235,13 @@ class MultiJobShopGraphEnv(gym.Env):
235
235
  @ready_operations_filter.setter
236
236
  def ready_operations_filter(
237
237
  self,
238
- pruning_function: Callable[
238
+ ready_operations_filter: Callable[
239
239
  [Dispatcher, List[Operation]], List[Operation]
240
240
  ],
241
241
  ) -> None:
242
242
  """Sets the ready operations filter."""
243
243
  self.single_job_shop_graph_env.dispatcher.ready_operations_filter = (
244
- pruning_function
244
+ ready_operations_filter
245
245
  )
246
246
 
247
247
  @property
@@ -0,0 +1,258 @@
1
+ """Contains wrappers for the environments."""
2
+
3
+ from typing import TypeVar, TypedDict
4
+ from gymnasium import ObservationWrapper
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+
8
+ from job_shop_lib.reinforcement_learning import (
9
+ ObservationDict,
10
+ SingleJobShopGraphEnv,
11
+ MultiJobShopGraphEnv,
12
+ create_edge_type_dict,
13
+ map_values,
14
+ )
15
+ from job_shop_lib.graphs import NodeType, JobShopGraph
16
+ from job_shop_lib.exceptions import ValidationError
17
+ from job_shop_lib.dispatching.feature_observers import FeatureType
18
+
19
+ T = TypeVar("T", bound=np.number)
20
+
21
+
22
+ class ResourceTaskGraphObservationDict(TypedDict):
23
+ """Represents a dictionary for resource task graph observations."""
24
+
25
+ edge_index_dict: dict[str, NDArray[np.int64]]
26
+ node_features_dict: dict[str, NDArray[np.float32]]
27
+ original_ids_dict: dict[str, NDArray[np.int32]]
28
+
29
+
30
+ # pylint: disable=line-too-long
31
+ class ResourceTaskGraphObservation(ObservationWrapper):
32
+ """Observation wrapper that converts an observation following the
33
+ :class:`ObservationDict` format to a format suitable to PyG's
34
+ [`HeteroData`](https://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.data.HeteroData.html).
35
+
36
+ In particular, the ``edge_index`` is converted into a ``edge_index_dict``
37
+ with keys ``(node_type_i, "to", node_type_j)``. The ``node_type_i`` and
38
+ ``node_type_j`` are the node types of the source and target nodes,
39
+ respectively.
40
+
41
+ Attributes:
42
+ global_to_local_id: A dictionary mapping global node IDs to local node
43
+ IDs for each node type.
44
+ type_ranges: A dictionary mapping node type names to (start, end) index
45
+ ranges.
46
+
47
+ Args:
48
+ env: The environment to wrap.
49
+ """
50
+
51
+ def __init__(self, env: SingleJobShopGraphEnv | MultiJobShopGraphEnv):
52
+ super().__init__(env)
53
+ self.global_to_local_id = self._compute_id_mappings()
54
+ self.type_ranges = self._compute_node_type_ranges()
55
+
56
+ @property
57
+ def job_shop_graph(self) -> JobShopGraph:
58
+ """Returns the job shop graph from the environment.
59
+
60
+ Raises:
61
+ ValidationError: If the environment is not an instance of
62
+ ``SingleJobShopGraphEnv`` or ``MultiJobShopGraphEnv``.
63
+ """
64
+ if isinstance(self.env, (SingleJobShopGraphEnv, MultiJobShopGraphEnv)):
65
+ return self.env.job_shop_graph
66
+ raise ValidationError(
67
+ "The environment must be an instance of "
68
+ "SingleJobShopGraphEnv or MultiJobShopGraphEnv"
69
+ )
70
+
71
+ def step(self, action: tuple[int, int]):
72
+ """Takes a step in the environment.
73
+
74
+ Args:
75
+ action:
76
+ The action to take. The action is a tuple of two integers
77
+ (job_id, machine_id):
78
+ the job ID and the machine ID in which to schedule the
79
+ operation.
80
+
81
+ Returns:
82
+ A tuple containing the following elements:
83
+
84
+ - The observation of the environment.
85
+ - The reward obtained.
86
+ - Whether the environment is done.
87
+ - Whether the episode was truncated (always False).
88
+ - A dictionary with additional information. The dictionary
89
+ contains the following keys: "feature_names", the names of the
90
+ features in the observation; and "available_operations_with_ids",
91
+ a list of available actions in the form of (operation_id,
92
+ machine_id, job_id).
93
+ """
94
+ observation, reward, done, truncated, info = self.env.step(action)
95
+ return self.observation(observation), reward, done, truncated, info
96
+
97
+ def reset(self, *, seed: int | None = None, options: dict | None = None):
98
+ """Resets the environment.
99
+
100
+ Args:
101
+ seed:
102
+ Added to match the signature of the parent class. It is not
103
+ used in this method.
104
+ options:
105
+ Additional options to pass to the environment. Not used in
106
+ this method.
107
+
108
+ Returns:
109
+ A tuple containing the following elements:
110
+
111
+ - The observation of the environment.
112
+ - A dictionary with additional information, keys
113
+ include: "feature_names", the names of the features in the
114
+ observation; and "available_operations_with_ids", a list of
115
+ available a list of available actions in the form of
116
+ (operation_id, machine_id, job_id).
117
+ """
118
+ observation, info = self.env.reset()
119
+ return self.observation(observation), info
120
+
121
+ def _compute_id_mappings(self) -> dict[int, int]:
122
+ """Computes mappings from global node IDs to type-local IDs.
123
+
124
+ Returns:
125
+ A dictionary mapping global node IDs to local node IDs for each
126
+ node type.
127
+ """
128
+ mappings = {}
129
+ for node_type in NodeType:
130
+ type_nodes = self.job_shop_graph.nodes_by_type[node_type]
131
+ if not type_nodes:
132
+ continue
133
+ # Create mapping from global ID to local ID
134
+ # (0 to len(type_nodes)-1)
135
+ type_mapping = {
136
+ node.node_id: local_id
137
+ for local_id, node in enumerate(type_nodes)
138
+ }
139
+ mappings.update(type_mapping)
140
+
141
+ return mappings
142
+
143
+ def _compute_node_type_ranges(self) -> dict[str, tuple[int, int]]:
144
+ """Computes index ranges for each node type.
145
+
146
+ Returns:
147
+ Dictionary mapping node type names to (start, end) index ranges
148
+ """
149
+ type_ranges = {}
150
+ for node_type in NodeType:
151
+ type_nodes = self.job_shop_graph.nodes_by_type[node_type]
152
+ if not type_nodes:
153
+ continue
154
+ start = min(node.node_id for node in type_nodes)
155
+ end = max(node.node_id for node in type_nodes) + 1
156
+ type_ranges[node_type.name.lower()] = (start, end)
157
+
158
+ return type_ranges
159
+
160
+ def observation(self, observation: ObservationDict):
161
+ edge_index_dict = create_edge_type_dict(
162
+ observation["edge_index"],
163
+ type_ranges=self.type_ranges,
164
+ relationship="to",
165
+ )
166
+ # mapping from global node ID to local node ID
167
+ for key, edge_index in edge_index_dict.items():
168
+ edge_index_dict[key] = map_values(
169
+ edge_index, self.global_to_local_id
170
+ )
171
+ node_features_dict = self._create_node_features_dict(observation)
172
+ node_features_dict, original_ids_dict = self._remove_nodes(
173
+ node_features_dict, observation["removed_nodes"]
174
+ )
175
+
176
+ return {
177
+ "edge_index_dict": edge_index_dict,
178
+ "node_features_dict": node_features_dict,
179
+ "original_ids_dict": original_ids_dict,
180
+ }
181
+
182
+ def _create_node_features_dict(
183
+ self, observation: ObservationDict
184
+ ) -> dict[str, NDArray]:
185
+ """Creates a dictionary of node features for each node type.
186
+
187
+ Args:
188
+ observation: The observation dictionary.
189
+
190
+ Returns:
191
+ Dictionary mapping node type names to node features.
192
+ """
193
+ node_type_to_feature_type = {
194
+ NodeType.OPERATION: FeatureType.OPERATIONS,
195
+ NodeType.MACHINE: FeatureType.MACHINES,
196
+ NodeType.JOB: FeatureType.JOBS,
197
+ }
198
+ node_features_dict = {}
199
+ for node_type, feature_type in node_type_to_feature_type.items():
200
+ if node_type in self.job_shop_graph.nodes_by_type:
201
+ node_features_dict[feature_type.value] = observation[
202
+ feature_type.value
203
+ ]
204
+ continue
205
+ if feature_type != FeatureType.JOBS:
206
+ continue
207
+ assert FeatureType.OPERATIONS.value in observation
208
+ job_features = observation[
209
+ feature_type.value # type: ignore[literal-required]
210
+ ]
211
+ job_ids_of_ops = [
212
+ node.operation.job_id
213
+ for node in self.job_shop_graph.nodes_by_type[
214
+ NodeType.OPERATION
215
+ ]
216
+ ]
217
+ job_features_expanded = job_features[job_ids_of_ops]
218
+ operation_features = observation[FeatureType.OPERATIONS.value]
219
+ node_features_dict[FeatureType.OPERATIONS.value] = np.concatenate(
220
+ (operation_features, job_features_expanded), axis=1
221
+ )
222
+ return node_features_dict
223
+
224
+ def _remove_nodes(
225
+ self,
226
+ node_features_dict: dict[str, NDArray[np.float32]],
227
+ removed_nodes: NDArray[np.bool_],
228
+ ) -> tuple[dict[str, NDArray[np.float32]], dict[str, NDArray[np.int32]]]:
229
+ """Removes nodes from the node features dictionary.
230
+
231
+ Args:
232
+ node_features_dict: The node features dictionary.
233
+
234
+ Returns:
235
+ The node features dictionary with the nodes removed and a
236
+ dictionary containing the original node ids.
237
+ """
238
+ removed_nodes_dict: dict[str, NDArray[np.float32]] = {}
239
+ original_ids_dict: dict[str, NDArray[np.int32]] = {}
240
+ feature_type_to_node_type = {
241
+ FeatureType.OPERATIONS.value: NodeType.OPERATION,
242
+ FeatureType.MACHINES.value: NodeType.MACHINE,
243
+ FeatureType.JOBS.value: NodeType.JOB,
244
+ }
245
+ for feature_type, features in node_features_dict.items():
246
+ node_type = feature_type_to_node_type[feature_type].name.lower()
247
+ if node_type not in self.type_ranges:
248
+ continue
249
+ start, end = self.type_ranges[node_type]
250
+ removed_nodes_of_this_type = removed_nodes[start:end]
251
+ removed_nodes_dict[node_type] = features[
252
+ ~removed_nodes_of_this_type
253
+ ]
254
+ original_ids_dict[node_type] = np.where(
255
+ ~removed_nodes_of_this_type
256
+ )[0]
257
+
258
+ return removed_nodes_dict, original_ids_dict
@@ -243,8 +243,27 @@ class SingleJobShopGraphEnv(gym.Env):
243
243
  *,
244
244
  seed: Optional[int] = None,
245
245
  options: Optional[Dict[str, Any]] = None,
246
- ) -> Tuple[ObservationDict, dict]:
247
- """Resets the environment."""
246
+ ) -> Tuple[ObservationDict, dict[str, Any]]:
247
+ """Resets the environment.
248
+
249
+ Args:
250
+ seed:
251
+ Added to match the signature of the parent class. It is not
252
+ used in this method.
253
+ options:
254
+ Additional options to pass to the environment. Not used in
255
+ this method.
256
+
257
+ Returns:
258
+ A tuple containing the following elements:
259
+
260
+ - The observation of the environment.
261
+ - A dictionary with additional information, keys
262
+ include: "feature_names", the names of the features in the
263
+ observation; and "available_operations_with_ids", a list of
264
+ available a list of available actions in the form of
265
+ (operation_id, machine_id, job_id).
266
+ """
248
267
  super().reset(seed=seed, options=options)
249
268
  self.dispatcher.reset()
250
269
  obs = self.get_observation()
@@ -1,6 +1,6 @@
1
1
  """Utility functions for reinforcement learning."""
2
2
 
3
- from typing import TypeVar, Any, Tuple, Optional, Type
3
+ from typing import TypeVar, Any, Type
4
4
 
5
5
  import numpy as np
6
6
  from numpy.typing import NDArray
@@ -12,9 +12,9 @@ T = TypeVar("T", bound=np.number)
12
12
 
13
13
  def add_padding(
14
14
  array: NDArray[Any],
15
- output_shape: Tuple[int, ...],
15
+ output_shape: tuple[int, ...],
16
16
  padding_value: float = -1,
17
- dtype: Optional[Type[T]] = None,
17
+ dtype: Type[T] | None = None,
18
18
  ) -> NDArray[T]:
19
19
  """Adds padding to the array.
20
20
 
@@ -90,6 +90,80 @@ def add_padding(
90
90
  return padded_array
91
91
 
92
92
 
93
+ def create_edge_type_dict(
94
+ edge_index: NDArray[T],
95
+ type_ranges: dict[str, tuple[int, int]],
96
+ relationship: str = "to",
97
+ ) -> dict[tuple[str, str, str], NDArray[T]]:
98
+ """Organizes edges based on node types.
99
+
100
+ Args:
101
+ edge_index:
102
+ numpy array of shape (2, E) where E is number of edges
103
+ type_ranges: dict[str, tuple[int, int]]
104
+ Dictionary mapping type names to their corresponding index ranges
105
+ [start, end) in the ``edge_index`` array.
106
+ relationship:
107
+ A string representing the relationship type between nodes.
108
+
109
+ Returns:
110
+ A dictionary with keys (type_i, relationship, type_j) and values as
111
+ edge indices
112
+ """
113
+ edge_index_dict: dict[tuple[str, str, str], NDArray] = {}
114
+ for type_name_i, (start_i, end_i) in type_ranges.items():
115
+ for type_name_j, (start_j, end_j) in type_ranges.items():
116
+ key: tuple[str, str, str] = (
117
+ type_name_i,
118
+ relationship,
119
+ type_name_j,
120
+ )
121
+ # Find edges where source is in type_i and target is in type_j
122
+ mask = (
123
+ (edge_index[0] >= start_i)
124
+ & (edge_index[0] < end_i)
125
+ & (edge_index[1] >= start_j)
126
+ & (edge_index[1] < end_j)
127
+ )
128
+ edge_index_dict[key] = edge_index[:, mask]
129
+
130
+ return edge_index_dict
131
+
132
+
133
+ def map_values(array: NDArray[T], mapping: dict[int, int]) -> NDArray[T]:
134
+ """Maps values in an array using a mapping.
135
+
136
+ Args:
137
+ array:
138
+ An NumPy array.
139
+
140
+ Returns:
141
+ A NumPy array where each element has been replaced by its
142
+ corresponding value from the mapping.
143
+
144
+ Raises:
145
+ ValidationError:
146
+ If the array contains values that are not in the mapping.
147
+
148
+ Examples:
149
+ >>> map_values(np.array([1, 2, 3]), {1: 10, 2: 20, 3: 30})
150
+ array([10, 20, 30])
151
+
152
+ >>> map_values(np.array([1, 2]), {1: 10, 2: 10, 3: 30})
153
+ array([10, 10])
154
+
155
+ """
156
+ if array.size == 0:
157
+ return array
158
+ try:
159
+ vectorized_mapping = np.vectorize(mapping.get)
160
+ return vectorized_mapping(array)
161
+ except TypeError as e:
162
+ raise ValidationError(
163
+ "The array contains values that are not in the mapping."
164
+ ) from e
165
+
166
+
93
167
  if __name__ == "__main__":
94
168
  import doctest
95
169
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: job-shop-lib
3
- Version: 1.0.0b2
3
+ Version: 1.0.0b4
4
4
  Summary: An easy-to-use and modular Python library for the Job Shop Scheduling Problem (JSSP)
5
5
  License: MIT
6
6
  Author: Pabloo22
@@ -60,7 +60,7 @@ See [this](https://colab.research.google.com/drive/1XV_Rvq1F2ns6DFG8uNj66q_rcoww
60
60
  Version 1.0.0 is currently in beta stage and can be installed with:
61
61
 
62
62
  ```bash
63
- pip install job-shop-lib==1.0.0b2
63
+ pip install job-shop-lib==1.0.0b4
64
64
  ```
65
65
 
66
66
  Although this version is not stable and may contain breaking changes in subsequent releases, it is recommended to install it to access the new reinforcement learning environments and familiarize yourself with new changes (see the [latest pull requests](https://github.com/Pabloo22/job_shop_lib/pulls?q=is%3Apr+is%3Aclosed)). There is a [documentation page](https://job-shop-lib.readthedocs.io/en/latest/) for versions 1.0.0a3 and onward.
@@ -89,11 +89,7 @@ Although this version is not stable and may contain breaking changes in subseque
89
89
  - **Agent-Task Graphs**: Encode instances as agent-task graphs (introduced in [ScheduleNet paper](https://arxiv.org/abs/2106.03051)). See [Agent-Task Graph](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/07-Agent-Task-Graph.ipynb).
90
90
  - Build your own custom graphs with the `JobShopGraph` class.
91
91
 
92
- <<<<<<< HEAD
93
92
  - **Gymnasium Environments**: Two environments for solving the problem with graph neural networks (GNNs) or any other method, and reinforcement learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
94
- =======
95
- - **Gymnasium Environments**: Two environments for solving the problem with Graph Neural Networks (GNNs) or any other method, and Reinforcement Learning (RL). See [SingleJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/09-SingleJobShopGraphEnv.ipynb) and [MultiJobShopGraphEnv](https://github.com/Pabloo22/job_shop_lib/blob/main/docs/source/examples/10-MultiJobShopGraphEnv.ipynb).
96
- >>>>>>> 031bdf3 ([Docs] Update links in README to point to the correct GitHub URLs for tutorials and examples)
97
93
 
98
94
  <!-- end key features -->
99
95
 
@@ -1,4 +1,4 @@
1
- job_shop_lib/__init__.py,sha256=xXYzjYqn-98QKOMQ4_fOBZYQ6Z6GUydDW5BNXiVSRCE,643
1
+ job_shop_lib/__init__.py,sha256=I9Ifdoq10hPCyLVpLFUKHTjSldDRCqJx-raUQKid0aw,643
2
2
  job_shop_lib/_base_solver.py,sha256=p17XmtufNc9Y481cqZUT45pEkUmmW1HWG53dfhIBJH8,1363
3
3
  job_shop_lib/_job_shop_instance.py,sha256=hNQGSJj0rEQpS-YhzwWmM6QzCWp6r--89jkghSgLvUs,18380
4
4
  job_shop_lib/_operation.py,sha256=hx2atpP8LPj9fvxpZIfhBFr9Uq6JP-MKAX5JzTvFXso,3847
@@ -40,7 +40,7 @@ job_shop_lib/generation/_general_instance_generator.py,sha256=e-NDkH-NoCwa14oADj
40
40
  job_shop_lib/generation/_instance_generator.py,sha256=VV0OKX4JgFq3I1EY6s3LrOdPjM3v4lH6S1hkUebTkFQ,4615
41
41
  job_shop_lib/generation/_transformations.py,sha256=X-hTAJVIHZ3bmF1rqS0zCit8r5SGpHpV8Fcl92fejow,5336
42
42
  job_shop_lib/generation/_utils.py,sha256=cBhGILE0FE3TqvWoHqpaFEffO8D2fb869pF-BdMlYsg,3617
43
- job_shop_lib/graphs/__init__.py,sha256=zw4aOE-7QF8Lt8316rCUwOEAGqznjiijumTlGqBmfuw,1840
43
+ job_shop_lib/graphs/__init__.py,sha256=RI9vGUR_89X34UoHpl1HYZpxT8JOWg8dwZTe5ImDCVg,1849
44
44
  job_shop_lib/graphs/_build_disjunctive_graph.py,sha256=UbUYdeQaaeEqLchcKJGHEFGl4wElfGLb1o_R-u8wqnA,5120
45
45
  job_shop_lib/graphs/_build_resource_task_graphs.py,sha256=GHUHkUNPxVf1miScgPPMe2YqlXFEMxIy5cDhNw7OZ1E,6954
46
46
  job_shop_lib/graphs/_constants.py,sha256=K-GeVvh_DTWpo1KOX1clmxWS_pkUJbq19yOBmrCVIxI,1086
@@ -50,12 +50,13 @@ job_shop_lib/graphs/graph_updaters/__init__.py,sha256=UhnZL55e3cAv7hVetB6bRmIOn8
50
50
  job_shop_lib/graphs/graph_updaters/_graph_updater.py,sha256=j1f7iWsa62GVszK2BPaMxnKBCEGWa9owm8g4VWUje8w,1967
51
51
  job_shop_lib/graphs/graph_updaters/_residual_graph_updater.py,sha256=SfgmDyMwfW56OBjJPaU76c42IsX5qx9j-eMtrv0DjKk,6047
52
52
  job_shop_lib/graphs/graph_updaters/_utils.py,sha256=X5YfwJA1CCgpm1r9C036Gal2CkDh2SSak7wl7TbdjHw,704
53
- job_shop_lib/reinforcement_learning/__init__.py,sha256=gOY-C6BMeFr3084MKMMbW0CoK7gMsaOYNsgnYuepswQ,1033
54
- job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py,sha256=ib1Y6cItVvId4PfesiQ0XKbh9y6h8LVhD0gYDO4wSlk,15732
53
+ job_shop_lib/reinforcement_learning/__init__.py,sha256=opqJyVJ6VPyeaQOQr4hmUTkiUAXOi5tbyCnuNw5jLTI,1421
54
+ job_shop_lib/reinforcement_learning/_multi_job_shop_graph_env.py,sha256=memQefVWqatRNodt8hBXVvFcgQKJRmnuB8AWqiDl8_k,15746
55
+ job_shop_lib/reinforcement_learning/_resource_task_graph_observation.py,sha256=C4sEssy4TqHdr6nLJlg7Cdg48RBRSvmkYDzG1vaTf6U,9920
55
56
  job_shop_lib/reinforcement_learning/_reward_observers.py,sha256=iWHccnujeAKyTQn2ilQ4BhcEccoSTyJqQ5yOiP5GG_Y,2984
56
- job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py,sha256=DZnXeXmzMGKq-vwFhxukmSDN1UyrkUfbnjpjFtC9_Bs,15845
57
+ job_shop_lib/reinforcement_learning/_single_job_shop_graph_env.py,sha256=3mljeI4k9haLDuDZZhv4NcLpb1_xOlQNdEqMoRsh6bw,16592
57
58
  job_shop_lib/reinforcement_learning/_types_and_constants.py,sha256=xozdM_Wabdbe9e1a769p5980OSNBwQqc9yyaSGW2ODQ,1743
58
- job_shop_lib/reinforcement_learning/_utils.py,sha256=ksg2ghSncxd0K3AR5hGS5PQejjd-Hgx6LGtXX_oatOc,2523
59
+ job_shop_lib/reinforcement_learning/_utils.py,sha256=giikAj9Xl2f_cYq_AKCrgHn79TRLfGhYNqXz7bEnKuk,4827
59
60
  job_shop_lib/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
61
  job_shop_lib/visualization/gantt/__init__.py,sha256=HGXwRgDuMAldqU0JBdiZCd5e79XBz1r96qHeDVlzE54,1145
61
62
  job_shop_lib/visualization/gantt/_gantt_chart_creator.py,sha256=LTsVhpB1Fb_2o08HRZPPXSekwzR7fyTSC6h549XMqhU,8638
@@ -64,7 +65,7 @@ job_shop_lib/visualization/gantt/_plot_gantt_chart.py,sha256=9-NSSNsVcW8gYLZtAuF
64
65
  job_shop_lib/visualization/graphs/__init__.py,sha256=282hZFg07EyQu4HVt4GzFfYnY6ZF376IMjnWZ5eg0ZQ,611
65
66
  job_shop_lib/visualization/graphs/_plot_disjunctive_graph.py,sha256=4VBMYiFXXkCGSnGYN9iqNtWrbLJQxAMHojPHhAbdA0s,14387
66
67
  job_shop_lib/visualization/graphs/_plot_resource_task_graph.py,sha256=RgJqHS5hJh3KkyaLbtpG_bER981BFRwGpflz7I7gS64,13271
67
- job_shop_lib-1.0.0b2.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
68
- job_shop_lib-1.0.0b2.dist-info/METADATA,sha256=_6waZfwgtsPOyvZ5rw18aulHrqEOJji5jcde9NrQBpg,16978
69
- job_shop_lib-1.0.0b2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
70
- job_shop_lib-1.0.0b2.dist-info/RECORD,,
68
+ job_shop_lib-1.0.0b4.dist-info/LICENSE,sha256=9mggivMGd5taAu3xbmBway-VQZMBzurBGHofFopvUsQ,1069
69
+ job_shop_lib-1.0.0b4.dist-info/METADATA,sha256=9tz-kjhEJIDvEjr5B69IOBI6_gREvO53E1sAN0iKFQk,16424
70
+ job_shop_lib-1.0.0b4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
71
+ job_shop_lib-1.0.0b4.dist-info/RECORD,,