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.math3.ml.neuralnet; 019 020import java.io.Serializable; 021import java.io.ObjectInputStream; 022import java.util.concurrent.atomic.AtomicReference; 023import java.util.concurrent.atomic.AtomicLong; 024 025import org.apache.commons.math3.exception.DimensionMismatchException; 026import org.apache.commons.math3.util.Precision; 027 028 029/** 030 * Describes a neuron element of a neural network. 031 * 032 * This class aims to be thread-safe. 033 * 034 * @since 3.3 035 */ 036public class Neuron implements Serializable { 037 /** Serializable. */ 038 private static final long serialVersionUID = 20130207L; 039 /** Identifier. */ 040 private final long identifier; 041 /** Length of the feature set. */ 042 private final int size; 043 /** Neuron data. */ 044 private final AtomicReference<double[]> features; 045 /** Number of attempts to update a neuron. */ 046 private final AtomicLong numberOfAttemptedUpdates = new AtomicLong(0); 047 /** Number of successful updates of a neuron. */ 048 private final AtomicLong numberOfSuccessfulUpdates = new AtomicLong(0); 049 050 /** 051 * Creates a neuron. 052 * The size of the feature set is fixed to the length of the given 053 * argument. 054 * <br/> 055 * Constructor is package-private: Neurons must be 056 * {@link Network#createNeuron(double[]) created} by the network 057 * instance to which they will belong. 058 * 059 * @param identifier Identifier (assigned by the {@link Network}). 060 * @param features Initial values of the feature set. 061 */ 062 Neuron(long identifier, 063 double[] features) { 064 this.identifier = identifier; 065 this.size = features.length; 066 this.features = new AtomicReference<double[]>(features.clone()); 067 } 068 069 /** 070 * Performs a deep copy of this instance. 071 * Upon return, the copied and original instances will be independent: 072 * Updating one will not affect the other. 073 * 074 * @return a new instance with the same state as this instance. 075 * @since 3.6 076 */ 077 public synchronized Neuron copy() { 078 final Neuron copy = new Neuron(getIdentifier(), 079 getFeatures()); 080 copy.numberOfAttemptedUpdates.set(numberOfAttemptedUpdates.get()); 081 copy.numberOfSuccessfulUpdates.set(numberOfSuccessfulUpdates.get()); 082 083 return copy; 084 } 085 086 /** 087 * Gets the neuron's identifier. 088 * 089 * @return the identifier. 090 */ 091 public long getIdentifier() { 092 return identifier; 093 } 094 095 /** 096 * Gets the length of the feature set. 097 * 098 * @return the number of features. 099 */ 100 public int getSize() { 101 return size; 102 } 103 104 /** 105 * Gets the neuron's features. 106 * 107 * @return a copy of the neuron's features. 108 */ 109 public double[] getFeatures() { 110 return features.get().clone(); 111 } 112 113 /** 114 * Tries to atomically update the neuron's features. 115 * Update will be performed only if the expected values match the 116 * current values.<br/> 117 * In effect, when concurrent threads call this method, the state 118 * could be modified by one, so that it does not correspond to the 119 * the state assumed by another. 120 * Typically, a caller {@link #getFeatures() retrieves the current state}, 121 * and uses it to compute the new state. 122 * During this computation, another thread might have done the same 123 * thing, and updated the state: If the current thread were to proceed 124 * with its own update, it would overwrite the new state (which might 125 * already have been used by yet other threads). 126 * To prevent this, the method does not perform the update when a 127 * concurrent modification has been detected, and returns {@code false}. 128 * When this happens, the caller should fetch the new current state, 129 * redo its computation, and call this method again. 130 * 131 * @param expect Current values of the features, as assumed by the caller. 132 * Update will never succeed if the contents of this array does not match 133 * the values returned by {@link #getFeatures()}. 134 * @param update Features's new values. 135 * @return {@code true} if the update was successful, {@code false} 136 * otherwise. 137 * @throws DimensionMismatchException if the length of {@code update} is 138 * not the same as specified in the {@link #Neuron(long,double[]) 139 * constructor}. 140 */ 141 public boolean compareAndSetFeatures(double[] expect, 142 double[] update) { 143 if (update.length != size) { 144 throw new DimensionMismatchException(update.length, size); 145 } 146 147 // Get the internal reference. Note that this must not be a copy; 148 // otherwise the "compareAndSet" below will always fail. 149 final double[] current = features.get(); 150 if (!containSameValues(current, expect)) { 151 // Some other thread already modified the state. 152 return false; 153 } 154 155 // Increment attempt counter. 156 numberOfAttemptedUpdates.incrementAndGet(); 157 158 if (features.compareAndSet(current, update.clone())) { 159 // The current thread could atomically update the state (attempt succeeded). 160 numberOfSuccessfulUpdates.incrementAndGet(); 161 return true; 162 } else { 163 // Some other thread came first (attempt failed). 164 return false; 165 } 166 } 167 168 /** 169 * Retrieves the number of calls to the 170 * {@link #compareAndSetFeatures(double[],double[]) compareAndSetFeatures} 171 * method. 172 * Note that if the caller wants to use this method in combination with 173 * {@link #getNumberOfSuccessfulUpdates()}, additional synchronization 174 * may be required to ensure consistency. 175 * 176 * @return the number of update attempts. 177 * @since 3.6 178 */ 179 public long getNumberOfAttemptedUpdates() { 180 return numberOfAttemptedUpdates.get(); 181 } 182 183 /** 184 * Retrieves the number of successful calls to the 185 * {@link #compareAndSetFeatures(double[],double[]) compareAndSetFeatures} 186 * method. 187 * Note that if the caller wants to use this method in combination with 188 * {@link #getNumberOfAttemptedUpdates()}, additional synchronization 189 * may be required to ensure consistency. 190 * 191 * @return the number of successful updates. 192 * @since 3.6 193 */ 194 public long getNumberOfSuccessfulUpdates() { 195 return numberOfSuccessfulUpdates.get(); 196 } 197 198 /** 199 * Checks whether the contents of both arrays is the same. 200 * 201 * @param current Current values. 202 * @param expect Expected values. 203 * @throws DimensionMismatchException if the length of {@code expected} 204 * is not the same as specified in the {@link #Neuron(long,double[]) 205 * constructor}. 206 * @return {@code true} if the arrays contain the same values. 207 */ 208 private boolean containSameValues(double[] current, 209 double[] expect) { 210 if (expect.length != size) { 211 throw new DimensionMismatchException(expect.length, size); 212 } 213 214 for (int i = 0; i < size; i++) { 215 if (!Precision.equals(current[i], expect[i])) { 216 return false; 217 } 218 } 219 return true; 220 } 221 222 /** 223 * Prevents proxy bypass. 224 * 225 * @param in Input stream. 226 */ 227 private void readObject(ObjectInputStream in) { 228 throw new IllegalStateException(); 229 } 230 231 /** 232 * Custom serialization. 233 * 234 * @return the proxy instance that will be actually serialized. 235 */ 236 private Object writeReplace() { 237 return new SerializationProxy(identifier, 238 features.get()); 239 } 240 241 /** 242 * Serialization. 243 */ 244 private static class SerializationProxy implements Serializable { 245 /** Serializable. */ 246 private static final long serialVersionUID = 20130207L; 247 /** Features. */ 248 private final double[] features; 249 /** Identifier. */ 250 private final long identifier; 251 252 /** 253 * @param identifier Identifier. 254 * @param features Features. 255 */ 256 SerializationProxy(long identifier, 257 double[] features) { 258 this.identifier = identifier; 259 this.features = features; 260 } 261 262 /** 263 * Custom serialization. 264 * 265 * @return the {@link Neuron} for which this instance is the proxy. 266 */ 267 private Object readResolve() { 268 return new Neuron(identifier, 269 features); 270 } 271 } 272}