-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathExecutor.cs
369 lines (337 loc) · 19.8 KB
/
Executor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
using SC.Heuristics.PrimalHeuristic;
using SC.ObjectModel;
using SC.ObjectModel.Additionals;
using SC.ObjectModel.Configuration;
using SC.ObjectModel.Interfaces;
using SC.Linear;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace SC.CLI
{
/// <summary>
/// A class used to wrap the methods to enable execution by the CLI
/// </summary>
public class Executor
{
/// <summary>
/// The footprint file
/// </summary>
public const string FILENAME_FOOTPRINT = "footprint.csv";
/// <summary>
/// The consolidated footprint file
/// </summary>
public const string FILENAME_CONSOLIDATED_FOOTPRINTS = "footsprints.csv";
/// <summary>
/// The status over time log file
/// </summary>
public const string FILENAME_STATUS_OVER_TIME = "progression.dat";
/// <summary>
/// The name of the instance that is solved
/// </summary>
public const string FILENAME_INSTANCE_NAME = "instance.txt";
/// <summary>
/// The name of the config used for solving
/// </summary>
public const string FILENAME_CONFIG_NAME = "config.txt";
/// <summary>
/// The name of the gnuplot progression script file
/// </summary>
public const string FILENAME_PROGRESSION_SCRIPT = "progression.gp";
/// <summary>
/// The name of the batch file to generate in order to execute the progression script
/// </summary>
public const string FILENAME_PROGRESSION_SCRIPT_BATCH_FILE = "progression.cmd";
/// <summary>
/// Executes a single method-run
/// </summary>
/// <param name="instanceFile">The path to the instance</param>
/// <param name="configFile">The path to the config</param>
/// <param name="outputDirectory">The directory to write the results to</param>
/// <param name="seedNumber">The seed to use</param>
public static void Execute(string instanceFile, string configFile, string outputDirectory, string seedNumber)
{
// Save parameters for the case of an exception
ExInstanceName = instanceFile;
ExConfigName = configFile;
ExSeedNumber = seedNumber;
// Read config
Configuration config = Configuration.Read(configFile);
Instance instance = Instance.ReadXML(instanceFile);
// Create output dir if not already existing
string exportationDir = Path.Combine(outputDirectory, instance.Name + "-" + config.Name + "-" + seedNumber);
if (Directory.Exists(exportationDir))
Directory.Delete(exportationDir, true);
// Wait for directory to be really deleted - nasty but necessary
while (Directory.Exists(exportationDir))
Thread.Sleep(100);
// Create a fresh directory
Directory.CreateDirectory(exportationDir);
// Catch unhandled exceptions
if (!AppDomain.CurrentDomain.FriendlyName.EndsWith("vshost.exe"))
{
Console.WriteLine("Adding handler for unhandled exceptions ...");
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(LogUnhandledException);
}
// Determine log file
string logFile = Path.Combine(exportationDir, "output.txt");
// Write instance name
using (StreamWriter sw = new StreamWriter(Path.Combine(exportationDir, FILENAME_INSTANCE_NAME)))
sw.Write(instance.Name);
// Write configuration name
using (StreamWriter sw = new StreamWriter(Path.Combine(exportationDir, FILENAME_CONFIG_NAME)))
sw.Write(config.Name);
// Write all output
using (StreamWriter outputWriter = new StreamWriter(logFile) { AutoFlush = true })
{
// Write solution status over time
using (StreamWriter statusWriter = new StreamWriter(Path.Combine(exportationDir, FILENAME_STATUS_OVER_TIME)) { AutoFlush = true })
{
// Prepare
Action<string> logger = (string s) => { outputWriter.Write(s); Console.Write(s); };
Action<string> loggerLine = (string s) => { outputWriter.Write(s + Environment.NewLine); Console.WriteLine(s); };
statusWriter.WriteLine("% time incumbent");
config.LogSolutionStatus = (double timeStamp, double incumbent) => { statusWriter.WriteLine(timeStamp.ToString(ExportationConstants.FORMATTER) + ExportationConstants.GNUPLOT_DELIMITER + incumbent.ToString(ExportationConstants.FORMATTER)); };
config.Log = logger;
config.Seed = int.Parse(seedNumber);
IMethod method = null;
switch (config.Type)
{
case MethodType.FrontLeftBottomStyle: { method = new LinearModelFLB(instance, config); } break;
case MethodType.TetrisStyle: { method = new LinearModelTetris(instance, config); } break;
case MethodType.HybridStyle: { method = new LinearModelHybrid(instance, config); } break;
case MethodType.SpaceIndexed: throw new NotImplementedException("Space indexed model not working for now.");
case MethodType.ExtremePointInsertion: { method = new ExtremePointInsertionHeuristic(instance, config); } break;
case MethodType.SpaceDefragmentation: { method = new SpaceDefragmentationHeuristic(instance, config); } break;
case MethodType.PushInsertion: { method = new PushInsertion(instance, config); } break;
case MethodType.ALNS: { method = new ALNS(instance, config); } break;
default: throw new ArgumentException("Unknown method: " + config.Type.ToString());
}
// Output some information before starting
loggerLine("Welcome to SardineCan CLI");
loggerLine("Parameters: " + instanceFile + " " + configFile + " " + outputDirectory + " " + seedNumber);
loggerLine("Initializing ...");
loggerLine("Instance: " + instance.Name);
loggerLine("Config: " + config.Name);
loggerLine("Seed: " + seedNumber);
loggerLine("Config-details: ");
LogConfigDetails(config, logger);
loggerLine("");
// Execute
logger("Executing ... ");
PerformanceResult result = method.Run();
// Log some information to the console
loggerLine("Finished!");
loggerLine("");
loggerLine("Result outline:");
loggerLine("Obj: " + result.ObjectiveValue.ToString(CultureInfo.InvariantCulture));
loggerLine("VolumeContained: " + result.Solution.VolumeContained.ToString(CultureInfo.InvariantCulture));
loggerLine("VolumeOfContainers: " + result.Solution.VolumeOfContainers.ToString(CultureInfo.InvariantCulture));
loggerLine("VolumeContainedRelative: " + (result.Solution.VolumeContainedRelative * 100).ToString(CultureInfo.InvariantCulture) + "%");
loggerLine("VolumeOfContainersInUse: " + result.Solution.VolumeOfContainersInUse.ToString(CultureInfo.InvariantCulture));
loggerLine("NumberOfContainersInUse: " + result.Solution.NumberOfContainersInUse.ToString(CultureInfo.InvariantCulture));
loggerLine("NumberOfPiecesPacked: " + result.Solution.NumberOfPiecesPacked.ToString(CultureInfo.InvariantCulture));
loggerLine("SolutionTime: " + result.SolutionTime.TotalSeconds.ToString(CultureInfo.InvariantCulture) + "s");
// Log performance information
using (StreamWriter solutionInfoWriter = new StreamWriter(File.Open(Path.Combine(exportationDir, "result.txt"), FileMode.Create)))
{
solutionInfoWriter.WriteLine("Runtime: " + result.SolutionTime.ToString());
solutionInfoWriter.WriteLine("ObjectiveValue: " + result.ObjectiveValue.ToString(ExportationConstants.FORMATTER));
solutionInfoWriter.WriteLine("BestBound: " + result.BestBound.ToString(ExportationConstants.FORMATTER));
solutionInfoWriter.WriteLine("RemainingGap: " + result.Gap.ToString(ExportationConstants.FORMATTER));
result.Instance.OutputInfo(solutionInfoWriter);
//result.Solution.out // TODO define output of solution info
}
// Log footprint
using (StreamWriter sw = new StreamWriter(Path.Combine(exportationDir, FILENAME_FOOTPRINT)))
sw.WriteLine(
instance.Name + ExportationConstants.CSV_DELIMITER +
config.Name + ExportationConstants.CSV_DELIMITER +
seedNumber + ExportationConstants.CSV_DELIMITER +
instance.Containers.Count + ExportationConstants.CSV_DELIMITER +
instance.Pieces.Count + ExportationConstants.CSV_DELIMITER +
instance.Containers.Sum(c => c.VirtualPieces.Count).ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
instance.Containers.Sum(c => c.Slants.Count).ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.ObjectiveValue.ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.Solution.VolumeContained.ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.Solution.VolumeOfContainers.ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
(result.Solution.VolumeContainedRelative * 100).ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.Solution.VolumeOfContainersInUse.ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.Solution.NumberOfContainersInUse.ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.Solution.NumberOfPiecesPacked.ToString(CultureInfo.InvariantCulture) + ExportationConstants.CSV_DELIMITER +
result.SolutionTime.TotalSeconds.ToString(CultureInfo.InvariantCulture));
// Write instance-solution as xml
if (instance.Solutions.Any())
instance.WriteXML(Path.Combine(exportationDir, "solution.xinst"));
}
}
}
public static PerformanceResult Execute(Instance instance, Configuration config, Action<string> logger)
{
// Prepare logging
void loggerLine(string msg) { logger?.Invoke(msg + Environment.NewLine); }
config.Log = logger;
// Init method
IMethod method;
switch (config.Type)
{
case MethodType.FrontLeftBottomStyle: { method = new LinearModelFLB(instance, config); } break;
case MethodType.TetrisStyle: { method = new LinearModelTetris(instance, config); } break;
case MethodType.HybridStyle: { method = new LinearModelHybrid(instance, config); } break;
case MethodType.SpaceIndexed: throw new NotImplementedException("Space indexed model not working for now.");
case MethodType.ExtremePointInsertion: { method = new ExtremePointInsertionHeuristic(instance, config); } break;
case MethodType.SpaceDefragmentation: { method = new SpaceDefragmentationHeuristic(instance, config); } break;
case MethodType.PushInsertion: { method = new PushInsertion(instance, config); } break;
case MethodType.ALNS: { method = new ALNS(instance, config); } break;
default: throw new ArgumentException("Unknown method: " + config.Type.ToString());
}
// Output some information before starting
loggerLine($">>> Welcome to SardineCan");
loggerLine($"Initializing ...");
loggerLine($"Instance: {instance.Name}");
loggerLine($"Config: {config.Name}");
loggerLine($"Seed: {config.Seed}");
loggerLine($"Config-details: ");
LogConfigDetails(config, logger);
logger?.Invoke(Environment.NewLine);
// Execute
loggerLine($"Executing ... ");
PerformanceResult result = method.Run();
// Log some information to the console
loggerLine($"Finished!");
loggerLine($"Result outline:");
loggerLine($"Obj: {result.ObjectiveValue.ToString(CultureInfo.InvariantCulture)}");
loggerLine($"VolumeContained: {result.Solution.VolumeContained.ToString(CultureInfo.InvariantCulture)}");
loggerLine($"VolumeOfContainers: {result.Solution.VolumeOfContainers.ToString(CultureInfo.InvariantCulture)}");
loggerLine($"VolumeContainedRelative: {(result.Solution.VolumeContainedRelative * 100).ToString(CultureInfo.InvariantCulture)}%");
loggerLine($"VolumeOfContainersInUse: {result.Solution.VolumeOfContainersInUse.ToString(CultureInfo.InvariantCulture)}");
loggerLine($"NumberOfContainersInUse: {result.Solution.NumberOfContainersInUse.ToString(CultureInfo.InvariantCulture)}");
loggerLine($"NumberOfPiecesPacked: {result.Solution.NumberOfPiecesPacked.ToString(CultureInfo.InvariantCulture)}");
loggerLine($"SolutionTime: {result.SolutionTime.TotalSeconds.ToString(CultureInfo.InvariantCulture)}s");
// We're done here
return result;
}
/// <summary>
/// Returns an appropriate header for the consolidated footprint file
/// </summary>
/// <returns>The header</returns>
public static string GetFootPrintHeader()
{
return "Instance" + ExportationConstants.CSV_DELIMITER +
"Config" + ExportationConstants.CSV_DELIMITER +
"Seed" + ExportationConstants.CSV_DELIMITER +
"Containers" + ExportationConstants.CSV_DELIMITER +
"Pieces" + ExportationConstants.CSV_DELIMITER +
"VirtualPieces" + ExportationConstants.CSV_DELIMITER +
"Slants" + ExportationConstants.CSV_DELIMITER +
"ObjectiveValue" + ExportationConstants.CSV_DELIMITER +
"VolumeContained" + ExportationConstants.CSV_DELIMITER +
"VolumeOfContainers" + ExportationConstants.CSV_DELIMITER +
"VolumeContainedRelative" + ExportationConstants.CSV_DELIMITER +
"VolumeOfContainersInUse" + ExportationConstants.CSV_DELIMITER +
"NumberOfContainersInUse" + ExportationConstants.CSV_DELIMITER +
"NumberOfPiecesPacked" + ExportationConstants.CSV_DELIMITER +
"SolutionTimeInSeconds";
}
/// <summary>
/// Logs detailed information about the used configuration.
/// </summary>
/// <param name="config">The config to log details about.</param>
/// <param name="logger">The logger to use.</param>
private static void LogConfigDetails(Configuration config, Action<string> logger)
{
foreach (var field in config.GetType().GetProperties())
{
// Is string like
string value = "";
// If the field has a xmlignore attribute: ignore it here too
if (field.GetCustomAttributes(false).Any(a => a is XmlIgnoreAttribute))
continue;
// See if it already is a string
if (field.PropertyType == typeof(string))
{
value = (string)field.GetValue(config);
}
else
{
// Fetch to-string method - check whether a formatter is necessary
var toStringMethod = field.PropertyType.GetMethod("ToString", new[] { typeof(CultureInfo) });
if (field.GetValue(config) == null)
{
value = null;
}
else
{
if (toStringMethod != null)
value = toStringMethod.Invoke(field.GetValue(config), new object[] { CultureInfo.InvariantCulture })?.ToString();
else
value = field.GetValue(config)?.ToString();
}
}
// Output it
logger?.Invoke(field.Name + ": " + value + Environment.NewLine);
}
}
/// <summary>
/// Logs an unhandled exception.
/// </summary>
/// <param name="ex">The exception to log.</param>
private static void LogUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
LogException((Exception)e.ExceptionObject);
}
/// <summary>
/// The saved instance name that is written to output in case of an exception.
/// </summary>
private static string ExInstanceName;
/// <summary>
/// The saved config name that is written to output in case of an exception.
/// </summary>
private static string ExConfigName;
/// <summary>
/// The saved seed number that is written to output in case of an exception.
/// </summary>
private static string ExSeedNumber;
/// <summary>
/// Logs an exception to the default file.
/// </summary>
/// <param name="ex">The exception to log.</param>
/// <param name="additionFileToLogTo">Specifies an additional file to log the exception to.</param>
private static void LogException(Exception ex, string additionFileToLogTo = null)
{
// Init output log
using (StreamWriter sw = new StreamWriter("exception.txt", true))
{
StreamWriter additionalSW = additionFileToLogTo != null ? new StreamWriter(additionFileToLogTo, true) : null;
Action<string> log = (string msg) => { sw.Write(msg); Console.Write(msg); if (additionalSW != null) { additionalSW.Write(msg); } };
Action<string> logLine = (string msg) => { sw.WriteLine(msg); Console.WriteLine(msg); if (additionalSW != null) { additionalSW.WriteLine(msg); } };
logLine("---> Caught an unhandled exception: ");
logLine("Instance: " + ExInstanceName);
logLine("Config: " + ExConfigName);
logLine("Seed: " + ExSeedNumber);
logLine("Message: " + ex.Message);
logLine("Time: " + DateTime.Now.ToString(CultureInfo.InvariantCulture));
logLine("Stacktrace:");
logLine(ex.StackTrace);
log("InnerException: ");
if (ex.InnerException != null)
{
logLine(ex.InnerException.Message);
logLine("Stacktrace:");
logLine(ex.InnerException.StackTrace);
}
else
{
logLine("None");
}
additionalSW.Close();
}
}
}
}