#region License Information
/* HeuristicLab
* Copyright (C) 2002-2016 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.Linq;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.ExpressionGenerator.Interfaces;
using HeuristicLab.Random;
namespace HeuristicLab.ExpressionGenerator {
public interface IExpressionTemplate {
string Label { get; set; }
}
public abstract class ExpressionTemplate : IExpressionTemplate {
protected List> arguments;
protected readonly Func, double> transform;
public string Label { get; set; }
protected Expression Instantiate(string name, IRandom random, int n, bool sampleWithRepetition = false) {
var func = Expression.Function(name, transform, SampleArguments(random, n, sampleWithRepetition));
func.Label = Label;
return func;
}
protected IEnumerable SampleArguments(IRandom random, int n, bool sampleWithRepetition = false) {
var weights = arguments.Select(x => x.Item2);
var args = sampleWithRepetition
? arguments.SampleProportional(random, n, weights, windowing: false).Select(x => x.Item1)
: arguments.SampleProportionalWithoutRepetition(random, n, weights, windowing: false).Select(x => x.Item1);
return args;
}
public abstract Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false);
protected ExpressionTemplate(Func, double> transform) {
this.transform = transform;
}
public void AddArgument(Expression expression, double weight = 1d) {
arguments = arguments ?? new List>();
arguments.Add(Tuple.Create(expression, weight));
}
public void AddArguments(IEnumerable expressions) {
arguments = arguments ?? new List>();
arguments.AddRange(expressions.Select(x => Tuple.Create(x, 1d)));
}
public void AddArguments(IEnumerable expressions, IEnumerable weights) {
arguments = arguments ?? new List>();
arguments.AddRange(expressions.Zip(weights, Tuple.Create));
}
}
public class RandomArityTemplate : ExpressionTemplate {
private readonly IDistribution arityDistribution;
public RandomArityTemplate(Func, double> transform, IDistribution arityDistribution) : base(transform) {
this.arityDistribution = arityDistribution;
}
public override Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false) {
var arity = arityDistribution.Sample();
return Instantiate(name, random, arity, sampleWithRepetition);
}
}
public class FixedArityTemplate : ExpressionTemplate {
private readonly int arity;
public FixedArityTemplate(Func, double> transform, int arity) : base(transform) {
this.arity = arity;
}
public override Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false) {
return Instantiate(name, random, arity, sampleWithRepetition);
}
}
public class BinaryOperatorTemplate : IExpressionTemplate {
public string Label { get; set; }
private readonly Func, double> transform;
private List> leftArguments;
private List> rightArguments;
public BinaryOperatorTemplate(Func, double> transform) {
this.transform = transform;
}
public void AddLeftArgument(Expression expression, double weight = 1d) {
leftArguments = leftArguments ?? new List>();
leftArguments.Add(Tuple.Create(expression, weight));
}
public void AddLeftArguments(IEnumerable expressions) {
leftArguments = leftArguments ?? new List>();
leftArguments.AddRange(expressions.Select(x => Tuple.Create(x, 1d)));
}
public void AddLeftArguments(IEnumerable expressions, IEnumerable weights) {
leftArguments = leftArguments ?? new List>();
leftArguments.AddRange(expressions.Zip(weights, Tuple.Create));
}
public void AddRightArgument(Expression expression, double weight = 1d) {
rightArguments = rightArguments ?? new List>();
rightArguments.Add(Tuple.Create(expression, weight));
}
public void AddRightArguments(IEnumerable expressions) {
rightArguments = rightArguments ?? new List>();
rightArguments.AddRange(expressions.Select(x => Tuple.Create(x, 1d)));
}
public void AddRightArguments(IEnumerable expressions, IEnumerable weights) {
rightArguments = rightArguments ?? new List>();
rightArguments.AddRange(expressions.Zip(weights, Tuple.Create));
}
public Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false) {
Expression leftArgument = null;
Expression rightArgument = null;
{
var weights = leftArguments.Select(x => x.Item2);
leftArgument = sampleWithRepetition
? leftArguments.SampleProportional(random, 1, weights, windowing: false).Select(x => x.Item1).First() :
leftArguments.SampleProportionalWithoutRepetition(random, 1, weights, windowing: false).Select(x => x.Item1).First();
}
{
var weights = rightArguments.Select(x => x.Item2);
rightArgument = sampleWithRepetition
? rightArguments.SampleProportional(random, 1, weights).Select(x => x.Item1).First() :
rightArguments.SampleProportionalWithoutRepetition(random, 1, weights).Select(x => x.Item1).First();
}
var func = Expression.Function(name, transform, new Expression[] { leftArgument, rightArgument });
func.Label = Label;
return func;
}
}
public class ScalingTemplate : ExpressionTemplate {
private readonly IDistribution scaledVariance;
// w * expr
public ScalingTemplate(IDistribution scaledVariance = null) : base(ExpressionGenerator.Product) {
this.scaledVariance = scaledVariance;
}
public override Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false) {
var expr = SampleArguments(random, 1, sampleWithRepetition).First();
// use the evaluator to determine variance of the sampled expression and initialize the constant accordingly to reach the target variance
var @const = 1.0;
if (scaledVariance != null) {
var evaluator = new ExpressionEvaluator();
var data = evaluator.GenerateData(expr, 10000);
var variance = data[expr].VariancePop();
@const = scaledVariance.Sample() / variance;
}
var func = Expression.Function(name, transform, new Expression[] { Expression.Constant(name, @const), expr });
func.Label = "*";
return func;
}
}
public class OffsetTemplate : ExpressionTemplate {
private readonly IDistribution offset;
// expr + offset
public OffsetTemplate(IDistribution offset = null) : base(ExpressionGenerator.Sum) {
this.offset = offset;
}
public override Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false) {
var expr = SampleArguments(random, 1, sampleWithRepetition).First();
// use the evaluator to determine variance of the sampled expression and initialize the constant accordingly to reach the target offset
var @const = 0.0;
if (offset != null) {
var evaluator = new ExpressionEvaluator();
var data = evaluator.GenerateData(expr, 10000);
var average = data[expr].Average();
@const = offset.Sample() - average;
}
var func = Expression.Function(name, transform, new Expression[] { Expression.Constant(name, @const), expr });
func.Label = "+";
return func;
}
}
public class RangeTemplate : ExpressionTemplate {
private readonly IDistribution minValue;
private readonly IDistribution valueRange;
// expr + offset
public RangeTemplate(IDistribution minValue = null, IDistribution valueRange = null) : base(null) {
this.minValue = minValue;
this.valueRange = valueRange;
}
public override Expression Instantiate(string name, IRandom random, bool sampleWithRepetition = false) {
var expr = SampleArguments(random, 1, sampleWithRepetition).First();
// use the evaluator to determine variance of the sampled expression and initialize the constant accordingly to reach the target offset
var mult = 1.0;
var add = 0.0;
if (minValue != null || valueRange != null) {
var evaluator = new ExpressionEvaluator();
var data = evaluator.GenerateData(expr, 10000);
if (valueRange != null) {
var targetRange = valueRange.Sample();
var min = data[expr].Min();
var max = data[expr].Max();
var range = max - min;
mult = targetRange / range;
add = -min + min / mult;
}
if (minValue != null) {
var targetMin = minValue.Sample();
var min = data[expr].Min();
add += (targetMin - min) / mult;
}
expr = Expression.Function(name + "-toZero", ExpressionGenerator.Sum, new Expression[] { Expression.Constant("name-add0", add), expr });
expr.Label = "+";
expr = Expression.Function(name + "-mult", ExpressionGenerator.Product, new Expression[] { Expression.Constant("name-mult", mult), expr });
expr.Label = "*";
}
return expr;
}
}
}