From 6d3705ba3f7688647a96cb702a7c8e2b0c6fb33a Mon Sep 17 00:00:00 2001 From: "mhsatman@gmail.com" Date: Tue, 21 May 2024 20:20:03 +0300 Subject: [PATCH] add makespan() for calculating elapsed time in Johnson's Rule --- CHANGELOG.md | 1 + README.md | 9 +++ docs/src/algorithms.md | 5 ++ src/OperationsResearchModels.jl | 4 +- src/johnsons.jl | 75 ++++++++++++++++++++++- test/testjohnsons.jl | 103 ++++++++++++++++++++++++-------- 6 files changed, 170 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e39cbb..2e2068d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### 0.2.1 (Upcoming Release) - Typed Inf used instead of Inf64 in Johnson's rule. +- Implement makespan for Johnson's rule. ### 0.2.1 diff --git a/README.md b/README.md index 60c8745..0cfaf21 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,15 @@ julia> result.path ``` + +## Johnson's Rule for Job Scheduling (Ordering) Problems + +Function `johnson()` implements 2-machine, 3-machine, and $n$-machine versions of the Johnson's Rule. +If possible, the problem with higher number of machines can be transformed into 2-machine problems. +`makespan()` function calculates the total time elapsed in the production. Please see the documentation +for details. + + ## Simplex with iterations Suppose the problem is diff --git a/docs/src/algorithms.md b/docs/src/algorithms.md index 49f9d77..f956e60 100644 --- a/docs/src/algorithms.md +++ b/docs/src/algorithms.md @@ -65,3 +65,8 @@ OperationsResearchModels.solve(p::KnapsackProblem) ```@docs OperationsResearchModels.johnsons ``` + +### Makespan +```@docs +OperationsResearchModels.makespan +``` diff --git a/src/OperationsResearchModels.jl b/src/OperationsResearchModels.jl index e272430..c1a758e 100644 --- a/src/OperationsResearchModels.jl +++ b/src/OperationsResearchModels.jl @@ -50,7 +50,7 @@ import .CPM: CpmActivity, earliestfinishtime, longestactivity, CpmProblem, CpmRe import .CPM: PertActivity, PertProblem, PertResult import .Knapsack: KnapsackResult, KnapsackProblem import .Latex: latex -import .Johnsons: JohnsonResult, johnsons, JohnsonException +import .Johnsons: JohnsonResult, johnsons, JohnsonException, makespan export TransportationProblem, TransportationResult, balance, isbalanced, northwestcorner export Connection, ShortestPathResult, MaximumFlowResult, nodes @@ -65,7 +65,7 @@ export KnapsackResult, KnapsackProblem export Simplex export Utility export latex -export JohnsonResult, johnsons, JohnsonException +export JohnsonResult, johnsons, JohnsonException, makespan export JuMP, HiGHS diff --git a/src/johnsons.jl b/src/johnsons.jl index 4e1f1df..dfcb621 100644 --- a/src/johnsons.jl +++ b/src/johnsons.jl @@ -1,6 +1,10 @@ module Johnsons -export JohnsonResult, JohnsonException, johnsons +export JohnsonResult +export JohnsonException +export johnsons +export makespan + # TODO: Add makespan calculation @@ -14,6 +18,13 @@ struct JohnsonResult # makespan::Float64 end +struct Process + start + duration + finish +end + + """ @@ -127,4 +138,66 @@ function johnsons_nmachines(times::Matrix)::JohnsonResult return johnsons_2machines([G H]) end + + + +""" + makespan(times::Matrix, permutation::Vector{Int}) + + Given a matrix of times and a permutation of the jobs, returns the makespan of the jobs. + +# Arguments + +- `times::Matrix`: a matrix of times +- `permutation::Vector{Int}`: a permutation of the jobs + +# Returns +- `Float64`: the makespan of the jobs + +# Example + +```julia + +julia> times = Float64[ + 3 3 5; + 8 4 8; + 7 2 10; + 5 1 7; + 2 5 6 +] + +julia> result = makespan(times, [1, 4, 5, 3, 2]) +``` +""" +function makespan(times::Matrix, permutation::Vector{Int})::Float64 + + n, m = size(times) + + timetable = Array{Process,2}(undef, m, n) + + for machine_id in 1:m + for task_id in 1:n + current_task = permutation[task_id] + if machine_id == 1 + if task_id == 1 + start = 0 + else + start = timetable[machine_id, task_id-1].finish + end + else + if task_id == 1 + start = timetable[machine_id-1, task_id].finish + else + start = max(timetable[machine_id, task_id-1].finish, timetable[machine_id-1, task_id].finish) + end + end + duration = times[current_task, machine_id] + finish = start + duration + timetable[machine_id, task_id] = Process(start, duration, finish) + end + end + + return timetable[end, end].finish +end + end # end of module Johnsons \ No newline at end of file diff --git a/test/testjohnsons.jl b/test/testjohnsons.jl index aa40479..46251f5 100644 --- a/test/testjohnsons.jl +++ b/test/testjohnsons.jl @@ -17,7 +17,7 @@ end - @testset "Example 2 with 2-machines" begin + @testset "Example 2 with 2-machines" begin times = Float64[ 4 7; 8 3; @@ -29,63 +29,75 @@ result = johnsons(times) + time_elapsed = makespan(times, result.permutation) + @test result.permutation == [1, 3, 5, 6, 4, 2] - end + + @test time_elapsed == 41 + end end - @testset "Three machines" verbose = true begin + @testset "Three machines" verbose = true begin - @testset "Example 1 for 3-machines" begin + @testset "Example 1 for 3-machines" begin times = Float64[ 3 3 5; 8 4 8; 7 2 10; 5 1 7; - 2 5 6 + 2 5 6 ] result = johnsons(times) + time_elapsed = makespan(times, result.permutation) + @test result.permutation == [1, 4, 5, 3, 2] - end - end + + @test time_elapsed == 42 + end + end - @testset "Five machines" verbose = true begin + @testset "Five machines" verbose = true begin - @testset "Example 1 for 5-machines" begin + @testset "Example 1 for 5-machines" begin times = Float64[ 7 5 2 3 9; 6 6 4 5 10; 5 4 5 6 8; - 8 3 3 2 6 + 8 3 3 2 6 ] result = johnsons(times) + time_elapsed = makespan(times, result.permutation) + @test result.permutation == [1, 3, 2, 4] - end + + @test time_elapsed == 51 + end end - @testset "Cannot reduce to 2-machines" begin + @testset "Cannot reduce to 2-machines" begin - times = Float64[ - 3 3 5 2; - 8 4 8 3; - 7 2 10 4; - 5 1 7 5; - 2 5 6 6 - ] + times = Float64[ + 3 3 5 2; + 8 4 8 3; + 7 2 10 4; + 5 1 7 5; + 2 5 6 6 + ] - @test_throws JohnsonException johnsons(times) + @test_throws JohnsonException johnsons(times) end - @testset "Other types of matrices (Int)" begin + @testset "Other types of matrices (Int)" begin mat = rand(1:10, 10, 2) @@ -93,11 +105,11 @@ # expect no error @test true - end + end - @testset "Other types of matrices (UInt8)" begin + @testset "Other types of matrices (UInt8)" begin - mat = convert(Array{UInt8, 2}, rand(1:10, 10, 2)) + mat = convert(Array{UInt8,2}, rand(1:10, 10, 2)) result = johnsons(mat) @@ -105,4 +117,47 @@ @test true end + + + @testset "makespan" begin + + best_permutation = [1, 4, 5, 3, 2] + best_makespan = 42 + + + times = Float64[ + 3 3 5; + 8 4 8; + 7 2 10; + 5 1 7; + 2 5 6 + ] + + + ms = Float64[ + makespan(times, [1, 2, 3, 4, 5]), + makespan(times, [1, 2, 3, 5, 4]), + makespan(times, [1, 2, 4, 3, 5]), + makespan(times, [1, 2, 4, 5, 3]), + makespan(times, [1, 2, 5, 3, 4]), + makespan(times, [1, 2, 5, 4, 3]), + makespan(times, [1, 3, 2, 4, 5]), + makespan(times, [1, 3, 2, 5, 4]), + makespan(times, [1, 3, 4, 2, 5]), + makespan(times, [1, 3, 4, 5, 2]), + makespan(times, [1, 3, 5, 2, 4]), + makespan(times, [1, 3, 5, 4, 2]), + makespan(times, [1, 4, 2, 3, 5]), + makespan(times, [1, 4, 2, 5, 3]), + makespan(times, [1, 4, 3, 2, 5]), + makespan(times, [1, 4, 3, 5, 2]), + makespan(times, [1, 4, 5, 2, 3]), + makespan(times, [1, 4, 5, 3, 2]) + ] + + @test minimum(ms) == best_makespan + end + + + end \ No newline at end of file