001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.math4.neuralnet.oned;
019
020import org.apache.commons.math4.neuralnet.internal.NeuralNetException;
021import org.apache.commons.math4.neuralnet.FeatureInitializer;
022import org.apache.commons.math4.neuralnet.Network;
023
024/**
025 * Neural network with the topology of a one-dimensional line.
026 * Each neuron defines one point on the line.
027 *
028 * @since 3.3
029 */
030public class NeuronString {
031    /** Minimal number of neurons. */
032    private static final int MIN_NEURONS = 2;
033    /** Underlying network. */
034    private final Network network;
035    /** Number of neurons. */
036    private final int size;
037    /** Wrap. */
038    private final boolean wrap;
039
040    /**
041     * Mapping of the 1D coordinate to the neuron identifiers
042     * (attributed by the {@link #network} instance).
043     */
044    private final long[] identifiers;
045
046    /**
047     * Constructor with restricted access, solely used for deserialization.
048     *
049     * @param wrap Whether to wrap the dimension (i.e the first and last
050     * neurons will be linked together).
051     * @param featuresList Arrays that will initialize the features sets of
052     * the network's neurons.
053     * @throws IllegalArgumentException if {@code num < 2}.
054     */
055    NeuronString(boolean wrap,
056                 double[][] featuresList) {
057        size = featuresList.length;
058
059        if (size < MIN_NEURONS) {
060            throw new NeuralNetException(NeuralNetException.TOO_SMALL, size, MIN_NEURONS);
061        }
062
063        this.wrap = wrap;
064
065        final int fLen = featuresList[0].length;
066        network = new Network(0, fLen);
067        identifiers = new long[size];
068
069        // Add neurons.
070        for (int i = 0; i < size; i++) {
071            identifiers[i] = network.createNeuron(featuresList[i]);
072        }
073
074        // Add links.
075        createLinks();
076    }
077
078    /**
079     * Creates a one-dimensional network:
080     * Each neuron not located on the border of the mesh has two
081     * neurons linked to it.
082     * <br>
083     * The links are bi-directional.
084     * Neurons created successively are neighbours (i.e. there are
085     * links between them).
086     * <br>
087     * The topology of the network can also be a circle (if the
088     * dimension is wrapped).
089     *
090     * @param num Number of neurons.
091     * @param wrap Whether to wrap the dimension (i.e the first and last
092     * neurons will be linked together).
093     * @param featureInit Arrays that will initialize the features sets of
094     * the network's neurons.
095     * @throws IllegalArgumentException if {@code num < 2}.
096     */
097    public NeuronString(int num,
098                        boolean wrap,
099                        FeatureInitializer[] featureInit) {
100        if (num < MIN_NEURONS) {
101            throw new NeuralNetException(NeuralNetException.TOO_SMALL, num, MIN_NEURONS);
102        }
103
104        size = num;
105        this.wrap = wrap;
106        identifiers = new long[num];
107
108        final int fLen = featureInit.length;
109        network = new Network(0, fLen);
110
111        // Add neurons.
112        for (int i = 0; i < num; i++) {
113            final double[] features = new double[fLen];
114            for (int fIndex = 0; fIndex < fLen; fIndex++) {
115                features[fIndex] = featureInit[fIndex].value();
116            }
117            identifiers[i] = network.createNeuron(features);
118        }
119
120        // Add links.
121        createLinks();
122    }
123
124    /**
125     * Retrieves the underlying network.
126     * A reference is returned (enabling, for example, the network to be
127     * trained).
128     * This also implies that calling methods that modify the {@link Network}
129     * topology may cause this class to become inconsistent.
130     *
131     * @return the network.
132     */
133    public Network getNetwork() {
134        return network;
135    }
136
137    /**
138     * Gets the number of neurons.
139     *
140     * @return the number of neurons.
141     */
142    public int getSize() {
143        return size;
144    }
145
146    /**
147     * Indicates whether the line of neurons is wrapped.
148     *
149     * @return {@code true} if the last neuron is linked to the first neuron.
150     */
151    public boolean isWrapped() {
152        return wrap;
153    }
154
155    /**
156     * Retrieves the features set from the neuron at location
157     * {@code i} in the map.
158     *
159     * @param i Neuron index.
160     * @return the features of the neuron at index {@code i}.
161     * @throws IllegalArgumentException if {@code i} is out of range.
162     */
163    public double[] getFeatures(int i) {
164        if (i < 0 ||
165            i >= size) {
166            throw new NeuralNetException(NeuralNetException.OUT_OF_RANGE, i, 0, size - 1);
167        }
168
169        return network.getNeuron(identifiers[i]).getFeatures();
170    }
171
172    /**
173     * Creates the neighbour relationships between neurons.
174     */
175    private void createLinks() {
176        for (int i = 0; i < size - 1; i++) {
177            network.addLink(network.getNeuron(i), network.getNeuron(i + 1));
178        }
179        for (int i = size - 1; i > 0; i--) {
180            network.addLink(network.getNeuron(i), network.getNeuron(i - 1));
181        }
182        if (wrap) {
183            network.addLink(network.getNeuron(0), network.getNeuron(size - 1));
184            network.addLink(network.getNeuron(size - 1), network.getNeuron(0));
185        }
186    }
187}