Modeling scheduling problems with HeuristicLab
HeuristicLab makes it very easy to model scheduling problems. HeuristicLab comes included with the simulation framework Sim#, a port of SimPy to C#. With Sim# writing process-based simulation models requires little effort. Here, I demonstrate in only a few lines of code how to implement a permutation flow shop problem using a Programmable Problem (single-objective). You can create this problem in the GUI from the New Item dialog and paste following code into the editor pane, then hit compile. You can optimize solutions after you dragged it to the Problem tab of an algorithm that can handle programmable permutation-based problems (e.g. Genetic Algorithm).
using System; using System.Linq; using System.Collections.Generic; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.PermutationEncoding; using HeuristicLab.Optimization; using IRandom = HeuristicLab.Core.IRandom; using SimSharp; using SysEnv = System.Environment; using SimEnv = SimSharp.Environment; namespace HeuristicLab.Problems.Programmable { public class FlowShopProblemDefinition : CompiledProblemDefinition, ISingleObjectiveProblemDefinition { private static readonly string[] machineSep = new [] { SysEnv.NewLine }; private static readonly string[] jobSep = new [] { " ", "\t" }; // from http://mistic.heig-vd.ch/taillard/problemes.dir/ordonnancement.dir/flowshop.dir/tai20_5.txt private static readonly string instance = @" 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 "; private int M, J; // number of machines, jobs private List<List<int>> pij; // processing times per machine, job // objective is to minimize makespan public bool Maximization { get { return false; } } public override void Initialize() { // processing times are parsed from the instance pij = instance.Split(machineSep, StringSplitOptions.RemoveEmptyEntries) .Select(x => x.Split(jobSep, StringSplitOptions.RemoveEmptyEntries) .Select(y => int.Parse(y)).ToList()).ToList(); M = pij.Count; J = pij[0].Count; // encoding is created Encoding = new PermutationEncoding("p", length: J, type: PermutationTypes.Absolute); } public double Evaluate(Individual ind, IRandom random) { // create a new Sim# environment var env = new SimEnv(); // create the M machines var machines = Enumerable.Range(0, M).Select(x => new Resource(env)).ToArray(); // add the jobs in order given by the permutation foreach (var o in ind.Permutation("p")) env.Process(Job(env, o, machines)); // run the simulation env.Run(); // return the current simulation time (last finish time) return env.NowD; } private IEnumerable<Event> Job(SimEnv env, int j, Resource[] machines) { // for each of the M machines in order from 0 to M-1 for (var m = 0; m < M; m++) { // create a new request for the next machine // the machine is freed after the using block using (var req = machines[m].Request()) { // wait upon obtaining the machine yield return req; // process the job j on machine m yield return env.TimeoutD(pij[m][j]); } } } public void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) { } // for move-based algorithms, all possible swap2 neighbors are returned public IEnumerable<Individual> GetNeighbors(Individual ind, IRandom random) { var p = ind.Permutation("p"); foreach (var move in ExhaustiveSwap2MoveGenerator.Apply(p)) { var neighbor = ind.Copy(); var perm = neighbor.Permutation("p"); Swap2Manipulator.Apply(perm, move.Index1, move.Index2); yield return neighbor; } } } }
This includes a "simple" 5 machine / 20 job problem instance from Taillard's instances. Of course simulation enables to build much more complex models.
Note: Starting from Sim# 3.1.0 the class SimSharp.Environment is deprecated and should be replaced with SimSharp.Simulation.