/* * SVM.NET Library * Copyright (C) 2008 Matthew Johnson * * This program 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. * * This program 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 this program. If not, see . */ using System; using System.IO; using System.Threading; using System.Globalization; namespace SVM { /// /// Class which encapsulates a range transformation. /// public class RangeTransform : IRangeTransform { /// /// Default lower bound for scaling (-1). /// public const int DEFAULT_LOWER_BOUND = -1; /// /// Default upper bound for scaling (1). /// public const int DEFAULT_UPPER_BOUND = 1; /// /// Determines the Range transform for the provided problem. Uses the default lower and upper bounds. /// /// The Problem to analyze /// The Range transform for the problem public static RangeTransform Compute(Problem prob) { return Compute(prob, DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); } /// /// Determines the Range transform for the provided problem. /// /// The Problem to analyze /// The lower bound for scaling /// The upper bound for scaling /// The Range transform for the problem public static RangeTransform Compute(Problem prob, double lowerBound, double upperBound) { double[] minVals = new double[prob.MaxIndex]; double[] maxVals = new double[prob.MaxIndex]; for (int i = 0; i < prob.MaxIndex; i++) { minVals[i] = double.MaxValue; maxVals[i] = double.MinValue; } for (int i = 0; i < prob.Count; i++) { for (int j = 0; j < prob.X[i].Length; j++) { int index = prob.X[i][j].Index - 1; double value = prob.X[i][j].Value; minVals[index] = Math.Min(minVals[index], value); maxVals[index] = Math.Max(maxVals[index], value); } } for (int i = 0; i < prob.MaxIndex; i++) { if (minVals[i] == double.MaxValue || maxVals[i] == double.MinValue) { minVals[i] = 0; maxVals[i] = 0; } } return new RangeTransform(minVals, maxVals, lowerBound, upperBound); } private double[] _inputStart; private double[] _inputScale; private double _outputStart; private double _outputScale; private int _length; /// /// Constructor. /// /// The minimum values in each dimension. /// The maximum values in each dimension. /// The desired lower bound for all dimensions. /// The desired upper bound for all dimensions. public RangeTransform(double[] minValues, double[] maxValues, double lowerBound, double upperBound) { _length = minValues.Length; if (maxValues.Length != _length) throw new Exception("Number of max and min values must be equal."); _inputStart = new double[_length]; _inputScale = new double[_length]; for (int i = 0; i < _length; i++) { _inputStart[i] = minValues[i]; _inputScale[i] = maxValues[i] - minValues[i]; } _outputStart = lowerBound; _outputScale = upperBound - lowerBound; } private RangeTransform(double[] inputStart, double[] inputScale, double outputStart, double outputScale, int length) { _inputStart = inputStart; _inputScale = inputScale; _outputStart = outputStart; _outputScale = outputScale; _length = length; } /// /// Transforms the input array based upon the values provided. /// /// The input array /// A scaled array public Node[] Transform(Node[] input) { Node[] output = new Node[input.Length]; for (int i = 0; i < output.Length; i++) { int index = input[i].Index; double value = input[i].Value; output[i] = new Node(index, Transform(value, index)); } return output; } /// /// Transforms this an input value using the scaling transform for the provided dimension. /// /// The input value to transform /// The dimension whose scaling transform should be used /// The scaled value public double Transform(double input, int index) { index--; double tmp = input - _inputStart[index]; if (_inputScale[index] == 0) return 0; tmp /= _inputScale[index]; tmp *= _outputScale; return tmp + _outputStart; } /// /// Writes this Range transform to a stream. /// /// The stream to write to /// The range to write public static void Write(Stream stream, RangeTransform r) { TemporaryCulture.Start(); StreamWriter output = new StreamWriter(stream); output.WriteLine(r._length); output.Write(r._inputStart[0].ToString("r")); for (int i = 1; i < r._inputStart.Length; i++) output.Write(" " + r._inputStart[i].ToString("r")); output.WriteLine(); output.Write(r._inputScale[0].ToString("r")); for (int i = 1; i < r._inputScale.Length; i++) output.Write(" " + r._inputScale[i].ToString("r")); output.WriteLine(); output.WriteLine("{0} {1}", r._outputStart.ToString("r"), r._outputScale.ToString("r")); output.Flush(); TemporaryCulture.Stop(); } /// /// Writes this Range transform to a file. This will overwrite any previous data in the file. /// /// The file to write to /// The Range to write public static void Write(string outputFile, RangeTransform r) { FileStream s = File.Open(outputFile, FileMode.Create); try { Write(s, r); } finally { s.Close(); } } /// /// Reads a Range transform from a file. /// /// The file to read from /// The Range transform public static RangeTransform Read(string inputFile) { FileStream s = File.OpenRead(inputFile); try { return Read(s); } finally { s.Close(); } } /// /// Reads a Range transform from a stream. /// /// The stream to read from /// The Range transform public static RangeTransform Read(Stream stream) { return Read(new StreamReader(stream)); } public static RangeTransform Read(TextReader input) { TemporaryCulture.Start(); int length = int.Parse(input.ReadLine()); double[] inputStart = new double[length]; double[] inputScale = new double[length]; string[] parts = input.ReadLine().Split(); for (int i = 0; i < length; i++) inputStart[i] = double.Parse(parts[i]); parts = input.ReadLine().Split(); for (int i = 0; i < length; i++) inputScale[i] = double.Parse(parts[i]); parts = input.ReadLine().Split(); double outputStart = double.Parse(parts[0]); double outputScale = double.Parse(parts[1]); TemporaryCulture.Stop(); return new RangeTransform(inputStart, inputScale, outputStart, outputScale, length); } } }