Skip to content

Commit 8c8b75d

Browse files
drQuesadaUPMPabloo22gemini-code-assist[bot]
authored
[Docs] PR #84: Improve Tutorials (#82)
* Mejora: actualizar tutorial inicial de instalación * [Docs] Fix typo in tutorial 01 Suggestion by gemini Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Mejora: actualizar tutorial * [Chore] Ignore `.DS_Store` files Remove unnecessary .DS_Store files from the repository --------- Co-authored-by: Pablo Ariño <72697714+Pabloo22@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Pabloo22 <pablete.arino@gmail.com>
1 parent ed7949f commit 8c8b75d

File tree

6 files changed

+51
-25
lines changed

6 files changed

+51
-25
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# macOS file system metadata
2+
.DS_Store
3+
14
# Development files
25
.vscode/
36
tests/notebooks

docs/source/tutorial/00-Getting-Started.ipynb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
"source": [
77
"# Getting Started with Job Shop Lib\n",
88
"\n",
9+
"Recall that the Job Shop Scheduling Problem consists **of determining the optimal sequence of operations for a set of jobs to be processed on a set of machines**, where each job is composed of a series of operations that must be performed in a specific order, and each operation requires a specific machine for a given processing time. The objective is typically to minimize a performance criterion such as the total completion time (makespan) while respecting constraints such as machine capacity (only one job can be processed on a machine at a time) and job precedence relationships.\n",
10+
"\n",
911
"The main class of the library is the `JobShopInstance` class, which stores a list of jobs and its operations.\n",
1012
"\n",
1113
"Each operation is also a class, which stores the machine(s) in which the operation can be processed and its duration (also known as processing time). Let's see an example of how to use the `JobShopInstance` class to model a JSSP instance.\n",
1214
"\n",
13-
"In this example, we model a simple Job Shop Scheduling Problem using the `JobShopInstance` class. We define three types of machines: CPU, GPU, and Data Center, each represented by a unique identifier."
15+
"In this example, we model a simple Job Shop Scheduling Problem using the `JobShopInstance` class. We define three types of machines: CPU, GPU, and Data Center, each represented by a unique identifier. This just defines the problem, later on, we will find solutions and finally we will try to optimize them."
1416
]
1517
},
1618
{

docs/source/tutorial/01-How-Solutions-are-Represented.ipynb

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"cell_type": "markdown",
1515
"metadata": {},
1616
"source": [
17-
"First, the operations from the previous example are organized by their respective machines."
17+
"Recall the previous example:"
1818
]
1919
},
2020
{
@@ -44,6 +44,13 @@
4444
")"
4545
]
4646
},
47+
{
48+
"cell_type": "markdown",
49+
"metadata": {},
50+
"source": [
51+
"First, the operations are organized by their respective machines."
52+
]
53+
},
4754
{
4855
"cell_type": "code",
4956
"execution_count": 2,
@@ -135,6 +142,13 @@
135142
"print(\"Makespan:\", schedule.makespan())"
136143
]
137144
},
145+
{
146+
"cell_type": "markdown",
147+
"metadata": {},
148+
"source": [
149+
"Now that the Job Shop Scheduling Problem has a solution, it can be represented as follows:"
150+
]
151+
},
138152
{
139153
"cell_type": "code",
140154
"execution_count": 6,
@@ -157,6 +171,13 @@
157171
"_ = plot_gantt_chart(schedule, job_labels=[\"Job 1\", \"Job 2\", \"Job 3\"])"
158172
]
159173
},
174+
{
175+
"cell_type": "markdown",
176+
"metadata": {},
177+
"source": [
178+
"Without any algorithm it is easy to improve this solution by a simple visual inspection. We can manually rearrange the job sequences to obtain a better solution."
179+
]
180+
},
160181
{
161182
"cell_type": "code",
162183
"execution_count": 7,

docs/source/tutorial/02-Solving-the-Problem.ipynb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"## Solving the Problem\n",
88
"As you can see, manually creating solutions is a tedious task and requires taking into account each constraint carefully. This is the reason the `Dispatcher` class was created. This class allow us to just define the order in which operations are sequenced and the machines in which they are processed. The `Dispatcher` class will take care of the rest.\n",
99
"\n",
10-
"Let's see an example of how to use the `Dispatcher` class to solve the previous instance. In this case, a reasonable solution is to process the operations in the order they are defined in the instance. We can do this as follows:"
10+
"Let's see an example of how to use the `Dispatcher` class to solve the previous instance. In this case, a reasonable solution is to process, for each job, the operations in the order they are defined in the instance. We can do this as follows:"
1111
]
1212
},
1313
{
@@ -47,6 +47,13 @@
4747
" dispatcher.dispatch(job_3[i], job_3[i].machine_id)"
4848
]
4949
},
50+
{
51+
"cell_type": "markdown",
52+
"metadata": {},
53+
"source": [
54+
"The dispatcher took care of any possible overlaps providing the expected solution, as we can see below"
55+
]
56+
},
5057
{
5158
"cell_type": "code",
5259
"execution_count": 3,
@@ -76,8 +83,9 @@
7683
"cell_type": "markdown",
7784
"metadata": {},
7885
"source": [
86+
"Even though the dispatcher creates solutions easily, they are crafted manually/procedurally and therefore will usually be far from optimal. It is interesting to introduce solvers that find solutions according to certain optimality criteria.\n",
7987
"\n",
80-
"A solver is any `Callable` object that takes as input a `JobShopInstance` class and returns a `Schedule` with a complete solution of the instance.\n",
88+
"A **solver** is any `Callable` object that takes as input a `JobShopInstance` class and returns a `Schedule` with a complete solution of the instance.\n",
8189
"\n",
8290
"In this example, we are going to use the `CPSolver` class, contained inside `job_shop_lib.solvers` package, which uses [CP-SAT solver from Google OR-Tools](https://developers.google.com/optimization/cp/cp_solver)."
8391
]
@@ -118,7 +126,7 @@
118126
"cell_type": "markdown",
119127
"metadata": {},
120128
"source": [
121-
"This class returns a `Schedule` object with a complete solution of the instance. It also set some metadata of the solution, such as the time it took to solve the instance and the status of the solution:"
129+
"This class returns a `Schedule` object with a complete solution of the instance. It also incorporates some metadata of the solution, such as the time it took to solve the instance and the status of the solution:"
122130
]
123131
},
124132
{
@@ -146,7 +154,7 @@
146154
"cell_type": "markdown",
147155
"metadata": {},
148156
"source": [
149-
"Finally, we can plot the gantt chart of the solution using the `plot_gantt_chart` method."
157+
"We can now plot the gantt chart of the solution provided by the solver which is not only valid and complete, but clearly better in terms of makespan."
150158
]
151159
},
152160
{

docs/source/tutorial/03-Generating-New-Problems.ipynb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"source": [
88
"# Tutorial 03 - Generating New Problems (Random Instance Generation)\n",
99
"\n",
10-
"This notebook shows how to generate random **JobShopInstance** objects using the function `modular_instance_generator`.\n",
10+
"This notebook shows how to generate random **JobShopInstance** objects using the function `modular_instance_generator`. It’s important to note that this process is not about solving a given problem, but rather about creating entirely new problems. Each JobShopInstance represents a unique problem configuration with its own jobs, machines, and processing times.\n",
11+
"\n",
12+
"Generating problem instances is a crucial step in machine learning workflows, as it provides the data required for training and evaluating algorithms under a variety of conditions.\n",
1113
"\n",
1214
"> **Deprecated:** The classes `InstanceGenerator` and `GeneralInstanceGenerator` are deprecated. Use `modular_instance_generator` instead."
1315
]

docs/source/tutorial/04-Simulated-Annealing.ipynb

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"3. **Iteratively explore neighbors:** The algorithm then iteratively explores \"neighboring\" solutions. A neighbor is a new schedule created by making a small change to the current one, for example, by swapping the order of two operations on a single machine.\n",
1717
"4. **Accept or reject new solutions:**\n",
1818
" * If the neighbor solution has a lower energy (is better), it is always accepted as the new current solution.\n",
19-
" * If the neighbor solution has a higher energy (is worse), it might still be accepted with a certain probability. This probability is higher at the beginning (when the \"temperature\" is high) and decreases over time. This allows the algorithm to escape local optima and explore a wider range of solutions.\n",
19+
" * If the neighbor solution has a higher energy (is worse), it might still be accepted with a certain probability. This probability is higher at the beginning (when a threshold called \"temperature\" is high) and decreases over time. This allows the algorithm to escape local optima and explore a wider range of solutions.\n",
2020
"5. **Cooling down:** The \"temperature\" gradually decreases, reducing the probability of accepting worse solutions. The process stops when the system has \"cooled down\" (the temperature is low) or after a certain number of iterations.\n",
2121
"\n",
2222
"## Core Components\n",
@@ -28,7 +28,7 @@
2828
" * **Neighbor Generators:** These are functions that define how to create a \"neighbor\" schedule from a current one.\n",
2929
" * **Objective Functions:** The `energy` function calculates the objective value of a schedule, which is typically the makespan plus any penalties for constraint violations.\n",
3030
"\n",
31-
"## The `SimulatedAnnealingSolver`\n",
31+
"### The `SimulatedAnnealingSolver`\n",
3232
"\n",
3333
"This is the main entry point for using the solver. When you create an instance of this class, you can configure various parameters of the annealing process:\n",
3434
"\n",
@@ -38,7 +38,7 @@
3838
" * `neighbor_generator`: The function used to generate neighboring solutions. The default is `swap_in_critical_path`.\n",
3939
" * `seed`: A random seed for reproducibility.\n",
4040
"\n",
41-
"## Neighbor Generation Strategies\n",
41+
"### Neighbor Generation Strategies\n",
4242
"\n",
4343
"A key part of the simulated annealing process is how you explore the solution space by moving from one solution to a \"neighbor\". This implementation provides three different neighbor generation strategies in `_neighbor_generators.py`:\n",
4444
"\n",
@@ -48,7 +48,7 @@
4848
"\n",
4949
"3. **`swap_in_critical_path` (Default):** This is the most sophisticated of the three. It identifies the critical path of the current schedule (the sequence of operations that determines the makespan) and looks for consecutive operations on that path that are on the same machine. It then swaps one of these pairs. The idea is that modifying the critical path is the most direct way to try to reduce the makespan. If no such pair exists, it falls back to a standard adjacent swap.\n",
5050
"\n",
51-
"## The Objective Function\n",
51+
"### The Objective Function\n",
5252
"\n",
5353
"The objective function is what the simulated annealing algorithm tries to minimize. In the context of job shop scheduling, this is typically the makespan (the total time to complete all jobs) plus any penalties for violating constraints (like deadlines).\n",
5454
"\n",
@@ -58,7 +58,7 @@
5858
"\n",
5959
"### Basic Usage\n",
6060
"\n",
61-
"This example shows how to solve a benchmark instance (\"ft06\") with a specific seed to get a reproducible result."
61+
"This example shows how to solve a specific JSSP problem, the benchmark instance \"ft06\", with a specific seed to get a reproducible result."
6262
]
6363
},
6464
{
@@ -109,7 +109,7 @@
109109
"source": [
110110
"### Using a Different Neighbor Generator\n",
111111
"\n",
112-
"Although it's not recommended, you can easily plug in a different neighbor generation strategy by passing it to the `SimulatedAnnealingSolver`'s constructor. Here's how to use `swap_adjacent_operations`:\n"
112+
"You can specify a different neighbor generator by passing it to the `SimulatedAnnealingSolver` constructor. Here, we use `swap_adjacent_operations` as an example although its usage is not recommended: it produces very local changes and often generates infeasible neighbors that require repeated retries, which slows the search and reduces effectiveness. However, for the shake of experimentation:\n"
113113
]
114114
},
115115
{
@@ -313,7 +313,6 @@
313313
"# Durations between 2 and 15 to have some variability\n",
314314
"duration_creator = get_default_duration_matrix_creator((2, 15))\n",
315315
"\n",
316-
"\n",
317316
"def deadlines_creator(duration_matrix, rng):\n",
318317
" deadlines: list[list[int]] = []\n",
319318
" for job_row in duration_matrix:\n",
@@ -326,7 +325,6 @@
326325
" deadlines.append(row)\n",
327326
" return deadlines\n",
328327
"\n",
329-
"\n",
330328
"instance_gen = modular_instance_generator(\n",
331329
" machine_matrix_creator=machine_creator,\n",
332330
" duration_matrix_creator=duration_creator,\n",
@@ -350,8 +348,6 @@
350348
"baseline_schedule = baseline_solver.solve(instance)\n",
351349
"\n",
352350
"# Helper: count deadline violations\n",
353-
"\n",
354-
"\n",
355351
"def count_deadline_violations(schedule):\n",
356352
" violations = 0\n",
357353
" for machine_sched in schedule.schedule:\n",
@@ -438,17 +434,11 @@
438434
"source": [
439435
"Note that, in this case, we needed to use a higher initial temperature to effectively explore the solution space and reduce deadline violations. Otherwise, because of the high penalty, very few solutions would be accepted, hindering the search process. In general, the more violations we expect, the higher the initial temperature should be to allow the algorithm to explore a wider range of solutions."
440436
]
441-
},
442-
{
443-
"cell_type": "markdown",
444-
"id": "51f0604d",
445-
"metadata": {},
446-
"source": []
447437
}
448438
],
449439
"metadata": {
450440
"kernelspec": {
451-
"display_name": "job-shop-lib-gOF0HMZJ-py3.12",
441+
"display_name": "Python 3",
452442
"language": "python",
453443
"name": "python3"
454444
},
@@ -462,7 +452,7 @@
462452
"name": "python",
463453
"nbconvert_exporter": "python",
464454
"pygments_lexer": "ipython3",
465-
"version": "3.12.3"
455+
"version": "3.12.11"
466456
}
467457
},
468458
"nbformat": 4,

0 commit comments

Comments
 (0)