def test_get_processing_time(self, idx_job, idx_machine):
        frame = JobSchedulingFrame(self.processing_times)

        fst_time = self.processing_times[idx_job][idx_machine]
        scnd_time = frame.get_processing_time(idx_job, idx_machine)

        assert fst_time == scnd_time
    def test_set_processing_times(self):
        frame = JobSchedulingFrame([[]])
        frame.set_processing_times(self.processing_times)

        expected_frame = JobSchedulingFrame(self.processing_times)

        tm.assert_js_frame(frame, expected_frame)
def slope_index_func(frame: JobSchedulingFrame, idx_job: int) -> int:
    """
    Compute slope index for `idx_job` using the method invented by Palmer, D.S.

    Parameters
    ----------
    frame: JobSchedulingFrame
    idx_job: int

    Returns
    -------
    slope index: int

    Notes
    -----
    Journal Paper:
        Palmer, D.S., 1965. Sequencing jobs through a multi-stage process in
        the minimum total time a quick method of obtaining a near optimum.
        Operations Research Quarterly 16(1), 101-107

    """
    count_machines = frame.count_machines
    slope_index = 0
    for idx_machine in range(count_machines):
        slope_index -= (count_machines - (2 * (idx_machine + 1) - 1)) * \
                        frame.get_processing_time(idx_job, idx_machine)

    return slope_index
def neh_heuristics(frame: JobSchedulingFrame) -> list:
    """
    Compute approximate solution for instance of Flow Job problem by
    NEH heuristic.

    Parameters
    ----------
    frame: JobSchedulingFrame

    Returns
    -------
    solution: list
        sequence of job index

    Notes
    -----
    Journal Paper:
        Nawaz,M., Enscore,Jr.E.E, and Ham,I. (1983) A Heuristics Algorithm for
        the m Machine, n Job Flowshop Sequencing Problem.
        Omega-International Journal of Management Science 11(1), 91-95

    """
    count_jobs = frame.count_jobs

    all_processing_times = [0] * count_jobs
    for j in range(count_jobs):
        all_processing_times[j] = frame.get_sum_processing_time(j)

    init_jobs = [j for j in range(count_jobs)]
    init_jobs.sort(key=lambda x: all_processing_times[x], reverse=True)

    solution, _ = local_search_partitial_sequence(frame, init_jobs)
    return solution
def fgh_heuristic(frame: JobSchedulingFrame, count_alpha: int = 1) -> list:
    init_jobs = [idx_job for idx_job in range(frame.count_jobs)]
    sum_times = [
        frame.get_sum_processing_time(idx_job) for idx_job in init_jobs
    ]
    tetta = sum(sum_times) / frame.count_jobs

    time_min = min(sum_times)
    time_max = max(sum_times)
    alpha_min = time_min / (time_min + tetta)
    alpha_max = time_max / (time_max + tetta)
    period = (alpha_max - alpha_min) / count_alpha

    solutions = []
    for i in range(count_alpha):
        alpha = alpha_max - period * i
        init_jobs.sort(
            key=lambda idx_job: fgh_index(sum_times[idx_job], alpha, tetta),
            reverse=True)
        solutions.append(copy(init_jobs))

    for i in range(count_alpha):
        solutions[i], _ = local_search_partitial_sequence(frame, solutions[i])

    solutions.sort(key=lambda solution: compute_end_time(frame, solution))
    return solutions[0]
    def setup_method(self):
        self.frame1 = JobSchedulingFrame([[17, 19, 13], [15, 11, 12],
                                          [14, 21, 16], [20, 16, 20],
                                          [16, 17, 17]])
        self.frame1_solution1 = [2, 4, 3, 0, 1]
        self.end_time_f1_s1 = 114
        self.str_f1_s1 = ('2: (0, 14), (14, 35), (35, 51),\n'
                          '4: (14, 30), (35, 52), (52, 69),\n'
                          '3: (30, 50), (52, 68), (69, 89),\n'
                          '0: (50, 67), (68, 87), (89, 102),\n'
                          '1: (67, 82), (87, 98), (102, 114),\n')

        self.str_f1_s1_1machine = ('2: (0, 14),\n'
                                   '4: (14, 30),\n'
                                   '3: (30, 50),\n'
                                   '0: (50, 67),\n'
                                   '1: (67, 82),\n')

        self.frame1_solution2 = [4, 2, 3, 0, 1]
        self.end_time_f1_s2 = 115

        self.str_f1_s2 = ('4: (0, 16), (16, 33), (33, 50),\n'
                          '2: (16, 30), (33, 54), (54, 70),\n'
                          '3: (30, 50), (54, 70), (70, 90),\n'
                          '0: (50, 67), (70, 89), (90, 103),\n'
                          '1: (67, 82), (89, 100), (103, 115),\n')
def johnson_algorithm(frame: JobSchedulingFrame) -> list:
    """
    Compute solution for case of 2 machines of flow job problem by
    Johnson's algorithm.

    Parameters
    ----------
    frame: JobSchedulingFrame
        frame with exactly 2 machines

    Returns
    -------
    exact_solution: list
        list of job index
    """
    if frame.count_machines != 2:
        raise ValueError('count machines must be 2')

    # init job indexes
    exact_solution = [i for i in range(frame.count_jobs)]

    # sorting by increasing the minimum processing time
    exact_solution.sort(key=lambda idx_job: min(
        frame.get_processing_time(idx_job, idx_machine=0),
        frame.get_processing_time(idx_job, idx_machine=1)))

    jobs_on_first_machine = []
    jobs_on_second_machine = []

    # distribution of jobs on machines
    for idx_job in exact_solution:
        frst = frame.get_processing_time(idx_job, idx_machine=0)
        scnd = frame.get_processing_time(idx_job, idx_machine=1)
        if frst < scnd:
            jobs_on_first_machine.append(idx_job)
        else:
            jobs_on_second_machine.append(idx_job)

    exact_solution = jobs_on_first_machine

    # simulating the installation of jobs that are processed faster on the
    # second machine to the end of the processing queue on the first machine
    jobs_on_second_machine.reverse()
    exact_solution.extend(jobs_on_second_machine)

    return exact_solution
    def test_count_jobs_and_machines(self):
        frame = JobSchedulingFrame(self.processing_times)

        count_jobs = len(self.processing_times)
        count_machines = len(self.processing_times[0])

        assert frame.count_jobs == count_jobs
        assert frame.count_machines == count_machines
    def test_johnson_algorithm(self):
        frame = JobSchedulingFrame([[2, 3], [8, 3], [4, 6], [9, 5], [6, 8],
                                    [9, 7]])
        solution = johnson_algorithm(frame)
        assert solution == [0, 2, 4, 5, 3, 1]

        solution_end_time = compute_end_time(frame, solution)
        assert solution_end_time == 41
def cds_create_proc_times(frame: JobSchedulingFrame, sub_problem: int) -> list:
    """
    Create processing time matrix with 2 machines from matrix with M machines
    according to the CDS heuristic rule.

    Parameters
    ----------
    frame: JobSchedulingFrame
    sub_problem: int
        `sub_problem` values start with 1

    Returns
    -------
    matrix of processing times: list

    Notes
    -----
    Developed by Campbell, Dudek, and Smith in 1970.

    """
    processing_times = []
    count_machines = frame.count_machines

    for idx_job in range(frame.count_jobs):
        first_machine_time = 0
        second_machine_time = 0

        # From `sub_problem` count machines will make one artificial,
        # summing up the processing times on each of them.
        for idx_machine in range(sub_problem):
            first_machine_time += frame.get_processing_time(
                idx_job, idx_machine)

        # From `sub_problem` machines will make one artificial,
        # summing up the processing times on each of them.
        for idx_machine in range(count_machines - sub_problem, count_machines):
            second_machine_time += frame.get_processing_time(
                idx_job, idx_machine)

        processing_times.append([first_machine_time, second_machine_time])

    return processing_times
Exemple #11
0
    def test_str(self):
        taillard_str = ("number of jobs, number of machines, "
                        "initial seed, upper bound and lower bound :\n"
                        "          5           3"
                        "   NaN        NaN        NaN\n"
                        "processing times :\n"
                        "  17  15  14  20  16\n"
                        "  19  11  21  16  17\n"
                        "  13  12  16  20  17\n")

        assert taillard_str == str(JobSchedulingFrame(self.processing_times))
def cds_heuristics(frame: JobSchedulingFrame) -> list:
    """
    Compute approximate solution for instance of Flow Job problem by
    Campbell, Dudek, and Smith (CDS) heuristic.

    Parameters
    ----------
    frame: JobSchedulingFrame

    Returns
    -------
    solution: list
        sequence of job index

    Notes
    -----
    Developed by Campbell, Dudek, and Smith in 1970.

    """
    johnson_frame = JobSchedulingFrame([[]])
    johnson_solutions_with_end_time = []

    # Create `count_machines - 1` sub-problems
    # which will be solved by Johnson's algorithm
    for sub_problem in range(1, frame.count_machines):
        # Create processing times matrix for all jobs on only 2 machines
        proc_times = cds_create_proc_times(frame, sub_problem)
        johnson_frame.set_processing_times(proc_times)
        johnson_solution = johnson_algorithm(johnson_frame)

        # end time compute for the original task, that is `frame`
        end_time = compute_end_time(frame, johnson_solution)
        johnson_solutions_with_end_time.append((johnson_solution, end_time))

    johnson_solutions_with_end_time.sort(key=lambda elem: elem[1])

    # return only solution with minimum makespan (end_time)
    return johnson_solutions_with_end_time[0][0]
def test_cds_create_proc_times():
    processing_times = [[17, 19, 13], [15, 11, 12], [14, 21, 16], [20, 16, 20],
                        [16, 17, 17]]
    frame = JobSchedulingFrame(processing_times)
    johnson_frame = JobSchedulingFrame([[]])

    # sub problem index begin with 1
    processing_times_first = cds_create_proc_times(frame, 1)
    johnson_frame.set_processing_times(processing_times_first)

    assert_js_frame(
        johnson_frame,
        JobSchedulingFrame([[17, 13], [15, 12], [14, 16], [20, 20], [16, 17]]))

    processing_times_second = cds_create_proc_times(frame, 2)
    johnson_frame.set_processing_times(processing_times_second)

    assert_js_frame(
        johnson_frame,
        JobSchedulingFrame([[36, 32], [26, 23], [35, 37], [36, 36], [33, 34]]))
def _artificial_time(frame: JobSchedulingFrame, jobs: list,
                     unscheduled_jobs: list) -> int:
    #  copy processing time matrix jobs x machines from frame
    processing_times = frame.copy_proc_time

    #  creating processing times for artificial job as average of
    #  the processing times of jobs from `unscheduled_jobs`
    artificial_prc_times = []
    for idx_machine in range(frame.count_machines):
        average_time = 0.
        for idx_job in range(len(unscheduled_jobs)):
            average_time += processing_times[idx_job][idx_machine]

        average_time /= len(unscheduled_jobs)
        artificial_prc_times.append(round(average_time))

    processing_times.append(artificial_prc_times)

    assert frame.count_jobs + 1 == len(processing_times)
    assert frame.count_machines == len(processing_times[0])

    frame_with_artificial_job = JobSchedulingFrame(processing_times)
    jobs.append(frame.count_jobs - 1)  # added index of artificial job

    if len(jobs) != 1:
        end_time_sec_last_job = compute_end_time(frame_with_artificial_job,
                                                 jobs,
                                                 len(jobs) - 1,
                                                 frame.count_machines - 1)
    else:
        end_time_sec_last_job = 0

    end_time_last_job = compute_end_time(frame_with_artificial_job, jobs,
                                         len(jobs), frame.count_machines - 1)
    result_time = end_time_sec_last_job + end_time_last_job
    jobs.pop()
    return result_time
Exemple #15
0
    def test_bad_processing_times(self, processing_times):
        msg = 'processing_times must be list of lists of integers; same length'

        with pytest.raises(ValueError, match=msg):
            JobSchedulingFrame(processing_times)
Exemple #16
0
def read_flow_shop_instances(file_name):
    """
    Read from file with Tailard's instances to JobSchedulingFrames

    Parameters
    ----------
    file_name: str

    Returns
    -------
    Sequence of JobSchedulingFrame objects: list

    Notes
    -----
    http://mistic.heig-vd.ch/taillard/problemes.dir/ordonnancement.dir/ordonnancement.html
    file format:
        number of jobs, number of machines, initial seed,
        upper bound and lower bound :
              20           5   873654221        1278        1232
        processing times :
        54 83 15 71 77 36 53 38 27 87 76 91 14 29 12 77 32 87 68 94
        79  3 11 99 56 70 99 60  5 56  3 61 73 75 47 14 21 86  5 77
        16 89 49 15 89 45 60 23 57 64  7  1 63 41 63 47 26 75 77 40
        66 58 31 68 78 91 13 59 49 85 85  9 39 41 56 40 54 77 51 31
        58 56 20 85 53 35 53 41 69 13 86 72  8 49 47 87 58 18 68 28
    """

    file = open(file_name)
    frames = []
    counter_lines = 0
    for string in iter(file):
        counter_lines += 1
        try:
            if string.strip().startswith('number'):
                string = next(file)
                counter_lines += 1
                params = re.findall(r'\d+', string)[:4]
                if len(params) != 4:
                    raise FlowShopFormatError(file_name, counter_lines)
                count_jobs, count_machines, \
                    _, upper_bound = (int(param) for param in params)

                string = next(file)
                counter_lines += 1
                if string.strip().startswith('processing'):
                    processing_time = []
                    for _ in range(count_machines):
                        string = next(file)
                        counter_lines += 1
                        times = re.findall(r'\d+', string)
                        if len(times) != count_jobs:
                            raise FlowShopFormatError(file_name, counter_lines)
                        processing_time.append((int(time) for time in times))

                    processing_time = list(zip(*processing_time))  # transpose

                    assert count_jobs == len(processing_time)
                    assert count_machines == len(processing_time[0])

                    frames.append(
                        JobSchedulingFrame(processing_time,
                                           upper_bound=upper_bound))
                else:
                    raise FlowShopFormatError(file_name, counter_lines)
        except StopIteration:
            raise FlowShopFormatError(file_name, counter_lines)

    if len(frames) == 0:
        raise FlowShopFormatError(file_name, None)

    file.close()
    return frames
Exemple #17
0
    def test_bad_upper_bound(self, upper_bound):
        msg = 'upper_bound must be the NaN or int'

        with pytest.raises(ValueError, match=msg):
            JobSchedulingFrame(self.processing_times, upper_bound=upper_bound)
Exemple #18
0
 def test_upper_bound(self, upper_bound):
     frame = JobSchedulingFrame(self.processing_times,
                                upper_bound=upper_bound)
     assert upper_bound == frame.upper_bound
Exemple #19
0
    def test_bad_initial_seed(self, seed):
        msg = 'initial_seed must be the NaN or int'

        with pytest.raises(ValueError, match=msg):
            JobSchedulingFrame(self.processing_times, initial_seed=seed)
Exemple #20
0
 def test_initial_seed(self, seed):
     frame = JobSchedulingFrame(self.processing_times, initial_seed=seed)
     assert seed == frame.initial_seed
    def test_bad_frame(self):
        msg = 'count machines must be 2'
        frame = JobSchedulingFrame([[1, 2, 3], [4, 5, 6]])

        with pytest.raises(ValueError, match=msg):
            johnson_algorithm(frame)
def test_slope_index(idx_job, slope_index):
    processing_times = [[17, 19, 13], [15, 11, 12], [14, 21, 16], [20, 16, 20],
                        [16, 17, 17]]
    frame = JobSchedulingFrame(processing_times)

    assert slope_index == slope_index_func(frame, idx_job)