Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ScopedAlgorithms/HeuristicLab.Encodings.LinearLinkageEncoding/3.3/LinearLinkage.cs @ 14536

Last change on this file since 14536 was 13372, checked in by mkommend, 9 years ago

#2521: Fixed all problems.

File size: 10.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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.Common;
26using HeuristicLab.Core;
27using HeuristicLab.Data;
28using HeuristicLab.Optimization;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30
31namespace HeuristicLab.Encodings.LinearLinkageEncoding {
32  [Item("LinearLinkage", "Represents an LLE grouping of items.")]
33  [StorableClass]
34  public sealed class LinearLinkage : IntArray, ISolution {
35
36    [StorableConstructor]
37    private LinearLinkage(bool deserializing) : base(deserializing) { }
38    private LinearLinkage(LinearLinkage original, Cloner cloner) : base(original, cloner) { }
39    public LinearLinkage() { }
40    public LinearLinkage(int length) : base(length) { }
41    public LinearLinkage(int[] elements) : base(elements) { }
42
43    public override IDeepCloneable Clone(Cloner cloner) {
44      return new LinearLinkage(this, cloner);
45    }
46
47    /// <summary>
48    /// This method parses the encoded array and calculates the membership of
49    /// each element to the groups. It starts at the lowest element.
50    /// </summary>
51    /// <remarks>
52    /// Runtime complexity of this method is O(n) where n is the length of the
53    /// array.
54    /// </remarks>
55    /// <returns>An enumeration of all groups.</returns>
56    public IEnumerable<List<int>> GetGroups() {
57      var len = array.Length;
58      var remaining = new HashSet<int>(Enumerable.Range(0, len));
59      // iterate from lowest to highest index
60      for (var i = 0; i < len; i++) {
61        if (!remaining.Contains(i)) continue;
62        var group = new List<int> { i };
63        remaining.Remove(i);
64        var next = array[i];
65        if (next != i) {
66          int prev;
67          do {
68            group.Add(next);
69            if (!remaining.Remove(next))
70              throw new ArgumentException("Array is malformed and does not represent a valid LLE forward encoding.");
71            prev = next;
72            next = array[next];
73          } while (next != prev);
74        }
75        yield return group;
76      }
77    }
78
79    /// <summary>
80    /// This method parses the encoded array and gathers all elements
81    /// that belong to the same group as element <paramref name="index"/>.
82    /// </summary>
83    /// <param name="index">The element whose group should be returned.
84    /// </param>
85    /// <returns>The element at <paramref name="index"/> and all other
86    /// elements in the same group.</returns>
87    public IEnumerable<int> GetGroup(int index) {
88      foreach (var n in GetGroupForward(index))
89        yield return n;
90      // the element index has already been yielded
91      foreach (var n in GetGroupBackward(index).Skip(1))
92        yield return n;
93    }
94
95    /// <summary>
96    /// This method parses the encoded array and gathers the element
97    /// <paramref name="index"/> as well as subsequent elements that
98    /// belong to the same group.
99    /// </summary>
100    /// <param name="index">The element from which subsequent (having a
101    /// larger number) elements in the group should be returned.
102    /// </param>
103    /// <returns>The element <paramref name="index"/> and all subsequent
104    /// elements in the same group.</returns>
105    public IEnumerable<int> GetGroupForward(int index) {
106      yield return index;
107      var next = array[index];
108      if (next == index) yield break;
109      int prev;
110      do {
111        yield return next;
112        prev = next;
113        next = array[next];
114      } while (next != prev);
115    }
116
117    /// <summary>
118    /// This method parses the encoded array and gathers the element
119    /// given <paramref name="index"/> as well as preceeding elements that
120    /// belong to the same group.
121    /// </summary>
122    /// <remarks>
123    /// Warning, this code has performance O(index) as the array has to
124    /// be fully traversed backwards from the given index.
125    /// </remarks>
126    /// <param name="index">The element from which preceeding (having a
127    /// smaller number) elements in the group should be returned.
128    /// </param>
129    /// <returns>The element <paramref name="index"/> and all preceeding
130    /// elements in the same group.</returns>
131    public IEnumerable<int> GetGroupBackward(int index) {
132      yield return index;
133      var next = array[index];
134      // return preceding elements in group
135      for (var prev = index - 1; prev >= 0; prev--) {
136        if (array[prev] != next) continue;
137        next = prev;
138        yield return next;
139      }
140    }
141
142    /// <summary>
143    /// This method translates an enumeration of groups into the underlying
144    /// array representation.
145    /// </summary>
146    /// <remarks>
147    /// Throws an ArgumentException when there is an element assigned to
148    /// multiple groups or elements that are not assigned to any group.
149    /// </remarks>
150    /// <param name="grouping">The grouping of the elements, each element must
151    /// be part of exactly one group.</param>
152    public void SetGroups(IEnumerable<IEnumerable<int>> grouping) {
153      var len = array.Length;
154      var remaining = new HashSet<int>(Enumerable.Range(0, len));
155      foreach (var group in grouping) {
156        var prev = -1;
157        foreach (var g in group.OrderBy(x => x)) {
158          if (prev >= 0) array[prev] = g;
159          prev = g;
160          if (!remaining.Remove(prev))
161            throw new ArgumentException(string.Format("Element {0} is contained at least twice.", prev), "grouping");
162        }
163        if (prev >= 0) array[prev] = prev;
164      }
165      if (remaining.Count > 0)
166        throw new ArgumentException(string.Format("Elements are not assigned a group: {0}", string.Join(", ", remaining)));
167    }
168
169    /// <summary>
170    /// Performs a check whether the array represents a valid LLE encoding.
171    /// </summary>
172    /// <remarks>
173    /// The runtime complexity of this method is O(n) where n is the length of
174    /// the array.
175    /// </remarks>
176    /// <returns>True if the encoding is valid.</returns>
177    public bool Validate() {
178      var len = array.Length;
179      var remaining = new HashSet<int>(Enumerable.Range(0, len));
180      for (var i = 0; i < len; i++) {
181        if (!remaining.Contains(i)) continue;
182        remaining.Remove(i);
183        var next = array[i];
184        if (next == i) continue;
185        int prev;
186        do {
187          if (!remaining.Remove(next)) return false;
188          prev = next;
189          next = array[next];
190        } while (next != prev);
191      }
192      return remaining.Count == 0;
193    }
194
195    /// <summary>
196    /// This method flattens tree structures that may be present in groups.
197    /// These tree structures may be created by e.g. merging two groups by
198    /// linking one end node to the end node of another.
199    /// Consider following 1-based index array: 6, 6, 7, 5, 5, 8, 8, 8, 9.
200    /// This results in the following tree structure for group 8:
201    ///      8
202    ///    /   \
203    ///   6     7
204    ///  / \    |
205    /// 1   2   3
206    /// After this operation the array will be 2, 3, 6, 5, 5, 7, 8, 8, 9.
207    /// Representing a tree with one branch: 1 -> 2 -> 3 -> 6 -> 7 -> 8
208    /// </summary>
209    /// <remarks>
210    /// The method first converts the array to LLE-e format and then
211    /// linearizes the links. This requires two passes of the whole array
212    /// as well as a dictionary to hold the smallest index of each group.
213    /// The runtime complexity is O(n).
214    ///
215    /// The method assumes that there are no back links present.
216    /// </remarks>
217    public void LinearizeTreeStructures() {
218      // Step 1: Convert the array into LLE-e
219      ToLLEeInplace(array);
220      // Step 2: For all groups linearize the links
221      FromLLEe(array);
222    }
223
224    /// <summary>
225    /// Creates a copy of the underlying array and turns it into LLE-e.
226    /// </summary>
227    /// <remarks>
228    /// LLE-e is a special format where each element points to the
229    /// ending item of a group.
230    /// The LLE representation 2, 3, 5, 6, 5, 7, 8, 8 would become
231    /// 5, 5, 5, 8, 5, 8, 8, 8 in LLE-e.
232    ///
233    /// This operation runs in O(n) time.
234    /// </remarks>
235    /// <returns>An integer array in LLE-e representation</returns>
236    public int[] ToLLEe() {
237      var result = (int[])array.Clone();
238      ToLLEeInplace(result);
239      return result;
240    }
241
242    private void ToLLEeInplace(int[] a) {
243      var length = a.Length;
244      for (var i = length - 1; i >= 0; i--) {
245        if (array[i] == i) a[i] = i;
246        else a[i] = a[a[i]];
247      }
248    }
249
250    /// <summary>
251    /// Parses an LLE-e representation and modifies the underlying array
252    /// so that it is in LLE representation.
253    /// </summary>
254    /// <remarks>
255    /// This operation runs in O(n) time, but requires additional memory
256    /// in form of a dictionary.
257    /// </remarks>
258    /// <param name="llee">The LLE-e representation</param>
259    public void FromLLEe(int[] llee) {
260      var length = array.Length;
261      var groups = new Dictionary<int, int>();
262      for (var i = length - 1; i >= 0; i--) {
263        if (llee[i] == i) {
264          array[i] = i;
265          groups[i] = i;
266        } else {
267          var g = llee[i];
268          array[i] = groups[g];
269          groups[g] = i;
270        }
271      }
272    }
273  }
274}
Note: See TracBrowser for help on using the repository browser.