#region License Information /* HeuristicLab * Copyright (C) 2002-2018 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using HEAL.Attic; using HeuristicLab.Common; using HeuristicLab.Core; using HeuristicLab.Data; using HeuristicLab.Encodings.IntegerVectorEncoding; using HeuristicLab.Encodings.RealVectorEncoding; using HeuristicLab.Optimization; using HeuristicLab.Parameters; namespace HeuristicLab.Problems.Modifiers { [StorableType("FADAA1BF-7B7A-4E28-AA15-22AF56D63CFE")] [Item("MultiObjectiveEvaluationLogProblemModifier", "A problem modifier that logs evaluations in a file")] public class EvaluationLoggingProblemModifier : ProblemModifier { private readonly object fileLock = new object(); #region ParameterNames private const string LogFileParameterName = "LogFile"; #endregion #region Parameters public IValueParameter LogFileParameter => (IValueParameter)Parameters[LogFileParameterName]; #endregion #region ParameterNames public FileValue LogFile => LogFileParameter.Value; #endregion #region Constructors & Cloning [StorableConstructor] protected EvaluationLoggingProblemModifier(StorableConstructorFlag _) : base(_) { } protected EvaluationLoggingProblemModifier(EvaluationLoggingProblemModifier original, Cloner cloner) : base(original, cloner) { } public EvaluationLoggingProblemModifier() { var defaultFileName = $"{Environment.CurrentDirectory}\\Log_{Guid.NewGuid()}.txt"; var value = new FileValue(); value.StringValue.Value = defaultFileName; Parameters.Add(new ValueParameter(LogFileParameterName, "The log file into which evaluations should be logged.", value)); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { } public override IDeepCloneable Clone(Cloner cloner) { return new EvaluationLoggingProblemModifier(this, cloner); } #endregion #region ProblemModifier public override double[] ModifiedEvaluate(Individual individual, IRandom random) { var qualities = base.ModifiedEvaluate(individual, random); lock (fileLock) { LogResultsToFile(individual, LogFile.Value, Encoding); } return qualities; } #endregion #region Helpers private static void LogResultsToFile(Individual individual, string logFilePath, IEncoding enc) { if (string.IsNullOrEmpty(logFilePath)) throw new ArgumentException("Log path is null or empty"); var logFile = new FileInfo(logFilePath); if (!logFile.Exists) { logFile.Directory?.Create(); logFile.Create(); } var logLines = new List(); var sb = new StringBuilder(); var inputs = ExtractInputs(individual, enc).ToArray(); var additionalInfo = ExtractAdditionalInfo(individual, enc); if (logFile.Length == 0) { var digits = Math.Ceiling(Math.Log10(inputs.Length)); var header = Enumerable .Range(0, inputs.Length) .Select(x => string.Format($"X{{0:D{digits}}}", x)) .Concat(additionalInfo.Select(x => x.Item1)); logLines.Add(string.Join(";", header)); sb.Clear(); } logLines.Add(string.Join(";", inputs.Concat(additionalInfo.Select(x => x.Item2)))); sb.Clear(); File.AppendAllLines(logFilePath, logLines); } private static Tuple[] ExtractAdditionalInfo(Individual individual, IEncoding enc) { return individual.Values.Where(x => x.Key != enc.Name).OrderBy(x => x.Key).Select(x => Tuple.Create(x.Key, x.Value.ToString())).ToArray(); } private static IEnumerable ExtractInputs(Individual individual, IEncoding encoding) { var encodingType = encoding.GetType(); if (encodingType == typeof(IntegerVectorEncoding)) return individual.IntegerVector().Select(x => (object)x); if (encodingType == typeof(RealVectorEncoding)) return individual.RealVector().Select(x => (object)x); throw new ArgumentException("This input encoding is not supported yet"); } #endregion } }