View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math4.neuralnet.sofm;
19  
20  import org.junit.Test;
21  import org.junit.Assert;
22  
23  import org.apache.commons.rng.UniformRandomProvider;
24  import org.apache.commons.rng.simple.RandomSource;
25  
26  import org.apache.commons.math4.neuralnet.DistanceMeasure;
27  import org.apache.commons.math4.neuralnet.EuclideanDistance;
28  import org.apache.commons.math4.neuralnet.FeatureInitializer;
29  import org.apache.commons.math4.neuralnet.FeatureInitializerFactory;
30  import org.apache.commons.math4.neuralnet.MapRanking;
31  import org.apache.commons.math4.neuralnet.Network;
32  import org.apache.commons.math4.neuralnet.Neuron;
33  import org.apache.commons.math4.neuralnet.OffsetFeatureInitializer;
34  import org.apache.commons.math4.neuralnet.UpdateAction;
35  import org.apache.commons.math4.neuralnet.oned.NeuronString;
36  
37  /**
38   * Tests for {@link KohonenUpdateAction} class.
39   */
40  public class KohonenUpdateActionTest {
41      private final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
42  
43      /*
44       * Test assumes that the network is
45       *
46       *  0-----1-----2
47       */
48      @Test
49      public void testUpdate() {
50          final FeatureInitializer init
51              = new OffsetFeatureInitializer(FeatureInitializerFactory.uniform(rng, 0, 0.1));
52          final FeatureInitializer[] initArray = {init};
53  
54          final int netSize = 3;
55          final Network net = new NeuronString(netSize, false, initArray).getNetwork();
56          final DistanceMeasure dist = new EuclideanDistance();
57          final LearningFactorFunction learning
58              = LearningFactorFunctionFactory.exponentialDecay(1, 0.1, 100);
59          final NeighbourhoodSizeFunction neighbourhood
60              = NeighbourhoodSizeFunctionFactory.exponentialDecay(3, 1, 100);
61          final UpdateAction update = new KohonenUpdateAction(dist, learning, neighbourhood);
62          final MapRanking rank = new MapRanking(net, dist);
63  
64          // The following test ensures that, after one "update",
65          // 1. when the initial learning rate equal to 1, the best matching
66          //    neuron's features are mapped to the input's features,
67          // 2. when the initial neighbourhood is larger than the network's size,
68          //    all neuron's features get closer to the input's features.
69  
70          final double[] features = new double[] {0.3};
71          final double[] distancesBefore = getDistances(net, dist, features);
72          final Neuron bestBefore = rank.rank(features, 1).get(0);
73  
74          // Initial distance from the best match is larger than zero.
75          Assert.assertTrue(dist.applyAsDouble(bestBefore.getFeatures(), features) >= 0.2);
76  
77          update.update(net, features);
78  
79          final double[] distancesAfter = getDistances(net, dist, features);
80          final Neuron bestAfter = rank.rank(features, 1).get(0);
81  
82          Assert.assertEquals(bestBefore, bestAfter);
83          // Distance is now zero.
84          Assert.assertEquals(0, dist.applyAsDouble(bestAfter.getFeatures(), features), 1e-16);
85  
86          for (int i = 0; i < netSize; i++) {
87              // All distances have decreased.
88              Assert.assertTrue(distancesAfter[i] < distancesBefore[i]);
89          }
90      }
91  
92      /**
93       * Gets the distance of each Neuron to the specified features.
94       * Distances are returned ordered by the Neuron ID.
95       *
96       * @param net Network
97       * @param dist Distance measure
98       * @param features Feature vector
99       * @return the distances
100      */
101     private static double[] getDistances(Network net,
102                                          DistanceMeasure dist,
103                                          double[] features) {
104         return net.getNeurons()
105                   .stream()
106                   .sorted((a, b) -> Long.compare(a.getIdentifier(), b.getIdentifier()))
107                   .mapToDouble(n -> dist.applyAsDouble(n.getFeatures(), features))
108                   .toArray();
109     }
110 }