Source code for cpmpy.tools.io.jsplib

#!/usr/bin/env python
#-*- coding:utf-8 -*-
##
## jsplib.py
##
"""
Loader for the JSPLib format.

More can be read about it here: 

- https://github.com/tamy0612/JSPLIB


=================
List of functions
=================

.. autosummary::
    :nosignatures:

    load_jsplib
"""


import os
import builtins
import cpmpy as cp
import numpy as np
from typing import Union, Callable, TextIO

from cpmpy.expressions.variables import NDVarArray, _IntVarImpl
from cpmpy.tools.io.utils import _handle_loader_input


[docs] def load_jsplib(jsp: Union[str, os.PathLike, TextIO], open:Callable=builtins.open) -> cp.Model: """ Loader for JSPLib format. Loads an instance and returns its matching CPMpy model. Arguments: jsp (str or os.PathLike or TextIO): - A file path to a JSPlib file, or - A string containing the JSPLib content directly, or - A TextIO object already open for reading open (Callable): If jsp is the path to a file, a callable to "open" that file (default=python standard library's 'open'). Returns: cp.Model: The CPMpy model of the JSPLib instance. """ task_to_machines, task_durations = _parse_jsplib(jsp, open=open) model, (start, makespan) = _model_jsplib(task_to_machines=task_to_machines, task_durations=task_durations) return model
def _parse_jsplib(instance: Union[str, os.PathLike, TextIO], open: Callable = builtins.open) -> tuple[np.ndarray, np.ndarray]: """ Parse a JSPLib instance file Arguments: instance (str or os.PathLike or TextIO): - A file path to a JSPLib file, or - A string containing the JSPLib content directly, or - A TextIO object already open for reading open (Callable): If instance is the path to a file, a callable to "open" that file (default=python standard library's 'open'). Returns: tuple[np.ndarray, np.ndarray]: Two matrices: - task to machines indicating on which machine to run which task - task durations: indicating the duration of each task """ with _handle_loader_input(instance, open=open) as f: line = f.readline() while line.startswith("#"): line = f.readline() n_jobs, n_tasks = map(int, line.strip().split(" ")) matrix = np.fromstring(f.read(), sep=" ", dtype=int).reshape((n_jobs, n_tasks*2)) task_to_machines = np.empty(dtype=int, shape=(n_jobs, n_tasks)) task_durations = np.empty(dtype=int, shape=(n_jobs, n_tasks)) for t in range(n_tasks): task_to_machines[:, t] = matrix[:, t*2] task_durations[:, t] = matrix[:, t*2+1] return task_to_machines, task_durations def _model_jsplib(task_to_machines: np.ndarray, task_durations: np.ndarray) -> tuple[cp.Model, tuple[NDVarArray, _IntVarImpl]]: """ Model a JSPLib instance Arguments: task_to_machines (np.ndarray): The task to machines matrix task_durations (np.ndarray): The task durations matrix Returns: tuple[cp.Model, tuple[NDVarArray, _IntVarImpl]]: The model and the start and makespan variables Raises: AssertionError: If the shapes of the matrices are not compatible """ # Check if the shapes of the matrices are compatible assert task_to_machines.shape == task_durations.shape n_jobs, n_tasks = task_to_machines.shape horizon = task_durations.sum() # TODO: improve with better upper bound? start = cp.intvar(0, horizon, name="start", shape=(n_jobs,n_tasks)) end = cp.intvar(0, horizon, name="end", shape=(n_jobs,n_tasks)) makespan = cp.intvar(0, horizon, name="makespan") model = cp.Model() model.add(end[:,:-1] <= start[:,1:]) # precedences for machine in set(task_to_machines.flat): model.add(cp.NoOverlap(start[task_to_machines == machine], task_durations[task_to_machines == machine], end[task_to_machines == machine])) model.add(end <= makespan) model.minimize(makespan) return model, (start, makespan)