#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.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
using HeuristicLab.Optimization;
using HeuristicLab.Parameters;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using HeuristicLab.Problems.DataAnalysis;
using HeuristicLab.Problems.DataAnalysis.Symbolic;
using HeuristicLab.Problems.DataAnalysis.Symbolic.Regression;
namespace HeuristicLab.Algorithms.DataAnalysis.Experimental {
[Item("RBF (alglib)", "")]
[Creatable(CreatableAttribute.Categories.DataAnalysisRegression, Priority = 102)]
[StorableClass]
public sealed class RBF : FixedDataAnalysisAlgorithm {
[StorableConstructor]
private RBF(bool deserializing) : base(deserializing) { }
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
}
private RBF(RBF original, Cloner cloner)
: base(original, cloner) {
}
public override IDeepCloneable Clone(Cloner cloner) {
return new RBF(this, cloner);
}
public RBF()
: base() {
Problem = new RegressionProblem();
Parameters.Add(new ValueParameter("RBase", new DoubleValue(1.0)));
Parameters.Add(new ValueParameter("NLayers", new IntValue(3)));
Parameters.Add(new ValueParameter("LambdaNS", new DoubleValue(1.0)));
}
protected override void Run(CancellationToken cancellationToken) {
var scaling = CreateScaling(Problem.ProblemData.Dataset, Problem.ProblemData.TrainingIndices.ToArray(), Problem.ProblemData.AllowedInputVariables.ToArray());
double[,] inputMatrix = ExtractData(Problem.ProblemData.Dataset, Problem.ProblemData.TrainingIndices, Problem.ProblemData.AllowedInputVariables.ToArray(), scaling);
double[,] target = ExtractData(Problem.ProblemData.Dataset, Problem.ProblemData.TrainingIndices, new string[] { Problem.ProblemData.TargetVariable });
inputMatrix = inputMatrix.HorzCat(target);
if (inputMatrix.Cast().Any(x => double.IsNaN(x) || double.IsInfinity(x)))
throw new NotSupportedException("Splines does not support NaN or infinity values in the input dataset.");
var inputVars = Problem.ProblemData.AllowedInputVariables.ToArray();
if (inputVars.Length > 3) throw new NotSupportedException();
alglib.rbfmodel model;
alglib.rbfreport rep;
alglib.rbfcreate(inputVars.Length, 1, out model);
alglib.rbfsetzeroterm(model);
var rbase = ((DoubleValue)Parameters["RBase"].ActualValue).Value;
var nlayers = ((IntValue)Parameters["NLayers"].ActualValue).Value;
var lambdans = ((DoubleValue)Parameters["LambdaNS"].ActualValue).Value;
alglib.rbfsetalgohierarchical(model, rbase, nlayers, lambdans);
alglib.rbfsetpoints(model, inputMatrix);
alglib.rbfbuildmodel(model, out rep);
Results.Add(new Result("TerminationType", new DoubleValue(rep.terminationtype)));
Results.Add(new Result("RMSE", new DoubleValue(rep.rmserror)));
Results.Add(new Result("Solution", new RegressionSolution(new RBFModel(model, Problem.ProblemData.TargetVariable, inputVars, scaling),
(IRegressionProblemData)Problem.ProblemData.Clone())));
}
private static ITransformation[] CreateScaling(IDataset dataset, int[] rows, IReadOnlyCollection allowedInputVariables) {
var trans = new ITransformation[allowedInputVariables.Count];
int i = 0;
foreach (var variable in allowedInputVariables) {
var lin = new LinearTransformation(allowedInputVariables);
var max = dataset.GetDoubleValues(variable, rows).Max();
var min = dataset.GetDoubleValues(variable, rows).Min();
lin.Multiplier = 1.0 / (max - min);
lin.Addend = -min / (max - min);
trans[i] = lin;
i++;
}
return trans;
}
private static double[,] ExtractData(IDataset dataset, IEnumerable rows, IReadOnlyCollection allowedInputVariables, ITransformation[] scaling = null) {
double[][] variables;
if (scaling != null) {
variables =
allowedInputVariables.Select((var, i) => scaling[i].Apply(dataset.GetDoubleValues(var, rows)).ToArray())
.ToArray();
} else {
variables =
allowedInputVariables.Select(var => dataset.GetDoubleValues(var, rows).ToArray()).ToArray();
}
int n = variables.First().Length;
var res = new double[n, variables.Length];
for (int r = 0; r < n; r++)
for (int c = 0; c < variables.Length; c++) {
res[r, c] = variables[c][r];
}
return res;
}
}
}
// UNFINISHED
public class RBFModel : NamedItem, IRegressionModel {
private alglib.rbfmodel model;
public string TargetVariable { get; set; }
public IEnumerable VariablesUsedForPrediction { get; private set; }
private ITransformation[] scaling;
public event EventHandler TargetVariableChanged;
public RBFModel(RBFModel orig, Cloner cloner) : base(orig, cloner) {
this.TargetVariable = orig.TargetVariable;
this.VariablesUsedForPrediction = orig.VariablesUsedForPrediction.ToArray();
this.model = (alglib.rbfmodel)orig.model.make_copy();
this.scaling = orig.scaling.Select(s => cloner.Clone(s)).ToArray();
}
public RBFModel(alglib.rbfmodel model, string targetVar, string[] inputs, IEnumerable> scaling) : base("RBFModel", "RBFModel") {
this.model = model;
this.TargetVariable = targetVar;
this.VariablesUsedForPrediction = inputs;
this.scaling = scaling.ToArray();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new RBFModel(this, cloner);
}
public IRegressionSolution CreateRegressionSolution(IRegressionProblemData problemData) {
return new RegressionSolution(this, (IRegressionProblemData)problemData.Clone());
}
public IEnumerable GetEstimatedValues(IDataset dataset, IEnumerable rows) {
double[] x = new double[VariablesUsedForPrediction.Count()];
double[] y;
foreach (var r in rows) {
int c = 0;
foreach (var v in VariablesUsedForPrediction) {
x[c] = scaling[c].Apply(dataset.GetDoubleValue(v, r).ToEnumerable()).First(); // OUCH!
c++;
}
alglib.rbfcalc(model, x, out y);
yield return y[0];
}
}
}