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 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 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 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