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.math3.ml.neuralnet.oned;
19  
20  import java.io.Serializable;
21  import java.io.ObjectInputStream;
22  import org.apache.commons.math3.ml.neuralnet.Network;
23  import org.apache.commons.math3.ml.neuralnet.FeatureInitializer;
24  import org.apache.commons.math3.exception.NumberIsTooSmallException;
25  import org.apache.commons.math3.exception.OutOfRangeException;
26  
27  /**
28   * Neural network with the topology of a one-dimensional line.
29   * Each neuron defines one point on the line.
30   *
31   * @since 3.3
32   */
33  public class NeuronString implements Serializable {
34      /** Serial version ID */
35      private static final long serialVersionUID = 1L;
36      /** Underlying network. */
37      private final Network network;
38      /** Number of neurons. */
39      private final int size;
40      /** Wrap. */
41      private final boolean wrap;
42  
43      /**
44       * Mapping of the 1D coordinate to the neuron identifiers
45       * (attributed by the {@link #network} instance).
46       */
47      private final long[] identifiers;
48  
49      /**
50       * Constructor with restricted access, solely used for deserialization.
51       *
52       * @param wrap Whether to wrap the dimension (i.e the first and last
53       * neurons will be linked together).
54       * @param featuresList Arrays that will initialize the features sets of
55       * the network's neurons.
56       * @throws NumberIsTooSmallException if {@code num < 2}.
57       */
58      NeuronString(boolean wrap,
59                   double[][] featuresList) {
60          size = featuresList.length;
61  
62          if (size < 2) {
63              throw new NumberIsTooSmallException(size, 2, true);
64          }
65  
66          this.wrap = wrap;
67  
68          final int fLen = featuresList[0].length;
69          network = new Network(0, fLen);
70          identifiers = new long[size];
71  
72          // Add neurons.
73          for (int i = 0; i < size; i++) {
74              identifiers[i] = network.createNeuron(featuresList[i]);
75          }
76  
77          // Add links.
78          createLinks();
79      }
80  
81      /**
82       * Creates a one-dimensional network:
83       * Each neuron not located on the border of the mesh has two
84       * neurons linked to it.
85       * <br/>
86       * The links are bi-directional.
87       * Neurons created successively are neighbours (i.e. there are
88       * links between them).
89       * <br/>
90       * The topology of the network can also be a circle (if the
91       * dimension is wrapped).
92       *
93       * @param num Number of neurons.
94       * @param wrap Whether to wrap the dimension (i.e the first and last
95       * neurons will be linked together).
96       * @param featureInit Arrays that will initialize the features sets of
97       * the network's neurons.
98       * @throws NumberIsTooSmallException if {@code num < 2}.
99       */
100     public NeuronString(int num,
101                         boolean wrap,
102                         FeatureInitializer[] featureInit) {
103         if (num < 2) {
104             throw new NumberIsTooSmallException(num, 2, true);
105         }
106 
107         size = num;
108         this.wrap = wrap;
109         identifiers = new long[num];
110 
111         final int fLen = featureInit.length;
112         network = new Network(0, fLen);
113 
114         // Add neurons.
115         for (int i = 0; i < num; i++) {
116             final double[] features = new double[fLen];
117             for (int fIndex = 0; fIndex < fLen; fIndex++) {
118                 features[fIndex] = featureInit[fIndex].value();
119             }
120             identifiers[i] = network.createNeuron(features);
121         }
122 
123         // Add links.
124         createLinks();
125     }
126 
127     /**
128      * Retrieves the underlying network.
129      * A reference is returned (enabling, for example, the network to be
130      * trained).
131      * This also implies that calling methods that modify the {@link Network}
132      * topology may cause this class to become inconsistent.
133      *
134      * @return the network.
135      */
136     public Network getNetwork() {
137         return network;
138     }
139 
140     /**
141      * Gets the number of neurons.
142      *
143      * @return the number of neurons.
144      */
145     public int getSize() {
146         return size;
147     }
148 
149     /**
150      * Retrieves the features set from the neuron at location
151      * {@code i} in the map.
152      *
153      * @param i Neuron index.
154      * @return the features of the neuron at index {@code i}.
155      * @throws OutOfRangeException if {@code i} is out of range.
156      */
157     public double[] getFeatures(int i) {
158         if (i < 0 ||
159             i >= size) {
160             throw new OutOfRangeException(i, 0, size - 1);
161         }
162 
163         return network.getNeuron(identifiers[i]).getFeatures();
164     }
165 
166     /**
167      * Creates the neighbour relationships between neurons.
168      */
169     private void createLinks() {
170         for (int i = 0; i < size - 1; i++) {
171             network.addLink(network.getNeuron(i), network.getNeuron(i + 1));
172         }
173         for (int i = size - 1; i > 0; i--) {
174             network.addLink(network.getNeuron(i), network.getNeuron(i - 1));
175         }
176         if (wrap) {
177             network.addLink(network.getNeuron(0), network.getNeuron(size - 1));
178             network.addLink(network.getNeuron(size - 1), network.getNeuron(0));
179         }
180     }
181 
182     /**
183      * Prevents proxy bypass.
184      *
185      * @param in Input stream.
186      */
187     private void readObject(ObjectInputStream in) {
188         throw new IllegalStateException();
189     }
190 
191     /**
192      * Custom serialization.
193      *
194      * @return the proxy instance that will be actually serialized.
195      */
196     private Object writeReplace() {
197         final double[][] featuresList = new double[size][];
198         for (int i = 0; i < size; i++) {
199             featuresList[i] = getFeatures(i);
200         }
201 
202         return new SerializationProxy(wrap,
203                                       featuresList);
204     }
205 
206     /**
207      * Serialization.
208      */
209     private static class SerializationProxy implements Serializable {
210         /** Serializable. */
211         private static final long serialVersionUID = 20130226L;
212         /** Wrap. */
213         private final boolean wrap;
214         /** Neurons' features. */
215         private final double[][] featuresList;
216 
217         /**
218          * @param wrap Whether the dimension is wrapped.
219          * @param featuresList List of neurons features.
220          * {@code neuronList}.
221          */
222         SerializationProxy(boolean wrap,
223                            double[][] featuresList) {
224             this.wrap = wrap;
225             this.featuresList = featuresList;
226         }
227 
228         /**
229          * Custom serialization.
230          *
231          * @return the {@link Neuron} for which this instance is the proxy.
232          */
233         private Object readResolve() {
234             return new NeuronString(wrap,
235                                     featuresList);
236         }
237     }
238 }