Neuron.java
- /*
- * 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.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 {
- /** 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;
- }
- }