Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.VRPEnhancements/HeuristicLab.Problems.VehicleRouting/3.4/Encodings/Potvin/Creators/Cluster.cs @ 14442

Last change on this file since 14442 was 14442, checked in by jzenisek, 7 years ago

#2707 fixed min variance bug in time window based k-means clustering

File size: 7.7 KB
Line 
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using HeuristicLab.Core;
5using HeuristicLab.Problems.VehicleRouting.Interfaces;
6
7namespace HeuristicLab.Problems.VehicleRouting.Encodings.Potvin {
8
9  public class ClusterAlgorithm<C,CO> where C : Cluster<CO> where CO : ClusterElement {
10
11    public static List<C> KMeans(IRandom random, List<CO> clusterElements, int k, double changeThreshold) {
12      HashSet<int> initMeans = new HashSet<int>();
13      int nextMean = -1;
14
15      List<C> clusters = CreateCList();
16
17      // (1) initialize each cluster with a random element as mean
18      for (int i = 0; i < k && i < clusterElements.Count; i++) {
19        C cluster = (C)Activator.CreateInstance(typeof(C));
20        cluster.Id = i;
21
22        do {
23          nextMean = random.Next(0, clusterElements.Count);
24        } while (initMeans.Contains(nextMean));
25        initMeans.Add(nextMean);
26        cluster.SetMean(clusterElements[nextMean]);
27        clusters.Add(cluster);
28      }
29
30      // (2) repeat clustering until change rate is below threshold
31      double changeRate = 0.0;
32
33      do {
34        int changes = KMeansRun(clusters, clusterElements);
35        changeRate = (double)changes / clusterElements.Count;
36      } while (changeRate > changeThreshold);
37
38
39      // remove empty clusters
40      clusters.RemoveAll(c => c.Elements.Count.Equals(0));
41
42      return clusters;
43    }
44
45    private static int KMeansRun(List<C> clusters, List<CO> clusterElements) {
46      int changes = 0;
47
48      // clear clusters from previous assigned elements
49      foreach (var c in clusters) {
50        c.Elements.Clear();
51      }
52
53      // assign elements to currently most suitable clusters
54      foreach (var e in clusterElements) {
55        int optClusterIdx = 0;
56        double optImpact = clusters[optClusterIdx].CalculateImpact(e);
57        for (int i = 1; i < clusters.Count; i++) {
58          double impact = clusters[i].CalculateImpact(e);
59          if (impact < optImpact) {
60            optImpact = impact;
61            optClusterIdx = i;
62          }
63        }
64        if (clusters[optClusterIdx].AddObject(e)) {
65          changes++;
66        }
67      }
68
69      // update mean and variance
70      foreach (var c in clusters) {
71        c.CalculateMean();
72        c.CalculateVariance();
73      }
74
75      return changes;
76    }
77
78    private static List<C> CreateCList() {
79      var listType = typeof(List<>);
80      var constructedListType = listType.MakeGenericType(typeof(C));
81      return (List<C>)Activator.CreateInstance(constructedListType);
82    }
83  }
84
85  #region Cluster
86  public interface ICluster<T> where T : ClusterElement {
87    void SetMean(T o);
88    bool AddObject(T o);
89    void CalculateMean();
90    void CalculateVariance();
91    double CalculateImpact(T e);
92    double CalculateDistance(T e1, T e2);
93
94  }
95  public abstract class Cluster<T> : ICluster<T> where T : ClusterElement {
96    public int Id;
97
98    public List<T> Elements { get; private set; }
99
100    public T Mean { get; set; }
101
102    public double Variance { get; set; }
103
104    protected Cluster() {
105      Elements = new List<T>();
106    }
107
108    protected Cluster(int id) {
109      Id = id;
110      Elements = new List<T>();
111
112    }
113
114    public bool AddObject(T e) {
115      Elements.Add(e);
116
117      bool clusterChanged = e.ClusterId != Id;
118      e.ClusterId = Id;
119      return clusterChanged;
120    }
121
122    public void SetMean(T e) {
123      Mean = e;
124      Mean.ClusterId = Id;
125    }
126
127    public abstract void CalculateMean();
128
129    public abstract double CalculateDistance(T e1, T e2);
130
131    public virtual void CalculateVariance() {
132      if (Mean == null)
133        CalculateMean();
134
135      Variance = 0.0;
136      foreach (T e in Elements) {
137        Variance += Math.Pow(CalculateDistance(Mean, e), 2);
138      }
139      Variance /= Elements.Count;
140    }
141
142    public virtual double CalculateImpact(T e) {
143      if (Mean == null)
144        CalculateMean();
145
146      double newVariance = (Variance * Elements.Count + Math.Pow(CalculateDistance(Mean, e), 2)) / (Elements.Count + 1);
147      return newVariance - Variance;
148    }
149
150  }
151  public class SpatialDistanceCluster : Cluster<SpatialDistanceClusterElement> {
152    public SpatialDistanceCluster() : base() {
153    }
154
155    public SpatialDistanceCluster(int id) : base(id) {
156    }
157
158    public override void CalculateMean() {
159      int dimensions = (Mean != null) ? Mean.Coordinates.Length : (Elements.Count > 0) ? Elements[0].Coordinates.Length : 0;
160      SpatialDistanceClusterElement mean = new SpatialDistanceClusterElement(-1, dimensions, Id);
161      foreach (SpatialDistanceClusterElement e in Elements) {
162        for (int i = 0; i < Mean.Coordinates.Length; i++) {
163          mean.Coordinates[i] += (e.Coordinates[i] / Elements.Count);
164        }
165      }
166      Mean = mean;
167    }
168
169    public override double CalculateDistance(SpatialDistanceClusterElement e1, SpatialDistanceClusterElement e2) {
170      if (!e1.Coordinates.Length.Equals(e2.Coordinates.Length)) {
171        throw new ArgumentException("Distance could not be calculated since number of dimensions is unequal.");
172      }
173
174      double distance = 0.0;
175      for (int i = 0; i < e1.Coordinates.Length; i++) {
176        distance += Math.Pow(e1.Coordinates[i] - e2.Coordinates[i], 2);
177      }
178
179      return Math.Sqrt(distance);
180    }
181  }
182
183  public class TemporalDistanceCluster : Cluster<TemporalDistanceClusterElement> {
184    public TemporalDistanceCluster() : base() {
185    }
186
187    public TemporalDistanceCluster(int id) : base(id) {
188    }
189
190    public override void CalculateMean() {
191      TemporalDistanceClusterElement mean = new TemporalDistanceClusterElement(-1, Id);
192      foreach (TemporalDistanceClusterElement e in Elements) {
193        mean.ReadyTime += e.ReadyTime/Elements.Count;
194        mean.DueTime += e.DueTime/Elements.Count;
195      }
196      Mean = mean;
197    }
198
199    public override double CalculateDistance(TemporalDistanceClusterElement e1, TemporalDistanceClusterElement e2) {
200      double distance = 0.0;
201
202      distance += Math.Pow(e1.ReadyTime - e2.ReadyTime, 2);
203      distance += Math.Pow(e1.DueTime - e2.DueTime, 2);
204
205      return Math.Sqrt(distance);
206    }
207  }
208
209  #endregion Cluster
210
211  #region ClusterElement
212  public abstract class ClusterElement {
213    public int Id { get; set; }
214    public int ClusterId { get; set; }
215
216    protected ClusterElement() {
217      ClusterId = -1;
218    }
219
220    public ClusterElement(int id, int clusterId = -1) {
221      Id = id;
222      ClusterId = clusterId;
223    }
224  }
225  public class SpatialDistanceClusterElement : ClusterElement {
226
227    public double[] Coordinates { get; set; }
228
229    public SpatialDistanceClusterElement() : base() {
230    }
231
232    public SpatialDistanceClusterElement(int id, int clusterId = -1) : base(id, clusterId) {
233    }
234
235    public SpatialDistanceClusterElement(int id, double[] coordinates, int clusterId = -1) : base(id, clusterId) {
236      Coordinates = coordinates;
237    }
238
239    public SpatialDistanceClusterElement(int id, int dimensions, int clusterId = -1) : base(id, clusterId) {
240      Coordinates = new double[dimensions];
241    }
242
243  }
244
245  public class TemporalDistanceClusterElement : ClusterElement {
246    public double ReadyTime { get; set; }
247    public double DueTime { get; set; }
248
249    public TemporalDistanceClusterElement() : base() {
250    }
251
252    public TemporalDistanceClusterElement(int id, int clusterId = -1) : base(id, clusterId) {
253    }
254
255    public TemporalDistanceClusterElement(int id, double readyTime, double dueTime, int clusterId = -1) : base(id, clusterId) {
256      ReadyTime = readyTime;
257      DueTime = dueTime;
258    }
259  }
260
261  #endregion ClusterElement
262}
Note: See TracBrowser for help on using the repository browser.