using HeuristicLab.Core; using HeuristicLab.Data; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace HeuristicLab.Problems.Scheduling.MRCPSP { public static class MultiModeFileImporter { public static MultiModeResourceConstrainedProjectSchedulingProblem Import(string file) { var bounds = new Dictionary { {"J501_3", 25}, {"J502_2", 25}, {"J502_3", 22}, {"J502_4", 24}, {"J502_5", 23}, {"J504_1", 19}, {"J504_5", 22}, {"J507_1", 44}, {"J507_2", 42}, {"J507_3", 41}, {"J507_4", 45}, {"J507_5", 41}, {"J508_1", 37}, {"J508_2", 31}, {"J508_3", 33}, {"J508_4", 34}, {"J508_5", 42}, {"J509_1", 32}, {"J509_2", 24}, {"J509_3", 26}, {"J509_4", 26}, {"J509_5", 28}, {"J5010_1", 26}, {"J5010_2", 34}, {"J5010_3", 26}, {"J5010_4", 27}, {"J5010_5", 35}, {"J5011_1", 25}, {"J5011_2", 28}, {"J5011_3", 28}, {"J5011_4", 24}, {"J5011_5", 29}, {"J5012_1", 26}, {"J5012_2", 30}, {"J5012_3", 23}, {"J5012_4", 25}, {"J5012_5", 25}, {"J5020_1", 30}, {"J5020_2", 26}, {"J5020_4", 25}, {"J5020_5", 25}, {"J5021_1", 18}, {"J5021_5", 17}, {"J5022_1", 20}, {"J5023_1", 17}, {"J5023_4", 16}, {"J5043_1", 65}, {"J5043_2", 64}, {"J5043_3", 68}, {"J5043_4", 52}, {"J5043_5", 64}, {"J5044_1", 47}, {"J5044_2", 41}, {"J5044_3", 50}, {"J5044_4", 49}, {"J5044_5", 46}, {"J5045_1", 46}, {"J5045_2", 34}, {"J5045_3", 39}, {"J5045_4", 36}, {"J5045_5", 44}, {"J5046_1", 42}, {"J5046_2", 41}, {"J5046_3", 45}, {"J5046_4", 31}, {"J5046_5", 39}, {"J5047_1", 36}, {"J5047_2", 40}, {"J5047_3", 42}, {"J5047_4", 39}, {"J5047_5", 39}, {"J5048_1", 35}, {"J5048_2", 40}, {"J5048_3", 29}, {"J5048_4", 34}, {"J5048_5", 41}, {"J5056_1", 35}, {"J5056_3", 34}, {"J5056_4", 34}, {"J5056_5", 38}, {"J5057_3", 31}, {"J5058_1", 34}, {"J5058_4", 28}, {"J5058_5", 28}, {"J5074_5", 57}, {"J5079_1", 73}, {"J5079_2", 82}, {"J5079_3", 73}, {"J5079_4", 68}, {"J5079_5", 80}, {"J5080_1", 72}, {"J5080_2", 68}, {"J5080_3", 82}, {"J5080_4", 72}, {"J5080_5", 70}, {"J5081_1", 63}, {"J5081_3", 57}, {"J5081_4", 64}, {"J5081_5", 53}, {"J5082_1", 59}, {"J5082_2", 60}, {"J5082_3", 56}, {"J5082_4", 67}, {"J5082_5", 54}, {"J5083_1", 49}, {"J5083_4", 56}, {"J5083_5", 62}, {"J5084_1", 51}, {"J5084_2", 57}, {"J5084_3", 60}, {"J5084_4", 59}, {"J5084_5", 57}, {"J5092_4", 60}, {"J5094_2", 60}, {"J5094_3", 45}, {"J5094_4", 43}, {"J5096_2", 48} }; var instance = ParseMultiModeFile(file); instance.UpperBound = bounds.TryGetValue(Path.GetFileNameWithoutExtension(file), out var bound) ? bound : -1; return CreateProblem(instance); } private static MultiModeResourceConstrainedProjectSchedulingProblem CreateProblem(MMRCPSP instance) { var problem = new MultiModeResourceConstrainedProjectSchedulingProblem { Name = instance.Name }; if (instance.UpperBound > -1) { problem.BestKnownQuality = instance.UpperBound; } if (instance.NbTasks != instance.Tasks.Count) throw new InvalidDataException(); foreach (var task in instance.Tasks) { var activity = new Activity { Number = task.TaskId }; foreach (var succ in task.Successors) { activity.Successors.Add(new IntValue(succ)); } foreach (var mode in instance.Modes.Where(m => m.TaskId == task.TaskId)) { var m = new MRCPSP.Mode { Number = mode.ModeId, Duration = mode.Duration }; var num = 1; foreach (var demand in mode.DemRenewableRsrc) { if (demand == 0) { num++; continue; } m.ResourceDemands.Add(new ResourceDemand { Demand = demand, Number = num++ }); } foreach (var demand in mode.DemNonRenewableRsrc) { if (demand == 0) { num++; continue; } m.ResourceDemands.Add(new ResourceDemand { Demand = demand, Number = num++ }); } activity.Modes.Add(m); } problem.Activities.Add(activity); } problem.ResourceCapacities = new ItemList(); var number = 1; foreach (var cap in instance.CapRenewableRsrc) { problem.ResourceCapacities.Add(new ResourceCapacity { Name = $"R{number}", Number = number++, Capacity = cap, IsRenewable = true }); } foreach (var cap in instance.CapNonRenewableRsrc) { problem.ResourceCapacities.Add(new ResourceCapacity { Name = $"N{number - instance.NbRenewableRsrcs}", Number = number++, Capacity = cap, IsRenewable = false }); } problem.InitEncoding(); return problem; } private static MMRCPSP ParseMultiModeFile(string filePath) { var result = new MMRCPSP { Name = Path.GetFileNameWithoutExtension(filePath), Tasks = new List(), Modes = new List() }; var section = 0; var prevJob = 0; foreach (var line in File.ReadLines(filePath).Select(l => l.Trim())) { if (string.IsNullOrEmpty(line)) continue; if (line.StartsWith("RESOURCES")) { section = 1; continue; } if (line.StartsWith("PRECEDENCE RELATIONS")) { section = 2; continue; } if (line.StartsWith("REQUESTS/DURATIONS")) { section = 3; continue; } if (line.StartsWith("RESOURCE AVAILABILITIES") || line.StartsWith("RESOURCEAVAILABILITIES")) { section = 4; continue; } switch (section) { case 0: // Jobs if (line.StartsWith("jobs", StringComparison.InvariantCultureIgnoreCase)) { result.NbTasks = int.Parse(Regex.Match(line, @":[ \t]*(?\d+)").Groups["jobs"].Value); } break; case 1: // Resources if (line.StartsWith("- nonrenewable")) { result.NbNonRenewableRsrcs = int.Parse(Regex.Match(line, @":.*(?\d+).*N").Groups["nr"].Value); result.CapNonRenewableRsrc = new int[result.NbNonRenewableRsrcs]; } else if (line.StartsWith("- renewable")) { result.NbRenewableRsrcs = int.Parse(Regex.Match(line, @":.*(?\d+).*R").Groups["nr"].Value); result.CapRenewableRsrc = new int[result.NbRenewableRsrcs]; } break; case 2: // Precedence Relations if (char.IsDigit(line[0])) { var numbers = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); var task = new Task { TaskId = int.Parse(numbers[0]), Successors = new List(numbers.Skip(3).Select(x => int.Parse(x))) }; result.Tasks.Add(task); } break; case 3: // Requests/Durations if (char.IsDigit(line[0])) { var numbers = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); var mode = new Mode { DemRenewableRsrc = new int[result.NbRenewableRsrcs], DemNonRenewableRsrc = new int[result.NbNonRenewableRsrcs] }; var next = 0; if (numbers.Length == 3 + result.NbRenewableRsrcs + result.NbNonRenewableRsrcs) { prevJob = int.Parse(numbers[next++]); } mode.TaskId = prevJob; mode.ModeId = int.Parse(numbers[next++]); mode.Duration = int.Parse(numbers[next++]); for (var i = 0; i < result.NbRenewableRsrcs; i++) { mode.DemRenewableRsrc[i] = int.Parse(numbers[next++]); } for (var i = 0; i < result.NbNonRenewableRsrcs; i++) { mode.DemNonRenewableRsrc[i] = int.Parse(numbers[next++]); } result.Modes.Add(mode); } break; case 4: // Resource Availabilities if (char.IsDigit(line[0])) { var numbers = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); var next = 0; for (var i = 0; i < result.NbRenewableRsrcs; i++) { result.CapRenewableRsrc[i] = int.Parse(numbers[next++]); } for (var i = 0; i < result.NbNonRenewableRsrcs; i++) { result.CapNonRenewableRsrc[i] = int.Parse(numbers[next++]); } } break; default: // unkown section continue; } } return result; } private class MMRCPSP { public int[] CapNonRenewableRsrc; public int[] CapRenewableRsrc; public List Modes; public string Name; public int NbNonRenewableRsrcs; public int NbRenewableRsrcs; public int NbTasks; public List Tasks; public int UpperBound; } private class Mode { public int[] DemNonRenewableRsrc; public int[] DemRenewableRsrc; public int Duration; public int ModeId; public int TaskId; } private class Task { public List Successors; public int TaskId; } } }