| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.math4.neuralnet; |
| |
| import java.io.Serializable; |
| import java.io.ObjectInputStream; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| import org.apache.commons.numbers.core.Precision; |
| import org.apache.commons.math4.neuralnet.internal.NeuralNetException; |
| |
| /** |
| * Describes a neuron element of a neural network. |
| * |
| * This class aims to be thread-safe. |
| * |
| * @since 3.3 |
| */ |
| public class Neuron implements Serializable { |
| /** Serializable. */ |
| private static final long serialVersionUID = 20130207L; |
| /** Identifier. */ |
| private final long identifier; |
| /** Length of the feature set. */ |
| private final int size; |
| /** Neuron data. */ |
| private final AtomicReference<double[]> features; |
| /** Number of attempts to update a neuron. */ |
| private final AtomicLong numberOfAttemptedUpdates = new AtomicLong(0); |
| /** Number of successful updates of a neuron. */ |
| private final AtomicLong numberOfSuccessfulUpdates = new AtomicLong(0); |
| |
| /** |
| * Creates a neuron. |
| * The size of the feature set is fixed to the length of the given |
| * argument. |
| * <br> |
| * Constructor is package-private: Neurons must be |
| * {@link Network#createNeuron(double[]) created} by the network |
| * instance to which they will belong. |
| * |
| * @param identifier Identifier (assigned by the {@link Network}). |
| * @param features Initial values of the feature set. |
| */ |
| Neuron(long identifier, |
| double[] features) { |
| this.identifier = identifier; |
| this.size = features.length; |
| this.features = new AtomicReference<>(features.clone()); |
| } |
| |
| /** |
| * Performs a deep copy of this instance. |
| * Upon return, the copied and original instances will be independent: |
| * Updating one will not affect the other. |
| * |
| * @return a new instance with the same state as this instance. |
| * @since 3.6 |
| */ |
| public synchronized Neuron copy() { |
| final Neuron copy = new Neuron(getIdentifier(), |
| getFeatures()); |
| copy.numberOfAttemptedUpdates.set(numberOfAttemptedUpdates.get()); |
| copy.numberOfSuccessfulUpdates.set(numberOfSuccessfulUpdates.get()); |
| |
| return copy; |
| } |
| |
| /** |
| * Gets the neuron's identifier. |
| * |
| * @return the identifier. |
| */ |
| public long getIdentifier() { |
| return identifier; |
| } |
| |
| /** |
| * Gets the length of the feature set. |
| * |
| * @return the number of features. |
| */ |
| public int getSize() { |
| return size; |
| } |
| |
| /** |
| * Gets the neuron's features. |
| * |
| * @return a copy of the neuron's features. |
| */ |
| public double[] getFeatures() { |
| return features.get().clone(); |
| } |
| |
| /** |
| * Tries to atomically update the neuron's features. |
| * Update will be performed only if the expected values match the |
| * current values.<br> |
| * In effect, when concurrent threads call this method, the state |
| * could be modified by one, so that it does not correspond to the |
| * the state assumed by another. |
| * Typically, a caller {@link #getFeatures() retrieves the current state}, |
| * and uses it to compute the new state. |
| * During this computation, another thread might have done the same |
| * thing, and updated the state: If the current thread were to proceed |
| * with its own update, it would overwrite the new state (which might |
| * already have been used by yet other threads). |
| * To prevent this, the method does not perform the update when a |
| * concurrent modification has been detected, and returns {@code false}. |
| * When this happens, the caller should fetch the new current state, |
| * redo its computation, and call this method again. |
| * |
| * @param expect Current values of the features, as assumed by the caller. |
| * Update will never succeed if the contents of this array does not match |
| * the values returned by {@link #getFeatures()}. |
| * @param update Features's new values. |
| * @return {@code true} if the update was successful, {@code false} |
| * otherwise. |
| * @throws IllegalArgumentException if the length of {@code update} is |
| * not the same as specified in the {@link #Neuron(long,double[]) |
| * constructor}. |
| */ |
| public boolean compareAndSetFeatures(double[] expect, |
| double[] update) { |
| if (update.length != size) { |
| throw new NeuralNetException(NeuralNetException.SIZE_MISMATCH, |
| update.length, size); |
| } |
| |
| // Get the internal reference. Note that this must not be a copy; |
| // otherwise the "compareAndSet" below will always fail. |
| final double[] current = features.get(); |
| if (!containSameValues(current, expect)) { |
| // Some other thread already modified the state. |
| return false; |
| } |
| |
| // Increment attempt counter. |
| numberOfAttemptedUpdates.incrementAndGet(); |
| |
| if (features.compareAndSet(current, update.clone())) { |
| // The current thread could atomically update the state (attempt succeeded). |
| numberOfSuccessfulUpdates.incrementAndGet(); |
| return true; |
| } else { |
| // Some other thread came first (attempt failed). |
| return false; |
| } |
| } |
| |
| /** |
| * Retrieves the number of calls to the |
| * {@link #compareAndSetFeatures(double[],double[]) compareAndSetFeatures} |
| * method. |
| * Note that if the caller wants to use this method in combination with |
| * {@link #getNumberOfSuccessfulUpdates()}, additional synchronization |
| * may be required to ensure consistency. |
| * |
| * @return the number of update attempts. |
| * @since 3.6 |
| */ |
| public long getNumberOfAttemptedUpdates() { |
| return numberOfAttemptedUpdates.get(); |
| } |
| |
| /** |
| * Retrieves the number of successful calls to the |
| * {@link #compareAndSetFeatures(double[],double[]) compareAndSetFeatures} |
| * method. |
| * Note that if the caller wants to use this method in combination with |
| * {@link #getNumberOfAttemptedUpdates()}, additional synchronization |
| * may be required to ensure consistency. |
| * |
| * @return the number of successful updates. |
| * @since 3.6 |
| */ |
| public long getNumberOfSuccessfulUpdates() { |
| return numberOfSuccessfulUpdates.get(); |
| } |
| |
| /** |
| * Checks whether the contents of both arrays is the same. |
| * |
| * @param current Current values. |
| * @param expect Expected values. |
| * @throws IllegalArgumentException if the length of {@code expect} |
| * is not the same as specified in the {@link #Neuron(long,double[]) |
| * constructor}. |
| * @return {@code true} if the arrays contain the same values. |
| */ |
| private boolean containSameValues(double[] current, |
| double[] expect) { |
| if (expect.length != size) { |
| throw new NeuralNetException(NeuralNetException.SIZE_MISMATCH, |
| expect.length, size); |
| } |
| |
| for (int i = 0; i < size; i++) { |
| if (!Precision.equals(current[i], expect[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Prevents proxy bypass. |
| * |
| * @param in Input stream. |
| */ |
| private void readObject(ObjectInputStream in) { |
| throw new IllegalStateException(); |
| } |
| |
| /** |
| * Custom serialization. |
| * |
| * @return the proxy instance that will be actually serialized. |
| */ |
| private Object writeReplace() { |
| return new SerializationProxy(identifier, |
| features.get()); |
| } |
| |
| /** |
| * Serialization. |
| */ |
| private static class SerializationProxy implements Serializable { |
| /** Serializable. */ |
| private static final long serialVersionUID = 20130207L; |
| /** Features. */ |
| private final double[] features; |
| /** Identifier. */ |
| private final long identifier; |
| |
| /** |
| * @param identifier Identifier. |
| * @param features Features. |
| */ |
| SerializationProxy(long identifier, |
| double[] features) { |
| this.identifier = identifier; |
| this.features = features; |
| } |
| |
| /** |
| * Custom serialization. |
| * |
| * @return the {@link Neuron} for which this instance is the proxy. |
| */ |
| private Object readResolve() { |
| return new Neuron(identifier, |
| features); |
| } |
| } |
| } |