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 }