Free cookie consent management tool by TermsFeed Policy Generator

Changeset 13310


Ignore:
Timestamp:
11/19/15 19:39:07 (8 years ago)
Author:
mkommend
Message:

#2175: Merged r13241, r13300, r13307, r13308 into stable.

Location:
stable
Files:
13 edited
5 copied

Legend:

Unmodified
Added
Removed
  • stable

  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic

  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression

  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4

    • Property svn:mergeinfo set to (toggle deleted branches)
      /stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regressionmergedeligible
      /trunk/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4mergedeligible
      /branches/Benchmarking/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression6917-7005
      /branches/Benchmarking/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.46917-7005
      /branches/CloningRefactoring/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression4656-4721
      /branches/CloningRefactoring/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.44656-4721
      /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5471-5473
      /branches/DataAnalysis Refactoring/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45471-5473
      /branches/DataAnalysis SolutionEnsembles/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5815-6180
      /branches/DataAnalysis SolutionEnsembles/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45815-6180
      /branches/DataAnalysis.ComplexityAnalyzer/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.410750-13239
      /branches/DataAnalysis/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression4458-4459,​4462,​4464
      /branches/DataAnalysis/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.44458-4459,​4462,​4464
      /branches/DataPreprocessing/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression10085-11101
      /branches/DataPreprocessing/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.410085-11101
      /branches/GP.Grammar.Editor/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression6284-6795
      /branches/GP.Grammar.Editor/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.46284-6795
      /branches/GP.Symbols (TimeLag, Diff, Integral)/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5060
      /branches/GP.Symbols (TimeLag, Diff, Integral)/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45060
      /branches/HLScript/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression10331-10358
      /branches/HLScript/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.410331-10358
      /branches/HeuristicLab.DatasetRefactor/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.411570-12508
      /branches/HeuristicLab.Problems.Orienteering/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.411130-12721
      /branches/HeuristicLab.TreeSimplifier/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression8388-8942
      /branches/HeuristicLab.TreeSimplifier/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.48388-8942
      /branches/LogResidualEvaluator/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression10202-10483
      /branches/LogResidualEvaluator/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.410202-10483
      /branches/NET40/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5138-5162
      /branches/NET40/sources/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45138-5162
      /branches/ParallelEngine/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5175-5192
      /branches/ParallelEngine/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45175-5192
      /branches/ProblemInstancesRegressionAndClassification/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression7748-7810
      /branches/ProblemInstancesRegressionAndClassification/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.47748-7810
      /branches/QAPAlgorithms/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression6350-6627
      /branches/QAPAlgorithms/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.46350-6627
      /branches/Restructure trunk solution/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression6828
      /branches/Restructure trunk solution/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.46828
      /branches/SpectralKernelForGaussianProcesses/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression10204-10479
      /branches/SpectralKernelForGaussianProcesses/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.410204-10479
      /branches/SuccessProgressAnalysis/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5370-5682
      /branches/SuccessProgressAnalysis/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45370-5682
      /branches/SymbolicExpressionTreeDiversityAnalyzers/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.412029-12100
      /branches/SymbolicExpressionTreeEncoding/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.412336-12421
      /branches/Trunk/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression6829-6865
      /branches/Trunk/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.46829-6865
      /branches/VNS/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5594-5752
      /branches/VNS/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45594-5752
      /branches/histogram/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression5959-6341
      /branches/histogram/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.45959-6341
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression-3.4.csproj

    r12281 r13310  
    123123  </ItemGroup>
    124124  <ItemGroup>
     125    <Compile Include="MultiObjective\PearsonRSquaredNestedTreeSizeEvaluator.cs" />
     126    <Compile Include="MultiObjective\PearsonRSquaredNumberOfVariablesEvaluator.cs" />
     127    <Compile Include="MultiObjective\PearsonRSquaredTreeComplexityEvaluator.cs" />
    125128    <Compile Include="MultiObjective\SymbolicRegressionMultiObjectiveValidationBestSolutionAnalyzer.cs" />
    126129    <Compile Include="Plugin.cs" />
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/PearsonRSquaredNestedTreeSizeEvaluator.cs

    r13241 r13310  
    4444    public PearsonRSquaredNestedTreeSizeEvaluator() : base() { }
    4545
    46     public override IEnumerable<bool> Maximization { get { return new bool[2] { true, false }; } }
     46    public override IEnumerable<bool> Maximization { get { return new bool[2] { true, false }; } } // maximize R² & minimize nested tree size
    4747
    4848    public override IOperation InstrumentedApply() {
     
    6767      if (decimalPlaces >= 0)
    6868        r2 = Math.Round(r2, decimalPlaces);
    69       return new double[2] { r2, solution.IterateNodesPostfix().Sum(n => n.GetLength()) };
     69      return new double[2] { r2, solution.IterateNodesPostfix().Sum(n => n.GetLength()) }; // sum of the length of the whole sub-tree for each node
    7070    }
    7171
     
    7474      EstimationLimitsParameter.ExecutionContext = context;
    7575      ApplyLinearScalingParameter.ExecutionContext = context;
     76      // DecimalPlaces parameter is a FixedValueParameter and doesn't need the context.
    7677
    77       double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
     78      double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces); 
    7879
    7980      SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = null;
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/PearsonRSquaredNumberOfVariablesEvaluator.cs

    r13241 r13310  
    4444    public PearsonRSquaredNumberOfVariablesEvaluator() : base() { }
    4545
    46     public override IEnumerable<bool> Maximization { get { return new bool[2] { true, false }; } }
     46    public override IEnumerable<bool> Maximization { get { return new bool[2] { true, false }; } } // maximize R² and minimize the number of variables
    4747
    4848    public override IOperation InstrumentedApply() {
     
    6666      if (decimalPlaces >= 0)
    6767        r2 = Math.Round(r2, decimalPlaces);
    68       return new double[2] { r2, solution.IterateNodesPostfix().OfType<VariableTreeNode>().Count() };
     68      return new double[2] { r2, solution.IterateNodesPostfix().OfType<VariableTreeNode>().Count() }; // count the number of variables
    6969    }
    7070
     
    7373      EstimationLimitsParameter.ExecutionContext = context;
    7474      ApplyLinearScalingParameter.ExecutionContext = context;
     75      // DecimalPlaces parameter is a FixedValueParameter and doesn't need the context.
    7576
    7677      double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/PearsonRSquaredTreeComplexityEvaluator.cs

    r13241 r13310  
    4343    public PearsonRSquaredTreeComplexityEvaluator() : base() { }
    4444
    45     public override IEnumerable<bool> Maximization { get { return new bool[2] { true, false }; } }
     45    public override IEnumerable<bool> Maximization { get { return new bool[2] { true, false }; } } // maximize R² and minimize model complexity
    4646
    4747    public override IOperation InstrumentedApply() {
     
    7272      EstimationLimitsParameter.ExecutionContext = context;
    7373      ApplyLinearScalingParameter.ExecutionContext = context;
     74      // DecimalPlaces parameter is a FixedValueParameter and doesn't need the context.
    7475
    7576      double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectiveEvaluator.cs

    r12009 r13310  
    2222
    2323using HeuristicLab.Common;
     24using HeuristicLab.Core;
     25using HeuristicLab.Data;
     26using HeuristicLab.Parameters;
    2427using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     28
    2529namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression {
     30  [StorableClass]
    2631  public abstract class SymbolicRegressionMultiObjectiveEvaluator : SymbolicDataAnalysisMultiObjectiveEvaluator<IRegressionProblemData>, ISymbolicRegressionMultiObjectiveEvaluator {
     32    private const string DecimalPlacesParameterName = "Decimal Places";
     33    private const string UseConstantOptimizationParameterName = "Use constant optimization";
     34    private const string ConstantOptimizationIterationsParameterName = "Constant optimization iterations";
     35
     36    public IFixedValueParameter<IntValue> DecimalPlacesParameter {
     37      get { return (IFixedValueParameter<IntValue>)Parameters[DecimalPlacesParameterName]; }
     38    }
     39    public IFixedValueParameter<BoolValue> UseConstantOptimizationParameter {
     40      get { return (IFixedValueParameter<BoolValue>)Parameters[UseConstantOptimizationParameterName]; }
     41    }
     42
     43    public IFixedValueParameter<IntValue> ConstantOptimizationIterationsParameter {
     44      get { return (IFixedValueParameter<IntValue>)Parameters[ConstantOptimizationIterationsParameterName]; }
     45    }
     46
     47
     48    public int DecimalPlaces {
     49      get { return DecimalPlacesParameter.Value.Value; }
     50      set { DecimalPlacesParameter.Value.Value = value; }
     51    }
     52    public bool UseConstantOptimization {
     53      get { return UseConstantOptimizationParameter.Value.Value; }
     54      set { UseConstantOptimizationParameter.Value.Value = value; }
     55    }
     56    public int ConstantOptimizationIterations {
     57      get { return ConstantOptimizationIterationsParameter.Value.Value; }
     58      set { ConstantOptimizationIterationsParameter.Value.Value = value; }
     59    }
     60
    2761    [StorableConstructor]
    2862    protected SymbolicRegressionMultiObjectiveEvaluator(bool deserializing) : base(deserializing) { }
     
    3165    }
    3266
    33     protected SymbolicRegressionMultiObjectiveEvaluator() : base() { }
     67    protected SymbolicRegressionMultiObjectiveEvaluator()
     68      : base() {
     69      Parameters.Add(new FixedValueParameter<IntValue>(DecimalPlacesParameterName, "The number of decimal places used for rounding the quality values.", new IntValue(5)) { Hidden = true });
     70      Parameters.Add(new FixedValueParameter<BoolValue>(UseConstantOptimizationParameterName, "", new BoolValue(false)));
     71      Parameters.Add(new FixedValueParameter<IntValue>(ConstantOptimizationIterationsParameterName, "The number of iterations constant optimization should be applied.", new IntValue(5)));
     72    }
     73
     74    [StorableHook(HookType.AfterDeserialization)]
     75    private void AfterDeserialization() {
     76      if (!Parameters.ContainsKey(UseConstantOptimizationParameterName)) {
     77        Parameters.Add(new FixedValueParameter<BoolValue>(UseConstantOptimizationParameterName, "", new BoolValue(false)));
     78      }
     79      if (!Parameters.ContainsKey(DecimalPlacesParameterName)) {
     80        Parameters.Add(new FixedValueParameter<IntValue>(DecimalPlacesParameterName, "The number of decimal places used for rounding the quality values.", new IntValue(-1)) { Hidden = true });
     81      }
     82      if (!Parameters.ContainsKey(ConstantOptimizationIterationsParameterName)) {
     83        Parameters.Add(new FixedValueParameter<IntValue>(ConstantOptimizationIterationsParameterName, "The number of iterations constant optimization should be applied.", new IntValue(5)));
     84      }
     85    }
    3486  }
    3587}
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectiveMeanSquaredErrorTreeSizeEvaluator.cs

    r12009 r13310  
    2020#endregion
    2121
     22using System;
    2223using System.Collections.Generic;
    2324using HeuristicLab.Common;
     
    4748      IEnumerable<int> rows = GenerateRowsToEvaluate();
    4849      var solution = SymbolicExpressionTreeParameter.ActualValue;
    49       double[] qualities = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, solution, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, ProblemDataParameter.ActualValue, rows, ApplyLinearScalingParameter.ActualValue.Value);
     50      var problemData = ProblemDataParameter.ActualValue;
     51      var interpreter = SymbolicDataAnalysisTreeInterpreterParameter.ActualValue;
     52      var estimationLimits = EstimationLimitsParameter.ActualValue;
     53      var applyLinearScaling = ApplyLinearScalingParameter.ActualValue.Value;
     54
     55      if (UseConstantOptimization) {
     56        SymbolicRegressionConstantOptimizationEvaluator.OptimizeConstants(interpreter, solution, problemData, rows, applyLinearScaling, ConstantOptimizationIterations, estimationLimits.Upper, estimationLimits.Lower);
     57      }
     58
     59      double[] qualities = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, solution, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, ProblemDataParameter.ActualValue, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
    5060      QualitiesParameter.ActualValue = new DoubleArray(qualities);
    5161      return base.InstrumentedApply();
    5262    }
    5363
    54     public static double[] Calculate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree solution, double lowerEstimationLimit, double upperEstimationLimit, IRegressionProblemData problemData, IEnumerable<int> rows, bool applyLinearScaling) {
    55       IEnumerable<double> estimatedValues = interpreter.GetSymbolicExpressionTreeValues(solution, problemData.Dataset, rows);
    56       IEnumerable<double> targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
    57       OnlineCalculatorError errorState;
     64    public static double[] Calculate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree solution, double lowerEstimationLimit, double upperEstimationLimit, IRegressionProblemData problemData, IEnumerable<int> rows, bool applyLinearScaling, int decimalPlaces) {
     65      var mse = SymbolicRegressionSingleObjectiveMeanSquaredErrorEvaluator.Calculate(interpreter, solution, lowerEstimationLimit,
     66        upperEstimationLimit, problemData, rows, applyLinearScaling);
    5867
    59       double mse;
    60       if (applyLinearScaling) {
    61         var mseCalculator = new OnlineMeanSquaredErrorCalculator();
    62         CalculateWithScaling(targetValues, estimatedValues, lowerEstimationLimit, upperEstimationLimit, mseCalculator, problemData.Dataset.Rows);
    63         errorState = mseCalculator.ErrorState;
    64         mse = mseCalculator.MeanSquaredError;
    65       } else {
    66         IEnumerable<double> boundedEstimatedValues = estimatedValues.LimitToRange(lowerEstimationLimit, upperEstimationLimit);
    67         mse = OnlineMeanSquaredErrorCalculator.Calculate(targetValues, boundedEstimatedValues, out errorState);
    68       }
    69       if (errorState != OnlineCalculatorError.None) mse = double.NaN;
     68      if (decimalPlaces >= 0)
     69        mse = Math.Round(mse, decimalPlaces);
     70
    7071      return new double[2] { mse, solution.Length };
    7172    }
     
    7677      ApplyLinearScalingParameter.ExecutionContext = context;
    7778
    78       double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value);
     79      double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
    7980
    8081      SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = null;
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectivePearsonRSquaredTreeSizeEvaluator.cs

    r12669 r13310  
    2020#endregion
    2121
     22using System;
    2223using System.Collections.Generic;
    2324using HeuristicLab.Common;
     
    4748      IEnumerable<int> rows = GenerateRowsToEvaluate();
    4849      var solution = SymbolicExpressionTreeParameter.ActualValue;
    49       double[] qualities = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, solution, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, ProblemDataParameter.ActualValue, rows, ApplyLinearScalingParameter.ActualValue.Value);
     50      var problemData = ProblemDataParameter.ActualValue;
     51      var interpreter = SymbolicDataAnalysisTreeInterpreterParameter.ActualValue;
     52      var estimationLimits = EstimationLimitsParameter.ActualValue;
     53      var applyLinearScaling = ApplyLinearScalingParameter.ActualValue.Value;
     54
     55      if (UseConstantOptimization) {
     56        SymbolicRegressionConstantOptimizationEvaluator.OptimizeConstants(interpreter, solution, problemData, rows, applyLinearScaling, ConstantOptimizationIterations, estimationLimits.Upper, estimationLimits.Lower);
     57      }
     58      double[] qualities = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, solution, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, ProblemDataParameter.ActualValue, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
    5059      QualitiesParameter.ActualValue = new DoubleArray(qualities);
    5160      return base.InstrumentedApply();
    5261    }
    5362
    54     public static double[] Calculate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree solution, double lowerEstimationLimit, double upperEstimationLimit, IRegressionProblemData problemData, IEnumerable<int> rows, bool applyLinearScaling) {
    55       IEnumerable<double> estimatedValues = interpreter.GetSymbolicExpressionTreeValues(solution, problemData.Dataset, rows);
    56       IEnumerable<double> targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, rows);
    57       OnlineCalculatorError errorState;
    58 
    59       double r;
    60       if (applyLinearScaling) {
    61         var rCalculator = new OnlinePearsonsRCalculator();
    62         CalculateWithScaling(targetValues, estimatedValues, lowerEstimationLimit, upperEstimationLimit, rCalculator, problemData.Dataset.Rows);
    63         errorState = rCalculator.ErrorState;
    64         r = rCalculator.R;
    65       } else {
    66         IEnumerable<double> boundedEstimatedValues = estimatedValues.LimitToRange(lowerEstimationLimit, upperEstimationLimit);
    67         r = OnlinePearsonsRCalculator.Calculate(targetValues, boundedEstimatedValues, out errorState);
    68       }
    69 
    70       if (errorState != OnlineCalculatorError.None) r = double.NaN;
    71       return new double[2] { r*r, solution.Length };
     63    public static double[] Calculate(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree solution, double lowerEstimationLimit, double upperEstimationLimit, IRegressionProblemData problemData, IEnumerable<int> rows, bool applyLinearScaling, int decimalPlaces) {
     64      double r2 = SymbolicRegressionSingleObjectivePearsonRSquaredEvaluator.Calculate(interpreter, solution, lowerEstimationLimit, upperEstimationLimit, problemData, rows, applyLinearScaling);
     65      if (decimalPlaces >= 0)
     66        r2 = Math.Round(r2, decimalPlaces);
     67      return new double[2] { r2, solution.Length };
    7268    }
    7369
     
    7773      ApplyLinearScalingParameter.ExecutionContext = context;
    7874
    79       double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value);
     75      double[] quality = Calculate(SymbolicDataAnalysisTreeInterpreterParameter.ActualValue, tree, EstimationLimitsParameter.ActualValue.Lower, EstimationLimitsParameter.ActualValue.Upper, problemData, rows, ApplyLinearScalingParameter.ActualValue.Value, DecimalPlaces);
    8076
    8177      SymbolicDataAnalysisTreeInterpreterParameter.ExecutionContext = null;
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/MultiObjective/SymbolicRegressionMultiObjectiveTrainingBestSolutionAnalyzer.cs

    r12009 r13310  
    2020#endregion
    2121
     22using System.Collections.Generic;
     23using System.Linq;
     24using HeuristicLab.Analysis;
    2225using HeuristicLab.Common;
    2326using HeuristicLab.Core;
     27using HeuristicLab.Data;
    2428using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
     29using HeuristicLab.Optimization;
    2530using HeuristicLab.Parameters;
    2631using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
     
    3742    private const string SymbolicDataAnalysisTreeInterpreterParameterName = "SymbolicDataAnalysisTreeInterpreter";
    3843    private const string EstimationLimitsParameterName = "EstimationLimits";
     44    private const string MaximumSymbolicExpressionTreeLengthParameterName = "MaximumSymbolicExpressionTreeLength";
     45    private const string ValidationPartitionParameterName = "ValidationPartition";
     46
    3947    #region parameter properties
    4048    public ILookupParameter<IRegressionProblemData> ProblemDataParameter {
     
    4755      get { return (IValueLookupParameter<DoubleLimit>)Parameters[EstimationLimitsParameterName]; }
    4856    }
     57    public ILookupParameter<IntValue> MaximumSymbolicExpressionTreeLengthParameter {
     58      get { return (ILookupParameter<IntValue>)Parameters[MaximumSymbolicExpressionTreeLengthParameterName]; }
     59    }
     60
     61    public IValueLookupParameter<IntRange> ValidationPartitionParameter {
     62      get { return (IValueLookupParameter<IntRange>)Parameters[ValidationPartitionParameterName]; }
     63    }
    4964    #endregion
    5065
     
    5469    public SymbolicRegressionMultiObjectiveTrainingBestSolutionAnalyzer()
    5570      : base() {
    56       Parameters.Add(new LookupParameter<IRegressionProblemData>(ProblemDataParameterName, "The problem data for the symbolic regression solution."));
    57       Parameters.Add(new LookupParameter<ISymbolicDataAnalysisExpressionTreeInterpreter>(SymbolicDataAnalysisTreeInterpreterParameterName, "The symbolic data analysis tree interpreter for the symbolic expression tree."));
    58       Parameters.Add(new ValueLookupParameter<DoubleLimit>(EstimationLimitsParameterName, "The lower and upper limit for the estimated values produced by the symbolic regression model."));
     71      Parameters.Add(new LookupParameter<IRegressionProblemData>(ProblemDataParameterName, "The problem data for the symbolic regression solution.") { Hidden = true });
     72      Parameters.Add(new LookupParameter<ISymbolicDataAnalysisExpressionTreeInterpreter>(SymbolicDataAnalysisTreeInterpreterParameterName, "The symbolic data analysis tree interpreter for the symbolic expression tree.") { Hidden = true });
     73      Parameters.Add(new ValueLookupParameter<DoubleLimit>(EstimationLimitsParameterName, "The lower and upper limit for the estimated values produced by the symbolic regression model.") { Hidden = true });
     74      Parameters.Add(new LookupParameter<IntValue>(MaximumSymbolicExpressionTreeLengthParameterName, "Maximal length of the symbolic expression.") { Hidden = true });
     75      Parameters.Add(new ValueLookupParameter<IntRange>(ValidationPartitionParameterName, "The validation partition."));
     76    }
     77
     78    [StorableHook(HookType.AfterDeserialization)]
     79    private void AfterDeserialization() {
     80      if (!Parameters.ContainsKey(MaximumSymbolicExpressionTreeLengthParameterName))
     81        Parameters.Add(new LookupParameter<IntValue>(MaximumSymbolicExpressionTreeLengthParameterName, "Maximal length of the symbolic expression.") { Hidden = true });
     82      if (!Parameters.ContainsKey(ValidationPartitionParameterName))
     83        Parameters.Add(new ValueLookupParameter<IntRange>(ValidationPartitionParameterName, "The validation partition."));
    5984    }
    6085
     
    6893      return new SymbolicRegressionSolution(model, (IRegressionProblemData)ProblemDataParameter.ActualValue.Clone());
    6994    }
     95
     96    public override IOperation Apply() {
     97      var operation = base.Apply();
     98      var paretoFront = TrainingBestSolutionsParameter.ActualValue;
     99
     100      IResult result;
     101      ScatterPlot qualityToTreeSize;
     102      if (!ResultCollection.TryGetValue("Pareto Front Analysis", out result)) {
     103        qualityToTreeSize = new ScatterPlot("Quality vs Tree Size", "");
     104        qualityToTreeSize.VisualProperties.XAxisMinimumAuto = false;
     105        qualityToTreeSize.VisualProperties.XAxisMaximumAuto = false;
     106        qualityToTreeSize.VisualProperties.YAxisMinimumAuto = false;
     107        qualityToTreeSize.VisualProperties.YAxisMaximumAuto = false;
     108
     109        qualityToTreeSize.VisualProperties.XAxisMinimumFixedValue = 0;
     110        qualityToTreeSize.VisualProperties.XAxisMaximumFixedValue = MaximumSymbolicExpressionTreeLengthParameter.ActualValue.Value;
     111        qualityToTreeSize.VisualProperties.YAxisMinimumFixedValue = 0;
     112        qualityToTreeSize.VisualProperties.YAxisMaximumFixedValue = 2;
     113        ResultCollection.Add(new Result("Pareto Front Analysis", qualityToTreeSize));
     114      } else {
     115        qualityToTreeSize = (ScatterPlot)result.Value;
     116      }
     117
     118
     119      int previousTreeLength = -1;
     120      var sizeParetoFront = new LinkedList<ISymbolicRegressionSolution>();
     121      foreach (var solution in paretoFront.OrderBy(s => s.Model.SymbolicExpressionTree.Length)) {
     122        int treeLength = solution.Model.SymbolicExpressionTree.Length;
     123        if (!sizeParetoFront.Any()) sizeParetoFront.AddLast(solution);
     124        if (solution.TrainingNormalizedMeanSquaredError < sizeParetoFront.Last.Value.TrainingNormalizedMeanSquaredError) {
     125          if (treeLength == previousTreeLength)
     126            sizeParetoFront.RemoveLast();
     127          sizeParetoFront.AddLast(solution);
     128        }
     129        previousTreeLength = treeLength;
     130      }
     131
     132      qualityToTreeSize.Rows.Clear();
     133      var trainingRow = new ScatterPlotDataRow("Training NMSE", "", sizeParetoFront.Select(x => new Point2D<double>(x.Model.SymbolicExpressionTree.Length, x.TrainingNormalizedMeanSquaredError)));
     134      trainingRow.VisualProperties.PointSize = 8;
     135      qualityToTreeSize.Rows.Add(trainingRow);
     136
     137      var validationPartition = ValidationPartitionParameter.ActualValue;
     138      if (validationPartition.Size != 0) {
     139        var problemData = ProblemDataParameter.ActualValue;
     140        var validationIndizes = Enumerable.Range(validationPartition.Start, validationPartition.Size).ToList();
     141        var targetValues = problemData.Dataset.GetDoubleValues(problemData.TargetVariable, validationIndizes).ToList();
     142        OnlineCalculatorError error;
     143        var validationRow = new ScatterPlotDataRow("Validation NMSE", "",
     144          sizeParetoFront.Select(x => new Point2D<double>(x.Model.SymbolicExpressionTree.Length,
     145          OnlineNormalizedMeanSquaredErrorCalculator.Calculate(targetValues, x.GetEstimatedValues(validationIndizes), out error))));
     146        validationRow.VisualProperties.PointSize = 7;
     147        qualityToTreeSize.Rows.Add(validationRow);
     148      }
     149
     150      return operation;
     151    }
     152
    70153  }
    71154}
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SingleObjective/Evaluators/SymbolicRegressionConstantOptimizationEvaluator.cs

    r12702 r13310  
    164164
    165165
     166    // TODO: swap positions of lowerEstimationLimit and upperEstimationLimit parameters
    166167    public static double OptimizeConstants(ISymbolicDataAnalysisExpressionTreeInterpreter interpreter, ISymbolicExpressionTree tree, IRegressionProblemData problemData,
    167168      IEnumerable<int> rows, bool applyLinearScaling, int maxIterations, double upperEstimationLimit = double.MaxValue, double lowerEstimationLimit = double.MinValue, bool updateConstantsInTree = true) {
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic.Regression/3.4/SymbolicRegressionSolution.cs

    r12009 r13310  
    118118      estimationLimitResults.Add(new Result(TestNaNEvaluationsResultName, "", new IntValue()));
    119119      Add(new Result(EstimationLimitsResultsResultName, "Results concerning the estimation limits of symbolic regression solution", estimationLimitResults));
    120 
    121120      RecalculateResults();
    122121    }
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Analyzers/SymbolicDataAnalysisMultiObjectiveTrainingBestSolutionAnalyzer.cs

    r12009 r13310  
    2020#endregion
    2121
    22 using System;
    2322using System.Collections.Generic;
    2423using System.Linq;
     
    4241    private const string TrainingBestSolutionQualitiesParameterName = "Best training solution qualities";
    4342    private const string UpdateAlwaysParameterName = "Always update best solutions";
     43    private const string TrainingBestSolutionParameterName = "Best training solution";
    4444
    4545    #region parameter properties
     
    5555    #endregion
    5656    #region properties
    57     public ItemList<T> TrainingBestSolutions {
     57    private ItemList<T> TrainingBestSolutions {
    5858      get { return TrainingBestSolutionsParameter.ActualValue; }
    5959      set { TrainingBestSolutionsParameter.ActualValue = value; }
    6060    }
    61     public ItemList<DoubleArray> TrainingBestSolutionQualities {
     61    private ItemList<DoubleArray> TrainingBestSolutionQualities {
    6262      get { return TrainingBestSolutionQualitiesParameter.ActualValue; }
    6363      set { TrainingBestSolutionQualitiesParameter.ActualValue = value; }
    6464    }
    65     public BoolValue UpdateAlways {
    66       get { return UpdateAlwaysParameter.Value; }
     65    public bool UpdateAlways {
     66      get { return UpdateAlwaysParameter.Value.Value; }
     67      set { UpdateAlwaysParameter.Value.Value = value; }
    6768    }
    6869    #endregion
     
    9798      }
    9899
     100      if (!results.ContainsKey(TrainingBestSolutionParameterName)) {
     101        results.Add(new Result(TrainingBestSolutionParameterName, "", typeof(ISymbolicDataAnalysisSolution)));
     102      }
     103
    99104      //if the pareto front of best solutions shall be updated regardless of the quality, the list initialized empty to discard old solutions
    100       IList<double[]> trainingBestQualities;
    101       if (UpdateAlways.Value) {
     105      List<double[]> trainingBestQualities;
     106      if (UpdateAlways) {
    102107        trainingBestQualities = new List<double[]>();
    103108      } else {
     
    105110      }
    106111
    107       #region find best trees
    108       IList<int> nonDominatedIndexes = new List<int>();
    109       ISymbolicExpressionTree[] tree = SymbolicExpressionTree.ToArray();
     112      ISymbolicExpressionTree[] trees = SymbolicExpressionTree.ToArray();
    110113      List<double[]> qualities = Qualities.Select(x => x.ToArray()).ToList();
    111114      bool[] maximization = Maximization.ToArray();
    112       List<double[]> newNonDominatedQualities = new List<double[]>();
    113       for (int i = 0; i < tree.Length; i++) {
    114         if (IsNonDominated(qualities[i], trainingBestQualities, maximization) &&
    115           IsNonDominated(qualities[i], qualities, maximization)) {
    116           if (!newNonDominatedQualities.Contains(qualities[i], new DoubleArrayComparer())) {
    117             newNonDominatedQualities.Add(qualities[i]);
    118             nonDominatedIndexes.Add(i);
     115
     116      var nonDominatedIndividuals = new[] { new { Tree = default(ISymbolicExpressionTree), Qualities = default(double[]) } }.ToList();
     117      nonDominatedIndividuals.Clear();
     118
     119      // build list of new non-dominated solutions
     120      for (int i = 0; i < trees.Length; i++) {
     121        if (IsNonDominated(qualities[i], nonDominatedIndividuals.Select(ind => ind.Qualities), maximization) &&
     122            IsNonDominated(qualities[i], trainingBestQualities, maximization)) {
     123          for (int j = nonDominatedIndividuals.Count - 1; j >= 0; j--) {
     124            if (IsBetterOrEqual(qualities[i], nonDominatedIndividuals[j].Qualities, maximization)) {
     125              nonDominatedIndividuals.RemoveAt(j);
     126            }
    119127          }
     128          nonDominatedIndividuals.Add(new { Tree = trees[i], Qualities = qualities[i] });
    120129        }
    121130      }
    122       #endregion
     131
     132      var nonDominatedSolutions = nonDominatedIndividuals.Select(x => new { Solution = CreateSolution(x.Tree, x.Qualities), Qualities = x.Qualities }).ToList();
     133      nonDominatedSolutions.ForEach(s => s.Solution.Name = string.Join(",", s.Qualities.Select(q => q.ToString())));
     134
    123135      #region update Pareto-optimal solution archive
    124       if (nonDominatedIndexes.Count > 0) {
    125         ItemList<DoubleArray> nonDominatedQualities = new ItemList<DoubleArray>();
    126         ItemList<T> nonDominatedSolutions = new ItemList<T>();
    127         // add all new non-dominated solutions to the archive
    128         foreach (var index in nonDominatedIndexes) {
    129           T solution = CreateSolution(tree[index], qualities[index]);
    130           nonDominatedSolutions.Add(solution);
    131           nonDominatedQualities.Add(new DoubleArray(qualities[index]));
    132         }
    133         // add old non-dominated solutions only if they are not dominated by one of the new solutions
     136      if (nonDominatedSolutions.Count > 0) {
     137        //add old non-dominated solutions only if they are not dominated by one of the new solutions
    134138        for (int i = 0; i < trainingBestQualities.Count; i++) {
    135           if (IsNonDominated(trainingBestQualities[i], newNonDominatedQualities, maximization)) {
    136             if (!newNonDominatedQualities.Contains(trainingBestQualities[i], new DoubleArrayComparer())) {
    137               nonDominatedSolutions.Add(TrainingBestSolutions[i]);
    138               nonDominatedQualities.Add(TrainingBestSolutionQualities[i]);
    139             }
     139          if (IsNonDominated(trainingBestQualities[i], nonDominatedSolutions.Select(x => x.Qualities), maximization)) {
     140            nonDominatedSolutions.Add(new { Solution = TrainingBestSolutions[i], Qualities = TrainingBestSolutionQualities[i].ToArray() });
    140141          }
    141142        }
    142143
    143         results[TrainingBestSolutionsParameter.Name].Value = nonDominatedSolutions;
    144         results[TrainingBestSolutionQualitiesParameter.Name].Value = nonDominatedQualities;
     144        //assumes the the first objective is always the accuracy
     145        var sortedNonDominatedSolutions = maximization[0]
     146          ? nonDominatedSolutions.OrderByDescending(x => x.Qualities[0])
     147          : nonDominatedSolutions.OrderBy(x => x.Qualities[0]);
     148        var trainingBestSolution = sortedNonDominatedSolutions.Select(s => s.Solution).First();
     149        results[TrainingBestSolutionParameterName].Value = trainingBestSolution;
     150        TrainingBestSolutions = new ItemList<T>(sortedNonDominatedSolutions.Select(x => x.Solution));
     151        results[TrainingBestSolutionsParameter.Name].Value = TrainingBestSolutions;
     152        TrainingBestSolutionQualities = new ItemList<DoubleArray>(sortedNonDominatedSolutions.Select(x => new DoubleArray(x.Qualities)));
     153        results[TrainingBestSolutionQualitiesParameter.Name].Value = TrainingBestSolutionQualities;
    145154      }
    146155      #endregion
     
    148157    }
    149158
    150     private class DoubleArrayComparer : IEqualityComparer<double[]> {
    151       public bool Equals(double[] x, double[] y) {
    152         if (y.Length != x.Length) throw new ArgumentException();
    153         for (int i = 0; i < x.Length; i++) {
    154           if (!x[i].IsAlmost(y[i])) return false;
    155         }
    156         return true;
    157       }
    158 
    159       public int GetHashCode(double[] obj) {
    160         int c = obj.Length;
    161         for (int i = 0; i < obj.Length; i++)
    162           c ^= obj[i].GetHashCode();
    163         return c;
    164       }
    165     }
    166 
    167159    protected abstract T CreateSolution(ISymbolicExpressionTree bestTree, double[] bestQuality);
    168160
    169     private bool IsNonDominated(double[] point, IList<double[]> points, bool[] maximization) {
     161    private bool IsNonDominated(double[] point, IEnumerable<double[]> points, bool[] maximization) {
    170162      foreach (var refPoint in points) {
    171         bool refPointDominatesPoint = true;
    172         for (int i = 0; i < point.Length; i++) {
    173           refPointDominatesPoint &= IsBetterOrEqual(refPoint[i], point[i], maximization[i]);
    174         }
     163        bool refPointDominatesPoint = IsBetterOrEqual(refPoint, point, maximization);
    175164        if (refPointDominatesPoint) return false;
    176165      }
    177166      return true;
    178167    }
     168
     169    private bool IsBetterOrEqual(double[] lhs, double[] rhs, bool[] maximization) {
     170      for (int i = 0; i < lhs.Length; i++) {
     171        var result = IsBetterOrEqual(lhs[i], rhs[i], maximization[i]);
     172        if (!result) return false;
     173      }
     174      return true;
     175    }
     176
    179177    private bool IsBetterOrEqual(double lhs, double rhs, bool maximization) {
    180       if (maximization) return lhs > rhs;
    181       else return lhs < rhs;
     178      if (maximization) return lhs >= rhs;
     179      else return lhs <= rhs;
    182180    }
    183181  }
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj

    r13047 r13310  
    138138    <Compile Include="Importer\Token.cs" />
    139139    <Compile Include="Interfaces\IModelBacktransformator.cs" />
     140    <Compile Include="SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs" />
     141    <Compile Include="SymbolicDataAnalysisModelComplexityCalculator.cs" />
    140142    <Compile Include="SymbolicExpressionTreeBacktransformator.cs" />
    141143    <Compile Include="SymbolicDataAnalysisExpressionPruningOperator.cs" />
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisExpressionTreeSimplificationOperator.cs

    r13241 r13310  
    2828
    2929namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
    30   [Item("SymbolicExpressionTreeSimplificationOperator", "Simplfies symbolic expression trees encoding a mathematical formula.")]
     30  [Item("SymbolicExpressionTreeSimplificationOperator", "Simplifies symbolic expression trees encoding a mathematical formula.")]
    3131  [StorableClass]
    3232  public class SymbolicDataAnalysisExpressionTreeSimplificationOperator : SingleSuccessorOperator, ISymbolicExpressionTreeOperator {
  • stable/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/SymbolicDataAnalysisModelComplexityCalculator.cs

    r13241 r13310  
    4242            return 2;
    4343          }
    44         case OpCodes.Add: {
    45             double complexity = 0;
    46             for (int i = 0; i < node.SubtreeCount; i++) {
    47               complexity += CalculateComplexity(node.GetSubtree(i));
    48             }
    49             return complexity;
    50           }
     44        case OpCodes.Add:
    5145        case OpCodes.Sub: {
    5246            double complexity = 0;
     
    5650            return complexity;
    5751          }
    58         case OpCodes.Mul: {
    59             double complexity = 1;
    60             for (int i = 0; i < node.SubtreeCount; i++) {
    61               var nodeComplexity = CalculateComplexity(node.GetSubtree(i));
    62               complexity *= nodeComplexity + 1;
    63             }
    64             return complexity;
    65           }
     52        case OpCodes.Mul:
    6653        case OpCodes.Div: {
    6754            double complexity = 1;
     
    7259            return complexity;
    7360          }
    74         case OpCodes.Sin: {
    75             double complexity = CalculateComplexity(node.GetSubtree(0));
    76             return Math.Pow(2.0, complexity);
    77           }
    78         case OpCodes.Cos: {
    79             double complexity = CalculateComplexity(node.GetSubtree(0));
    80             return Math.Pow(2.0, complexity);
    81           }
    82         case OpCodes.Tan: {
    83             double complexity = CalculateComplexity(node.GetSubtree(0));
    84             return Math.Pow(2.0, complexity);
    85           }
    86         case OpCodes.Exp: {
    87             double complexity = CalculateComplexity(node.GetSubtree(0));
    88             return Math.Pow(2.0, complexity);
    89           }
     61        case OpCodes.Sin:
     62        case OpCodes.Cos:
     63        case OpCodes.Tan:
     64        case OpCodes.Exp:
    9065        case OpCodes.Log: {
    9166            double complexity = CalculateComplexity(node.GetSubtree(0));
     
    10075            return complexity * complexity * complexity;
    10176          }
    102         case OpCodes.Power: {
     77        case OpCodes.Power:         
     78        case OpCodes.Root: {
    10379            double complexity = CalculateComplexity(node.GetSubtree(0));
    104             var exponentNode = node.GetSubtree(1) as ConstantTreeNode;
    105             if (exponentNode != null) {
    106               double exponent = exponentNode.Value;
    107               if (exponent < 0) exponent = Math.Abs(exponent);
    108               if (exponent < 1) exponent = 1 / exponent;
    109               return Math.Pow(complexity, Math.Round(exponent));
     80            var exponent = node.GetSubtree(1) as ConstantTreeNode;
     81            if (exponent != null) {
     82              double expVal = exponent.Value;
     83              if (expVal < 0) expVal = Math.Abs(expVal);
     84              if (expVal < 1) expVal = 1 / expVal;
     85              return Math.Pow(complexity, Math.Round(expVal));
    11086            }
    11187
    112             double exponentComplexity = CalculateComplexity(node.GetSubtree(1));
    113             return Math.Pow(complexity, 2 * exponentComplexity);
    114           }
    115         case OpCodes.Root: {
    116             double complexity = CalculateComplexity(node.GetSubtree(0));
    117             var rootNode = node.GetSubtree(1) as ConstantTreeNode;
    118             if (rootNode != null) {
    119               double root = rootNode.Value;
    120               if (root < 0) root = Math.Abs(root);
    121               if (root < 1) root = 1 / root;
    122               return Math.Pow(complexity, Math.Round(root));
    123             }
    124 
    125             double rootComplexity = CalculateComplexity(node.GetSubtree(1));
    126             return Math.Pow(complexity, 2 * rootComplexity);
     88            double expComplexity = CalculateComplexity(node.GetSubtree(1));
     89            return Math.Pow(complexity, 2 * expComplexity);
    12790          }
    12891
Note: See TracChangeset for help on using the changeset viewer.