/* * 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 . * * Adaptions to work with direct C# translation of the java libSVM source code (version 3.1.2) by Gabriel Kronberger * */ using System; using System.Globalization; using System.IO; using System.Linq; using System.Threading; namespace LibSVM { /// /// Class which encapsulates a range transformation. /// public class RangeTransform { /// /// 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(svm_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(svm_problem prob, double lowerBound, double upperBound) { // node indices must be ordered int maxIndex = prob.x.Select(arr => arr.Last().index).Max(); double[] minVals = new double[maxIndex]; double[] maxVals = new double[maxIndex]; int[] count = new int[maxIndex]; for (int i = 0; i < maxIndex; i++) { minVals[i] = double.MaxValue; maxVals[i] = double.MinValue; count[i] = 0; } for (int i = 0; i < prob.l; 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); count[index]++; } } for (int i = 0; i < maxIndex; i++) { if (count[i] == 0) { 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 svm_node[] Transform(svm_node[] input) { svm_node[] output = new svm_node[input.Length]; for (int i = 0; i < output.Length; i++) { int index = input[i].index; double value = input[i].value; output[i] = new svm_node() { index = index, value = 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 (Math.Abs(_inputScale[index]) < 1E-12) 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) { var savedCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; 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(); Thread.CurrentThread.CurrentCulture = savedCulture; } /// /// 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) { var savedCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; 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]); Thread.CurrentThread.CurrentCulture = savedCulture; return new RangeTransform(inputStart, inputScale, outputStart, outputScale, length); } } }