// For almost all methods I should do checks for rank (length), but let's assume everything passed in is correct. This will expedite the program. public static bool IsCompletelySeparatedWithDiscriminant <L>(List <LabeledVector <L> > v1, List <LabeledVector <L> > v2, out DiscriminantEx <L> discriminant) { // Direct calculation: Sw^-1 * (Mean1 - Mean2) double[] m1 = v1.Select(v => v.Features).SpatialMean(); double[] m2 = v2.Select(v => v.Features).SpatialMean(); double[][] s1 = v1.Select(v => v.Features).ToArray().Covariance(); double[][] s2 = v2.Select(v => v.Features).ToArray().Covariance(); if (s1.All(r => r.All(c => double.IsNaN(c)))) { foreach (double[] r in s1) { for (int i = 0; i < r.Length; i++) { r[i] = LDAEx.RND_GENERATOR.Next(10) * LDAEx.COVARIANCE_NAN_REPLACEMENT_MUTLIPLIER; } } } if (s2.All(r => r.All(c => double.IsNaN(c)))) { foreach (double[] r in s2) { for (int i = 0; i < r.Length; i++) { r[i] = LDAEx.RND_GENERATOR.Next(10) * LDAEx.COVARIANCE_NAN_REPLACEMENT_MUTLIPLIER; } } } double[][] sw = s1.Add(s2); double[][] sw_inv = Accord.Math.Matrix.Inverse(sw); double[] w = Accord.Math.Matrix.Dot(sw_inv, m1.Subtract(m2)); w = Accord.Math.Matrix.Normalize(w); // Normalize it -- this step is ESSENTIAL double v1_min = double.MaxValue; double v1_max = double.MinValue; foreach (LabeledVector <L> v in v1) { double proj = Accord.Math.Matrix.Dot(w, v.Features); if (proj < v1_min) { v1_min = proj; } if (proj > v1_max) { v1_max = proj; } } double v2_min = double.MaxValue; double v2_max = double.MinValue; foreach (LabeledVector <L> v in v2) { double proj = Accord.Math.Matrix.Dot(w, v.Features); if (proj < v2_min) { v2_min = proj; } if (proj > v2_max) { v2_max = proj; } } if (v2_max < v1_min) { // Data set 1 is RIGHT of the projected SET mean // Data set 2 is LEFT of the projected SET mean double[] m = v1.Select(v => v.Features).Concat(v2.Select(v => v.Features)).SpatialMean(); discriminant = new DiscriminantEx <L>(w, v2.First().Label, v1.First().Label, Accord.Math.Matrix.Dot(w, m), Accord.Math.Matrix.Dot(w, m2), Accord.Math.Matrix.Dot(w, m1)); return(true); } else if (v1_max < v2_min) { // Data set 1 is LEFT of the projected SET mean // Data set 2 is RIGHT of the projected SET mean double[] m = v1.Select(v => v.Features).Concat(v2.Select(v => v.Features)).SpatialMean(); discriminant = new DiscriminantEx <L>(w, v1.First().Label, v2.First().Label, Accord.Math.Matrix.Dot(w, m), Accord.Math.Matrix.Dot(w, m1), Accord.Math.Matrix.Dot(w, m2)); return(true); } else { discriminant = null; return(false); } }
public int Spawn(LabeledVector <L> lvector, DistanceDelegate measure, ParallelStrategy parallelStrategy, bool spawnUsingLDA) { int nSpawnCount = 0; if (this.DoesEncloseVector(lvector, measure)) { if (parallelStrategy == ParallelStrategy.Multithreaded) { List <Task> lstSpawnThreads = new List <Task>(); foreach (SphereEx <L> child in this.Children) { lstSpawnThreads.Add(Task.Factory.StartNew(c => { nSpawnCount += ((SphereEx <L>)c).Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); }, child, TaskCreationOptions.LongRunning)); } Task.WaitAll(lstSpawnThreads.ToArray()); } else { foreach (SphereEx <L> child in this.Children) { nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); } } if (!spawnUsingLDA) { #region Regular Spawn if (!this.Label.Equals(lvector.Label) && !Vector.EqualsEx(this, lvector) && !this.DoesAtLeastOneChildEncloseVector(lvector, measure)) { this.AddChild(new SphereEx <L>(this.Radius - measure(this, lvector), lvector)); nSpawnCount++; } #endregion } else { #region LDA Spawn if (!this.DoesAtLeastOneChildEncloseVector(lvector, measure)) // Don't care about label as well as location of lvector { bool bContains = false; List <LabeledVector <L> > lst; if (!this.LDAVectors.TryGetValue(lvector.Label, out lst)) { this.LDAVectors.Add(lvector.Label, (lst = new List <LabeledVector <L> >())); } else { bContains = lst.Any(v => Vector.EqualsEx(v, lvector)); } if (!bContains) { lst.Add(lvector); if (this.LDAVectors.Keys.Count == 2 && !this.Children.Any()) { #region If the sphere contains exactly 2 classes, do the following... // 1. Create another discriminant // 2. If the discrimant CANNOT separate the classes, do the following... // 2a. Remove the lvector from the list it was just added to // 2b. Create new children from the classes // 2c. Try to spawn the presented LVector in the NEW children // 2d. Clear the dictionary, LDAVectors // 2e. If you can't spawn WITHIN the NEW children, add the vector to the this.LDAVectors // 3. Else (If the discrimant CAN separate the classes), do nothing but assign the property. bool bIsSeparable = false; DiscriminantEx <L> discriminant = null; try { //bIsSeparable = LDA.IsCompletelySeparatedWithDiscriminant(this.LDAVectors.ElementAt(0).Value, this.LDAVectors.ElementAt(1).Value, this, out discriminant); bIsSeparable = LDAEx.IsCompletelySeparatedWithDiscriminant(this.LDAVectors.ElementAt(0).Value, this.LDAVectors.ElementAt(1).Value, out discriminant); } catch { // Just consume, leaving bIsSeparable = false } if (!bIsSeparable) { lst.RemoveAt(lst.Count - 1); // Faster than .Remove() as I am not linearly searching List <SphereEx <L> > lstNewChildren = new List <SphereEx <L> >(); foreach (KeyValuePair <L, List <LabeledVector <L> > > kvp in this.LDAVectors) { if (kvp.Value.Any() && !kvp.Key.Equals(this.Label)) { //Vector vectorCentroid = Vector.Centroid(kvp.Value); //if (!Vector.EqualsEx(this, vectorCentroid)) //{ // SphereEx<L> child = new SphereEx<L>(this.Radius - measure(this, vectorCentroid), kvp.Key, (IVector)vectorCentroid); // this.AddChild(child); // lstNewChildren.Add(child); // nSpawnCount++; //} } } //bool hasSpawned = false; //foreach (SphereEx<L> child in lstNewChildren) //{ // if (child.DoesEncloseVector(lvector, measure)) // { // nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); // hasSpawned = true; // } //} this.LDAVectors.Clear(); //if (!hasSpawned) //{ // this.LDAVectors.Add(lvector.Label, new List<LabeledVector<L>>() { lvector }); //} } else { this.DiscriminantEx = discriminant; } #endregion } else if (this.LDAVectors.Keys.Count > 2) { #region If the sphere contains 3 or more classes, do the following... // 1. Create children from the OLDER label-sets // 2. Try to spawn the presented LVector IN the NEW children just created // 3. Clear the dictionary, LDAVectors // 4. If you can't spawn WITHIN the NEW children, add the vector to this.LDAVectors List <SphereEx <L> > lstNewChildren = new List <SphereEx <L> >(); foreach (KeyValuePair <L, List <LabeledVector <L> > > kvp in this.LDAVectors) { if (!kvp.Key.Equals(lvector.Label)) { Vector vectorCentroid = Vector.Centroid(kvp.Value); if (!Vector.EqualsEx(this, vectorCentroid)) { SphereEx <L> child = new SphereEx <L>(this.Radius - measure(this, vectorCentroid), kvp.Key, (IVector)vectorCentroid); this.AddChild(child); lstNewChildren.Add(child); nSpawnCount++; } } } //bool hasSpawned = false; //foreach (SphereEx<L> child in lstNewChildren) //{ // if (child.DoesEncloseVector(lvector, measure)) // { // nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); // hasSpawned = true; // } //} this.LDAVectors.Clear(); //if (!hasSpawned) //{ // this.LDAVectors.Add(lvector.Label, new List<LabeledVector<L>>() { lvector }); //} #endregion } // Note: If this.LDAVectors.Keys.Count == 1, don't do anything additional. } } #endregion } } return(nSpawnCount); }
public int Spawn(LabeledVector <L> lvector, DistanceDelegate measure, ParallelStrategy parallelStrategy, bool spawnUsingLDA) { int nSpawnCount = 0; if (this.DoesEncloseVector(lvector, measure)) { if (parallelStrategy == ParallelStrategy.Multithreaded) { List <Task> lstSpawnThreads = new List <Task>(); foreach (SphereEx <L> child in this.Children) { lstSpawnThreads.Add(Task.Factory.StartNew(c => { nSpawnCount += ((SphereEx <L>)c).Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); }, child, TaskCreationOptions.LongRunning)); } Task.WaitAll(lstSpawnThreads.ToArray()); } else { foreach (SphereEx <L> child in this.Children) { nSpawnCount += child.Spawn(lvector, measure, ParallelStrategy.SingleThreaded, spawnUsingLDA); } } if (!spawnUsingLDA) { #region Regular Spawn if (!this.Label.Equals(lvector.Label) && !Vector.EqualsEx(this, lvector) && !this.DoesAtLeastOneChildEncloseVector(lvector, measure)) { SphereEx <L> child = new SphereEx <L>(this.Radius - measure(this, lvector), lvector); child.LDAVectors.Add(child.Label, new List <KeyValuePair <int, LabeledVector <L> > >() { new KeyValuePair <int, LabeledVector <L> >(0, lvector) }); this.AddChild(child); nSpawnCount++; } #endregion } else { #region LDA Spawn // Note that LDA Spawn doesn't work with SquaredEuclideanDistance. I may -- in the future -- do an additional check to see if the vector is contained by the hypersphere with SquaredEuclideanDistance and EuclideanDistance. If so, this will work. Until then, I'll leave it as is. if (this.Children.Any()) { // Next if-statement a duplicate of the region "Regular Spawn" // Remember, LDA is only applied at nodes with no children (leaf) if (!this.Label.Equals(lvector.Label) && !Vector.EqualsEx(this, lvector) && !this.DoesAtLeastOneChildEncloseVector(lvector, measure)) { SphereEx <L> child = new SphereEx <L>(this.Radius - measure(this, lvector), lvector); child.LDAVectors.Add(child.Label, new List <KeyValuePair <int, LabeledVector <L> > >() { new KeyValuePair <int, LabeledVector <L> >(0, lvector) }); this.AddChild(child); nSpawnCount++; } } else { bool bContains = false; List <KeyValuePair <int, LabeledVector <L> > > lst; if (!this.LDAVectors.TryGetValue(lvector.Label, out lst)) { this.LDAVectors.Add(lvector.Label, (lst = new List <KeyValuePair <int, LabeledVector <L> > >())); } else { bContains = lst.Any(kvp => Vector.EqualsEx(kvp.Value, lvector)); } if (!bContains) { lst.Add(new KeyValuePair <int, LabeledVector <L> >(this.LDAVectors.Sum(kvp => kvp.Value.Count), lvector)); if (this.LDAVectors.Keys.Count == 2) { #region If the sphere contains exactly 2 classes, do the following... bool bIsSeparable = false; DiscriminantEx <L> discriminant = null; try { bIsSeparable = LDAEx.IsCompletelySeparatedWithDiscriminant(this.LDAVectors.ElementAt(0).Value.Select(kvp => kvp.Value).ToList(), this.LDAVectors.ElementAt(1).Value.Select(kvp => kvp.Value).ToList(), out discriminant); } catch { // Just consume, leaving bIsSeparable = false } if (!bIsSeparable) { bool spawnedAtLeastOnce = false; List <LabeledVector <L> > lstCache = this.LDAVectors.SelectMany(kvp => kvp.Value).OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToList(); this.LDAVectors.Clear(); this.DiscriminantEx = null; foreach (LabeledVector <L> v in lstCache) { if (!spawnedAtLeastOnce) { int count = this.Spawn(v, measure, ParallelStrategy.SingleThreaded, false); nSpawnCount += count; spawnedAtLeastOnce = count > 0; } else { nSpawnCount += this.Spawn(v, measure, ParallelStrategy.SingleThreaded, true); } } } else { if (discriminant != null) { this.DiscriminantEx = discriminant; } } #endregion } else if (this.LDAVectors.Keys.Count > 2) { #region If the sphere contains 3 or more classes, do the following... bool spawnedAtLeastOnce = false; List <LabeledVector <L> > lstCache = this.LDAVectors.SelectMany(kvp => kvp.Value).OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToList(); this.LDAVectors.Clear(); this.DiscriminantEx = null; foreach (LabeledVector <L> v in lstCache) { if (!spawnedAtLeastOnce) { int count = this.Spawn(v, measure, ParallelStrategy.SingleThreaded, false); nSpawnCount += count; spawnedAtLeastOnce = count > 0; } else { nSpawnCount += this.Spawn(v, measure, ParallelStrategy.SingleThreaded, true); } } #endregion } // Note: If this.LDAVectors.Keys.Count == 1, don't do anything additional. } } #endregion } } return(nSpawnCount); }