#region License Information
/* HeuristicLab
* Copyright (C) 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.Collections;
using System.Collections.Generic;
using System.Linq;
using HEAL.Attic;
using HeuristicLab.Analysis;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Random;
namespace HeuristicLab.Problems.DataAnalysis.Symbolic.Regression {
[StorableType("4318C6BD-E0A1-45FE-AC30-96E7F73B51FB")]
[Item("ShapeConstraintsAnalyzer", "Analyzes the number of shape constraint violations of symbolic regression models.")]
public class ShapeConstraintsAnalyzer : SymbolicDataAnalysisAnalyzer, ISymbolicExpressionTreeAnalyzer {
private const string ProblemDataParameterName = "ProblemData";
private const string ConstraintViolationsParameterName = "ConstraintViolations";
private const string InfeasibleSolutionsParameterName = "InfeasibleSolutions";
private const string AverageConstraintViolationsParameterName = "AverageConstraintViolations";
private const string SymbolicDataAnalysisTreeInterpreterParameterName = "SymbolicExpressionTreeInterpreter";
#region parameter properties
public ILookupParameter RegressionProblemDataParameter =>
(ILookupParameter) Parameters[ProblemDataParameterName];
public IResultParameter ConstraintViolationsParameter =>
(IResultParameter) Parameters[ConstraintViolationsParameterName];
public IResultParameter InfeasibleSolutionsParameter =>
(IResultParameter)Parameters[InfeasibleSolutionsParameterName];
public IResultParameter AverageConstraintViolationsParameter =>
(IResultParameter)Parameters[AverageConstraintViolationsParameterName];
public ILookupParameter SymbolicDataAnalysisTreeInterpreterParameter =>
(ILookupParameter)Parameters[SymbolicDataAnalysisTreeInterpreterParameterName];
#endregion
#region properties
public IRegressionProblemData RegressionProblemData => RegressionProblemDataParameter.ActualValue;
public DataTable ConstraintViolations => ConstraintViolationsParameter.ActualValue;
public DataTable InfeasibleSolutions => InfeasibleSolutionsParameter.ActualValue;
public DataTable AverageConstraintViolations => AverageConstraintViolationsParameter.ActualValue;
#endregion
public override bool EnabledByDefault => false;
[StorableConstructor]
protected ShapeConstraintsAnalyzer(StorableConstructorFlag _) : base(_) { }
protected ShapeConstraintsAnalyzer(ShapeConstraintsAnalyzer original, Cloner cloner) :
base(original, cloner) { }
public override IDeepCloneable Clone(Cloner cloner) {
return new ShapeConstraintsAnalyzer(this, cloner);
}
public ShapeConstraintsAnalyzer() {
Parameters.Add(new LookupParameter(ProblemDataParameterName,
"The problem data of the symbolic data analysis problem."));
Parameters.Add(new ResultParameter(ConstraintViolationsParameterName,
"The number of constraint violations."));
Parameters.Add(new ResultParameter(InfeasibleSolutionsParameterName,
"The number of infeasible solutions."));
Parameters.Add(new ResultParameter(AverageConstraintViolationsParameterName,
"The average violations of each constraint."));
Parameters.Add(new LookupParameter(SymbolicDataAnalysisTreeInterpreterParameterName,
"The interpreter that should be used to calculate the output values of the symbolic data analysis tree.") { Hidden = true });
ConstraintViolationsParameter.DefaultValue = new DataTable(ConstraintViolationsParameterName) {
VisualProperties = {
XAxisTitle = "Generations",
YAxisTitle = "Constraint violations"
}
};
InfeasibleSolutionsParameter.DefaultValue = new DataTable(InfeasibleSolutionsParameterName) {
VisualProperties = {
XAxisTitle = "Generations",
YAxisTitle = "Infeasible solutions"
}
};
AverageConstraintViolationsParameter.DefaultValue = new DataTable(SymbolicDataAnalysisTreeInterpreterParameterName) {
VisualProperties = {
XAxisTitle = "Generations",
YAxisTitle = "Average Constraint Violations"
}
};
}
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
if (!Parameters.ContainsKey(SymbolicDataAnalysisTreeInterpreterParameterName))
Parameters.Add(new LookupParameter(SymbolicDataAnalysisTreeInterpreterParameterName,
"The interpreter that should be used to calculate the output values of the symbolic data analysis tree.") { Hidden = true });
}
public override IOperation Apply() {
var problemData = (IShapeConstrainedRegressionProblemData)RegressionProblemData;
var trees = SymbolicExpressionTree.ToArray();
var results = ResultCollection;
var modelConstraints = problemData.ShapeConstraints.EnabledConstraints;
var extendedConstraints = problemData.CheckedExtendedConstraints;
var variableRanges = problemData.VariableRanges;
var constraintViolationsTable = ConstraintViolations;
var averageConstraintViolations = AverageConstraintViolations;
var interpreter = SymbolicDataAnalysisTreeInterpreterParameter.ActualValue;
var estimator = new IntervalArithBoundsEstimator();
if (!constraintViolationsTable.Rows.Any()) {
foreach (var constraint in modelConstraints) {
constraintViolationsTable.Rows.Add(new DataRow(constraint.ToString()));
averageConstraintViolations.Rows.Add(new DataRow(constraint.ToString()));
}
foreach (var extendedConstraint in extendedConstraints.SelectMany(x => x.ShapeConstraints.EnabledConstraints)) {
constraintViolationsTable.Rows.Add(new DataRow(extendedConstraint.ToString()));
averageConstraintViolations.Rows.Add(new DataRow(extendedConstraint.ToString()));
}
}
var violationsPerTree = new Dictionary();
var violationsPerConstraint = new Dictionary>();
foreach(var tree in trees) {
var violations = NMSESingleObjectiveConstraintsEvaluator.CalculateShapeConstraintsViolations(problemData, tree, interpreter, estimator, new MersenneTwister());
foreach(var violation in violations) {
var constraint = violation.Item1;
var error = violation.Item2;
if (!violationsPerConstraint.ContainsKey(constraint.ToString()))
violationsPerConstraint.Add(constraint.ToString(), new List());
violationsPerConstraint[constraint.ToString()].Add(error);
}
violationsPerTree.Add(tree, violations.Count(x => x.Item2 > 0));
}
foreach (var constraint in modelConstraints) {
var errors = violationsPerConstraint[constraint.ToString()];
constraintViolationsTable.Rows[constraint.ToString()].Values.Add(errors.Count(x => x > 0));
averageConstraintViolations.Rows[constraint.ToString()].Values.Add(errors.Sum() / errors.Count());
}
foreach (var extendedConstraint in extendedConstraints.SelectMany(x => x.ShapeConstraints.EnabledConstraints)) {
var errors = violationsPerConstraint[extendedConstraint.ToString()];
constraintViolationsTable.Rows[extendedConstraint.ToString()].Values.Add(errors.Count(x => x > 0));
averageConstraintViolations.Rows[extendedConstraint.ToString()].Values.Add(errors.Sum() / errors.Count());
}
var infeasibleSolutionsDataTable = InfeasibleSolutions;
if (infeasibleSolutionsDataTable.Rows.Count == 0)
infeasibleSolutionsDataTable.Rows.Add(new DataRow(InfeasibleSolutionsParameterName));
infeasibleSolutionsDataTable.Rows[InfeasibleSolutionsParameterName]
.Values
.Add(trees.Count(t => violationsPerTree[t] > 0));
return base.Apply();
}
}
}