Free cookie consent management tool by TermsFeed Policy Generator

source: branches/GeneralizedQAP/HeuristicLab.Problems.GeneralizedQuadraticAssignment.Common/3.3/ExtensionMethods.cs @ 7807

Last change on this file since 7807 was 7807, checked in by abeham, 12 years ago

#1614, #1848

  • updated extension methods and operators that use them
File size: 16.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using HeuristicLab.Core;
26
27namespace HeuristicLab.Problems.GeneralizedQuadraticAssignment.Common {
28  public static class ExtensionMethods {
29
30    #region Choosing from a sequence
31    /// <summary>
32    /// Chooses one elements from a sequence giving each element an equal chance.
33    /// </summary>
34    /// <remarks>
35    /// Runtime complexity is O(1) for sequences that are of type <see cref="IList{T}"/> and
36    /// O(N) for all other.
37    /// </remarks>
38    /// <exception cref="ArgumentException">If the sequence is empty.</exception>
39    /// <typeparam name="T">The type of the items to be selected.</typeparam>
40    /// <param name="source">The sequence of elements.</param>
41    /// <param name="random">The random number generator to use, its NextDouble() method must produce values in the range [0;1)</param>
42    /// <param name="count">The number of items to be selected.</param>
43    /// <returns>An element that has been chosen randomly from the sequence.</returns>
44    public static T SampleRandom<T>(this IEnumerable<T> source, IRandom random) {
45      if (!source.Any()) throw new ArgumentException("sequence is empty.", "source");
46      return source.SampleRandom(random, 1).First();
47    }
48    /// <summary>
49    /// Chooses <paramref name="count"/> elements from a sequence with repetition with equal chances for each element.
50    /// </summary>
51    /// <remarks>
52    /// Runtime complexity is O(count) for sequences that are <see cref="IList{T}"/> and
53    /// O(N * count) for all other. No exception is thrown if the sequence is empty.
54    ///
55    /// The method is online.
56    /// </remarks>
57    /// <typeparam name="T">The type of the items to be selected.</typeparam>
58    /// <param name="source">The sequence of elements.</param>
59    /// <param name="random">The random number generator to use, its NextDouble() method must produce values in the range [0;1)</param>
60    /// <param name="count">The number of items to be selected.</param>
61    /// <returns>A sequence of elements that have been chosen randomly.</returns>
62    public static IEnumerable<T> SampleRandom<T>(this IEnumerable<T> source, IRandom random, int count) {
63      var listSource = source as IList<T>;
64      if (listSource != null) {
65        while (count > 0) {
66          yield return listSource[random.Next(listSource.Count)];
67          count--;
68        }
69      } else {
70        while (count > 0) {
71          var enumerator = source.GetEnumerator();
72          enumerator.MoveNext();
73          T selectedItem = enumerator.Current;
74          int counter = 1;
75          while (enumerator.MoveNext()) {
76            counter++;
77            if (counter * random.NextDouble() < 1.0)
78              selectedItem = enumerator.Current;
79          }
80          yield return selectedItem;
81          count--;
82        }
83      }
84    }
85    /// <summary>
86    /// Chooses <paramref name="count"/> elements from a sequence without repetition with equal chances for each element.
87    /// The items are returned in the same order as they appear in the sequence.
88    /// </summary>
89    /// <remarks>
90    /// Runtime complexity is O(N) for all sequences.
91    /// No exception is thrown if the sequence contains less items than there are to be selected.
92    ///
93    /// The method is online.
94    /// </remarks>
95    /// <typeparam name="T">The type of the items to be selected.</typeparam>
96    /// <param name="source">The sequence of elements.</param>
97    /// <param name="random">The random number generator to use, its NextDouble() method must produce values in the range [0;1)</param>
98    /// <param name="count">The number of items to be selected.</param>
99    /// <returns>A sequence of elements that have been chosen randomly.</returns>
100    public static IEnumerable<T> SampleRandomWithoutRepetition<T>(this IEnumerable<T> source, IRandom random, int count) {
101      int remaining = source.Count();
102      foreach (var item in source) {
103        if (random.NextDouble() * remaining < count) {
104          count--;
105          yield return item;
106          if (count <= 0) break;
107        }
108        remaining--;
109      }
110    }
111
112    /// <summary>
113    /// Chooses elements out of a sequence with repetition. The chance that an item is selected is proportional or inverse-proportional
114    /// to the <paramref name="weights"/>.
115    /// </summary>
116    /// <remarks>
117    /// In case both <paramref name="inverseProportional"/> and <paramref name="windowing"/> are false values must be &gt; 0,
118    /// otherwise an InvalidOperationException is thrown.
119    ///
120    /// The method internally holds two arrays: One that is the sequence itself and another one for the values.
121    /// </remarks>
122    /// <typeparam name="T">The type of the items to be selected.</typeparam>
123    /// <param name="source">The sequence of elements.</param>
124    /// <param name="random">The random number generator to use, its NextDouble() method must produce values in the range [0;1)</param>
125    /// <param name="count">The number of items to be selected.</param>
126    /// <param name="weights">The weight values for the items.</param>
127    /// <param name="windowing">Whether to scale the proportional values or not.</param>
128    /// <param name="inverseProportional">Determines whether to choose proportionally (false) or inverse-proportionally (true).</param>
129    /// <returns>A sequence of selected items. The sequence might contain the same item more than once.</returns>
130    public static IEnumerable<T> SampleProportional<T>(this IEnumerable<T> source, IRandom random, int count, IEnumerable<double> weights, bool windowing = true, bool inverseProportional = false) {
131      return source.SampleProportional(random, weights, windowing, inverseProportional).Take(count);
132    }
133    /// <summary>
134    /// Same as <seealso cref="SampleProportional<T>"/>, but chooses an item exactly once.
135    /// </summary>
136    /// <remarks>
137    /// In case both <paramref name="inverseProportional"/> and <paramref name="windowing"/> are false values must be &gt; 0,
138    /// otherwise an InvalidOperationException is thrown.
139    ///
140    /// The method internally holds two arrays: One that is the sequence itself and another one for the values.
141    /// </remarks>
142    /// <typeparam name="T">The type of the items to be selected.</typeparam>
143    /// <param name="source">The sequence of elements.</param>
144    /// <param name="random">The random number generator to use, its NextDouble() method must produce values in the range [0;1)</param>
145    /// <param name="count">The number of items to be selected.</param>
146    /// <param name="weights">The weight values for the items.</param>
147    /// <param name="windowing">Whether to scale the proportional values or not.</param>
148    /// <param name="maximization">Determines whether to choose proportionally (true) or inverse-proportionally (false).</param>
149    /// <returns>A sequence of selected items.</returns>
150    public static IEnumerable<T> SampleProportionalWithoutRepetition<T>(this IEnumerable<T> source, IRandom random, int count, IEnumerable<double> weights, bool windowing = true, bool inverseProportional = false) {
151      return source.SampleProportionalWithoutRepetition(random, weights, windowing, inverseProportional).Take(count);
152    }
153    #region Proportional Helpers
154    private static IEnumerable<T> SampleProportional<T>(this IEnumerable<T> source, IRandom random, IEnumerable<double> weights, bool windowing, bool inverseProportional) {
155      var sourceArray = source.ToArray();
156      var valueArray = PrepareProportional<T>(sourceArray, weights, windowing, inverseProportional);
157      double total = valueArray.Sum();
158
159      while (true) {
160        int index = 0;
161        double ball = valueArray[index], sum = random.NextDouble() * total;
162        while (ball < sum)
163          ball += valueArray[++index];
164        yield return sourceArray[index];
165      }
166    }
167    private static IEnumerable<T> SampleProportionalWithoutRepetition<T>(this IEnumerable<T> source, IRandom random, IEnumerable<double> weights, bool windowing, bool inverseProportional) {
168      var sourceArray = source.ToArray();
169      var valueArray = PrepareProportional<T>(sourceArray, weights, windowing, inverseProportional);
170      double total = valueArray.Sum();
171
172      HashSet<int> chosenIndices = new HashSet<int>();
173      while (chosenIndices.Count < sourceArray.Length) {
174        int index = 0;
175        double ball = valueArray[index], sum = random.NextDouble() * total;
176        while (ball < sum) {
177          index++;
178          if (!chosenIndices.Contains(index))
179            ball += valueArray[++index];
180        }
181        yield return sourceArray[index];
182        chosenIndices.Add(index);
183        total -= valueArray[index];
184      }
185    }
186    private static double[] PrepareProportional<T>(IList<T> sourceArray, IEnumerable<double> weights, bool windowing, bool inverseProportional) {
187      double maxValue = double.MinValue, minValue = double.MaxValue;
188      double[] valueArray = new double[sourceArray.Count];
189
190      var weightsEnum = weights.GetEnumerator();
191      for (int i = 0; i < sourceArray.Count && weightsEnum.MoveNext(); i++) {
192        valueArray[i] = weightsEnum.Current;
193        if (valueArray[i] > maxValue) maxValue = valueArray[i];
194        if (valueArray[i] < minValue) minValue = valueArray[i];
195      }
196      if (minValue == maxValue) {  // all values are equal
197        for (int i = 0; i < sourceArray.Count; i++) {
198          valueArray[i] = 1.0;
199        }
200      } else {
201        if (windowing) {
202          if (inverseProportional) InverseProportionalScale(valueArray, minValue);
203          else ProportionalScale(valueArray, maxValue);
204        } else {
205          if (minValue < 0.0) throw new InvalidOperationException("Proportional selection without windowing does not work with values < 0.");
206          if (inverseProportional) InverseProportionalScale(valueArray, 2 * maxValue);
207        }
208      }
209      return valueArray;
210    }
211    private static void ProportionalScale(double[] values, double minValue) {
212      for (int i = 0; i < values.Length; i++) {
213        values[i] = values[i] - minValue;
214      }
215    }
216    private static void InverseProportionalScale(double[] values, double maxValue) {
217      for (int i = 0; i < values.Length; i++) {
218        values[i] = maxValue - values[i];
219      }
220    }
221    #endregion
222
223    /// <summary>
224    /// Selects all elements in the sequence that are maximal with respect to the given value.
225    /// </summary>
226    /// <remarks>
227    /// Runtime complexity of the operation is O(N).
228    /// </remarks>
229    /// <typeparam name="T">The type of the elements.</typeparam>
230    /// <param name="source">The enumeration in which the items with a maximal value should be found.</param>
231    /// <param name="valueSelector">The function that selects the value to compare.</param>
232    /// <returns>All elements in the enumeration where the selected value is the maximum.</returns>
233    public static IEnumerable<T> MaxItems<T>(this IEnumerable<T> source, Func<T, IComparable> valueSelector) {
234      IEnumerator<T> enumerator = source.GetEnumerator();
235      if (!enumerator.MoveNext()) return Enumerable.Empty<T>();
236      IComparable max = valueSelector(enumerator.Current);
237      var result = new List<T>();
238      result.Add(enumerator.Current);
239
240      while (enumerator.MoveNext()) {
241        T item = enumerator.Current;
242        IComparable comparison = valueSelector(item);
243        if (comparison.CompareTo(max) > 0) {
244          result.Clear();
245          result.Add(item);
246          max = comparison;
247        } else if (comparison.CompareTo(max) == 0) {
248          result.Add(item);
249        }
250      }
251      return result;
252    }
253
254    /// <summary>
255    /// Selects all elements in the sequence that are minimal with respect to the given value.
256    /// </summary>
257    /// <remarks>
258    /// Runtime complexity of the operation is O(N).
259    /// </remarks>
260    /// <typeparam name="T">The type of the elements.</typeparam>
261    /// <param name="source">The enumeration in which items with a minimal value should be found.</param>
262    /// <param name="valueSelector">The function that selects the value.</param>
263    /// <returns>All elements in the enumeration where the selected value is the minimum.</returns>
264    public static IEnumerable<T> MinItems<T>(this IEnumerable<T> source, Func<T, IComparable> valueSelector) {
265      IEnumerator<T> enumerator = source.GetEnumerator();
266      if (!enumerator.MoveNext()) return Enumerable.Empty<T>();
267      IComparable min = valueSelector(enumerator.Current);
268      var result = new List<T>();
269      result.Add(enumerator.Current);
270
271      while (enumerator.MoveNext()) {
272        T item = enumerator.Current;
273        IComparable comparison = valueSelector(item);
274        if (comparison.CompareTo(min) < 0) {
275          result.Clear();
276          result.Add(item);
277          min = comparison;
278        } else if (comparison.CompareTo(min) == 0) {
279          result.Add(item);
280        }
281      }
282      return result;
283    }
284    #endregion
285
286    #region Shuffling
287    /// <summary>
288    /// Shuffles an enumerable and returns a new enumerable according to the Fisher-Yates shuffle.
289    /// </summary>
290    /// <remarks>
291    /// Note that the source enumerable is transformed into an array.
292    ///
293    /// The implementation is described in http://stackoverflow.com/questions/1287567/c-is-using-random-and-orderby-a-good-shuffle-algorithm.
294    /// </remarks>
295    /// <typeparam name="T">The type of the items that are to be shuffled.</typeparam>
296    /// <param name="source">The enumerable that contains the items.</param>
297    /// <param name="random">The random number generator, its Next(n) method must deliver uniformly distributed random numbers in the range [0;n).</param>
298    /// <returns>An enumerable with the elements shuffled.</returns>
299    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, IRandom random) {
300      T[] elements = source.ToArray();
301      for (int i = elements.Length - 1; i > 0; i--) {
302        // Swap element "i" with a random earlier element (including itself)
303        int swapIndex = random.Next(i + 1);
304        yield return elements[swapIndex];
305        elements[swapIndex] = elements[i];
306        // we don't actually perform the swap, we can forget about the
307        // swapped element because we already returned it.
308      }
309      yield return elements[0];
310    }
311    #endregion
312
313    #region Graph walk
314    /// <summary>
315    /// Walks an operator graph in that it jumps from one operator to all its operator parameters and yields each operator it touches.
316    /// Cycles are detected and not walked twice.
317    /// </summary>
318    /// <param name="initial">The operator where the walk starts (is also yielded).</param>
319    /// <returns>An enumeration of all the operators that could be found.</returns>
320    public static IEnumerable<IOperator> Walk(this IOperator initial) {
321      var open = new Stack<IOperator>();
322      var visited = new HashSet<IOperator>();
323      open.Push(initial);
324
325      while (open.Any()) {
326        IOperator current = open.Pop();
327        if (visited.Contains(current)) continue;
328        visited.Add(current);
329
330        foreach (var parameter in current.Parameters.OfType<IValueParameter>()) {
331          if (typeof(IOperator).IsAssignableFrom(parameter.DataType)) {
332            if (parameter.Value != null)
333              open.Push((IOperator)parameter.Value);
334          }
335        }
336
337        yield return current;
338      }
339    }
340    #endregion
341
342    #region Matrix operations
343    /// <summary>
344    /// Returns a transposed matrix of the given <paramref name="matrix"/>.
345    /// </summary>
346    /// <typeparam name="T">The type of the matrix</typeparam>
347    /// <param name="matrix">The matrix that should be transposed.</param>
348    /// <returns>The transposed matrix.</returns>
349    public static T[,] Transpose<T>(this T[,] matrix) {
350      var result = new T[matrix.GetLength(1), matrix.GetLength(0)];
351      for (int i = 0; i < matrix.GetLength(0); i++)
352        for (int j = 0; j < matrix.GetLength(1); j++)
353          result[j, i] = matrix[i, j];
354      return result;
355    }
356    #endregion
357  }
358}
Note: See TracBrowser for help on using the repository browser.