using System; namespace HeuristicLab.Problems.ProgramSynthesis.Push.Problem { using System.Collections.Generic; using System.Linq; using Common; using Configuration; using Core; using Encodings.IntegerVectorEncoding; using Expressions; using HeuristicLab.BenchmarkSuite; using HeuristicLab.Data; using Instances; using Interpreter; using Optimization; using Parameters; using Persistence.Default.CompositeSerializers.Storable; using Stack; [StorableClass] [Creatable(CreatableAttribute.Categories.GeneticProgrammingProblems, Priority = 180)] [Item("Push Problem", "")] public class PushProblem : SingleObjectiveBasicProblem, IProblemInstanceConsumer { [Storable] private readonly PushConfiguration config; private PushInterpreterPool pool; public PushProblem() { config = new PushConfiguration(); pool = new PushInterpreterPool(config); InitEvents(); InitParameters(); Instructions = config; } [StorableConstructor] public PushProblem(bool deserializing) : base(deserializing) { } public PushProblem(PushProblem original, Cloner cloner) : base(original, cloner) { config = cloner.Clone(original.config); pool = new PushInterpreterPool(config); Instructions = config; this.InitEvents(); } [StorableHook(HookType.AfterDeserialization)] // ReSharper disable once UnusedMember.Local private void AfterDeserialization() { pool = new PushInterpreterPool(config); Instructions = config; InitEvents(); } private void InitEvents() { config.EnabledExpressionsChanged += EnabledExpressionsChanged; } private void EnabledExpressionsChanged(object sender, EnabledExpressionsChangedEventArgs e) { this.Encoding.Bounds[0, 1] = config.EnabledExpressions.Count - 1; this.Encoding.BoundsParameter.Value[0, 1] = config.EnabledExpressions.Count - 1; } #region Parameters private const string EvalPushLimitParameterName = "EvalPushLimit"; private const string EvalPushLimitParameterDescription = "This is the maximum allowed number of \"executions\" in a single top-level call to the interpreter. The execution of a single Push instruction counts as one execution, as does the processing of a single literal, as does the descent into one layer of parentheses (that is, the processing of the \"(\" counts as one execution)."; private const string MaxProgramLengthParameterName = "MaxProgramLength"; private const string MaxProgramLengthParameterDescription = "This is the maximum size of an item on the CODE stack, expressed as a number of points. A point is an instruction, a literal, or a pair of parentheses."; private const string TopLevelPushCodeParameterName = "TopLevelPushCode"; private const string TopLevelPushCodeParameterDescription = "When TRUE (which is the default), code passed to the top level of the interpreter will be pushed onto the CODE stack prior to execution."; private const string TopLevelPopCodeParameterName = "TopLevelPopCode"; private const string TopLevelPopCodeParameterDescription = "When TRUE, the CODE stack will be popped at the end of top level calls to the interpreter. The default is FALSE."; private const string InstructionsParameterName = "Instructions"; private const string InstructionsParameterDescription = "Enables/Disables Instructions"; private const string DataParameterName = "Data"; private const string DataParameterDescription = "Program Synthesis"; private const string MinRandomIntegerParameterName = "MinRandomInteger"; private const string MinRandomIntegerParameterDescription = "The minimum INTEGER that will be produced as an ephemeral random INTEGER constant or from a call to INTEGER.RAND."; private const string MaxRandomIntegerParameterName = "MaxRandomInteger"; private const string MaxRandomIntegerParameterDescription = "The maximum INTEGER that will be produced as an ephemeral random INTEGER constant or from a call to INTEGER.RAND."; private const string MinRandomFloatParameterName = "MinRandomFloat"; private const string MinRandomFloatParameterDescription = "The minimum FLOAT that will be produced as an ephemeral random FLOAT constant or from a call to FLOAT.RAND."; private const string MaxRandomFloatParameterName = "MaxRandomFloat"; private const string MaxRandomFloatParameterDescription = "The maximum FLOAT that will be produced as an ephemeral random FLOAT constant or from a call to FLOAT.RAND."; private const string NewErcNameProbabilityParameterName = "NewErcNameProbability"; private const string NewErcNameProbabilityParameterDescription = "The probability that the selection of the ephemeral random NAME constant for inclusion in randomly generated code will produce a new name."; private const string ErcProbabilityParameterName = "ErcProbability"; private const string ErcProbabilityParameterDescription = "The probability that the selection of a epheral random literal constant for inclusion in randomly generated code will produce a new literal."; private const string MaxPointsInRandomInstructionParameterName = "MaxPointsInRandomInstruction"; private const string MaxPointsInRandomInstructionParameterDescription = "MaxPointsInRandomInstruction"; private const string DataBoundsParameterName = "DataBounds"; private void InitParameters() { var bounds = new IntMatrix(1, 3, new[] { "Training Start", "Training End | Test Start", "Test End" }); bounds.ItemChanged += (s, e) => { if (this.DataDescriptor == null) return; var max = this.DataDescriptor.OriginalTestCount + this.DataDescriptor.OriginalTrainingCount - 1; bounds[0, 0] = Math.Min(Math.Max(bounds[0, 0], 0), max); bounds[0, 2] = Math.Max(Math.Min(bounds[0, 2], max), 0); bounds[0, 1] = Math.Min(Math.Max(bounds[0, 0], bounds[0, 1]), bounds[0, 2]); }; Parameters.Add(new FixedValueParameter( DataBoundsParameterName, bounds)); Parameters.Add(new FixedValueParameter( EvalPushLimitParameterName, EvalPushLimitParameterDescription, new IntValue(config.EvalPushLimit))); Parameters.Add(new FixedValueParameter( MaxProgramLengthParameterName, MaxProgramLengthParameterDescription, new IntValue(config.MaxPointsInProgram))); Parameters.Add(new FixedValueParameter( TopLevelPushCodeParameterName, TopLevelPushCodeParameterDescription, new BoolValue(config.TopLevelPushCode)) { Hidden = true }); Parameters.Add(new FixedValueParameter( TopLevelPopCodeParameterName, TopLevelPopCodeParameterDescription, new BoolValue(config.TopLevelPopCode)) { Hidden = true }); Parameters.Add(new ValueParameter( InstructionsParameterName, InstructionsParameterDescription)); Parameters.Add(new FixedValueParameter( MinRandomIntegerParameterName, MinRandomIntegerParameterDescription, new IntValue(config.MinRandomInteger)) { Hidden = true }); Parameters.Add(new FixedValueParameter( MaxRandomIntegerParameterName, MaxRandomIntegerParameterDescription, new IntValue(config.MaxRandomInteger)) { Hidden = true }); Parameters.Add(new FixedValueParameter( MinRandomFloatParameterName, MinRandomFloatParameterDescription, new DoubleValue(config.MinRandomFloat)) { Hidden = true }); Parameters.Add(new FixedValueParameter( MaxRandomFloatParameterName, MaxRandomFloatParameterDescription, new DoubleValue(config.MaxRandomFloat)) { Hidden = true }); Parameters.Add(new FixedValueParameter( NewErcNameProbabilityParameterName, NewErcNameProbabilityParameterDescription, new PercentValue(config.NewErcNameProbability)) { Hidden = true }); Parameters.Add(new FixedValueParameter( ErcProbabilityParameterName, ErcProbabilityParameterDescription, new PercentValue(config.ErcProbability)) { Hidden = true }); Parameters.Add(new FixedValueParameter( MaxPointsInRandomInstructionParameterName, MaxPointsInRandomInstructionParameterDescription, new IntValue(config.MaxPointsInRandomExpression)) { Hidden = true }); Parameters.Add(new ValueParameter(DataParameterName, DataParameterDescription)); Encoding.Bounds[0, 0] = 0; Encoding.Bounds[0, 1] = config.EnabledExpressions.Count - 1; Encoding.Length = config.MaxPointsInProgram; } public IValueParameter DataBoundsParameter { get { return (IValueParameter)Parameters[DataBoundsParameterName]; } } public IntMatrix DataBounds { get { return DataBoundsParameter.Value; } set { DataBoundsParameter.Value = value; } } public IValueParameter InstructionsParameter { get { return (IValueParameter)Parameters[InstructionsParameterName]; } } public IEnabledExpressionsConfiguration Instructions { get { return InstructionsParameter.Value; } set { InstructionsParameter.Value = value; } } public IValueParameter DataParameter { get { return (IValueParameter)Parameters[DataParameterName]; } } public IBenchmarkSuiteDataDescriptor DataDescriptor { get { return DataParameter.Value; } set { DataParameter.Value = value; } } public IValueParameter EvalPushLimitParameter { get { return (IValueParameter)this.Parameters[EvalPushLimitParameterName]; } } public int EvalPushLimit { get { return config.EvalPushLimit; } set { this.EvalPushLimitParameter.Value.Value = value; config.EvalPushLimit = value; } } public IValueParameter MaxProgramLengthParameter { get { return (IValueParameter)this.Parameters[MaxProgramLengthParameterName]; } } public int MaxProgramLength { get { return config.MaxPointsInProgram; } set { this.MaxProgramLengthParameter.Value.Value = value; this.Encoding.LengthParameter.Value.Value = value; config.MaxPointsInProgram = value; } } public IValueParameter TopLevelPushParameter { get { return (IValueParameter)this.Parameters[TopLevelPushCodeParameterName]; } } public bool TopLevelPushCode { get { return config.TopLevelPushCode; } set { this.TopLevelPushParameter.Value.Value = value; config.TopLevelPushCode = value; } } public IValueParameter TopLevelPopParameter { get { return (IValueParameter)this.Parameters[TopLevelPopCodeParameterName]; } } public bool TopLevelPopCode { get { return config.TopLevelPopCode; } set { this.TopLevelPushParameter.Value.Value = value; config.TopLevelPopCode = value; } } public IValueParameter MinRandomIntegerParameter { get { return (IValueParameter)this.Parameters[MinRandomIntegerParameterName]; } } public int MinRandomInteger { get { return config.MinRandomInteger; } set { this.MinRandomIntegerParameter.Value.Value = value; config.MinRandomInteger = value; } } public IValueParameter MaxRandomIntegerParameter { get { return (IValueParameter)this.Parameters[MaxRandomIntegerParameterName]; } } public int MaxRandomInteger { get { return config.MaxRandomInteger; } set { this.MaxRandomIntegerParameter.Value.Value = value; config.MaxRandomInteger = value; } } public IValueParameter MinRandomFloatParameter { get { return (IValueParameter)this.Parameters[MinRandomFloatParameterName]; } } public double MinRandomFloat { get { return config.MinRandomFloat; } set { this.MinRandomFloatParameter.Value.Value = value; config.MinRandomFloat = value; } } public IValueParameter MaxRandomFloatParameter { get { return (IValueParameter)this.Parameters[MaxRandomFloatParameterName]; } } public double MaxRandomFloat { get { return config.MaxRandomFloat; } set { this.MaxRandomFloatParameter.Value.Value = value; config.MaxRandomFloat = value; } } public IValueParameter NewErcNameProbabilityParameter { get { return (IValueParameter)this.Parameters[NewErcNameProbabilityParameterName]; } } public double NewErcNameProbability { get { return config.NewErcNameProbability; } set { this.NewErcNameProbabilityParameter.Value.Value = value; config.NewErcNameProbability = value; } } public IValueParameter ErcProbabilityParameter { get { return (IValueParameter)this.Parameters[ErcProbabilityParameterName]; } } public double ErcProbability { get { return config.ErcProbability; } set { this.ErcProbabilityParameter.Value.Value = value; config.ErcProbability = value; } } public IValueParameter MaxPointsInRandomInstructionParameter { get { return (IValueParameter)Parameters[MaxPointsInRandomInstructionParameterName]; } } public int MaxPointsInRandomInstruction { get { return config.MaxPointsInRandomExpression; } set { this.MaxPointsInRandomInstructionParameter.Value.Value = value; config.MaxPointsInRandomExpression = value; } } #endregion public override bool Maximization { get { return false; } } public override IDeepCloneable Clone(Cloner cloner) { return new PushProblem(this, cloner); } public override void Analyze(Individual[] individuals, double[] qualities, ResultCollection results, IRandom random) { const string bestSolutionResultName = "Best Solution"; var bestQuality = Maximization ? qualities.Max() : qualities.Min(); var bestIdx = Array.IndexOf(qualities, bestQuality); var bestIndividual = individuals[bestIdx]; var solution = new PushSolution(bestIndividual.IntegerVector(), bestQuality, DataDescriptor, random, config); if (!results.ContainsKey(bestSolutionResultName)) { results.Add(new Result(bestSolutionResultName, solution)); } else if (((PushSolution)results[bestSolutionResultName].Value).Quality < qualities[bestIdx]) { results[bestSolutionResultName].Value = solution; } } public override double Evaluate(Individual individual, IRandom random) { var program = individual.PushProgram(config.EnabledExpressions as IReadOnlyList); var expandExpression = new ExecExpandExpression(program); var results = new List(); using (var interpreter = pool.GetInstance(random)) { for (var i = DataBounds[0, 0]; i < DataBounds[0, 1]; i++) { var example = this.DataDescriptor.Examples[i]; interpreter.BooleanStack.Push(example.InputBoolean); interpreter.IntegerStack.Push(example.InputInt); interpreter.FloatStack.Push(example.InputFloat); interpreter.Run(expandExpression); var diff = GetDiff(example.OutputInt, interpreter.IntegerStack, this.DataDescriptor.WorstResult, LongDiffer) + GetDiff(example.OutputFloat, interpreter.FloatStack, this.DataDescriptor.WorstResult, DoubleDiffer) + GetDiff(example.OutputBoolean, interpreter.BooleanStack, this.DataDescriptor.WorstResult, BooleanDiffer); results.Add(diff); interpreter.Clear(); } } return results.Count == 0 ? 0d : results.Average(); } private static double DoubleDiffer(double a, double b) { var result = a - b; return result == double.MinValue ? double.MaxValue : Math.Abs(result); } private static double LongDiffer(long a, long b) { var result = a - b; return result == long.MinValue ? long.MaxValue : Math.Abs(result); } private static double BooleanDiffer(bool a, bool b) { return a && b ? 0 : a || b ? 1 : 2; } private static double GetDiff(IReadOnlyList estimated, IStack resultStack, double worstResult, Func differ) where T : IComparable { var count = Math.Min(estimated.Count, resultStack.Count); var result = resultStack.Peek(count); var comparableLength = Math.Min(estimated.Count, result.Length); var diff = 0d; for (var i = 0; i < comparableLength; i++) { diff += Math.Min(differ(estimated[i], result[0]), worstResult); } if (estimated.Count > result.Length) { diff += worstResult * (estimated.Count - comparableLength); } return diff; } public void Load(IBenchmarkSuiteDataDescriptor descriptor) { this.DataDescriptor = descriptor; BestKnownQuality = descriptor.BestResult; DataBounds[0, 0] = 0; DataBounds[0, 2] = this.DataDescriptor.OriginalTrainingCount + this.DataDescriptor.OriginalTestCount; DataBounds[0, 1] = this.DataDescriptor.OriginalTrainingCount; } } }