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; 19 20 import java.io.Serializable; 21 import java.io.ObjectInputStream; 22 import java.util.concurrent.atomic.AtomicReference; 23 import org.apache.commons.math3.util.Precision; 24 import org.apache.commons.math3.exception.DimensionMismatchException; 25 26 27 /** 28 * Describes a neuron element of a neural network. 29 * 30 * This class aims to be thread-safe. 31 * 32 * @since 3.3 33 */ 34 public class Neuron implements Serializable { 35 /** Serializable. */ 36 private static final long serialVersionUID = 20130207L; 37 /** Identifier. */ 38 private final long identifier; 39 /** Length of the feature set. */ 40 private final int size; 41 /** Neuron data. */ 42 private final AtomicReference<double[]> features; 43 44 /** 45 * Creates a neuron. 46 * The size of the feature set is fixed to the length of the given 47 * argument. 48 * <br/> 49 * Constructor is package-private: Neurons must be 50 * {@link Network#createNeuron(double[]) created} by the network 51 * instance to which they will belong. 52 * 53 * @param identifier Identifier (assigned by the {@link Network}). 54 * @param features Initial values of the feature set. 55 */ 56 Neuron(long identifier, 57 double[] features) { 58 this.identifier = identifier; 59 this.size = features.length; 60 this.features = new AtomicReference<double[]>(features.clone()); 61 } 62 63 /** 64 * Gets the neuron's identifier. 65 * 66 * @return the identifier. 67 */ 68 public long getIdentifier() { 69 return identifier; 70 } 71 72 /** 73 * Gets the length of the feature set. 74 * 75 * @return the number of features. 76 */ 77 public int getSize() { 78 return size; 79 } 80 81 /** 82 * Gets the neuron's features. 83 * 84 * @return a copy of the neuron's features. 85 */ 86 public double[] getFeatures() { 87 return features.get().clone(); 88 } 89 90 /** 91 * Tries to atomically update the neuron's features. 92 * Update will be performed only if the expected values match the 93 * current values.<br/> 94 * In effect, when concurrent threads call this method, the state 95 * could be modified by one, so that it does not correspond to the 96 * the state assumed by another. 97 * Typically, a caller {@link #getFeatures() retrieves the current state}, 98 * and uses it to compute the new state. 99 * During this computation, another thread might have done the same 100 * thing, and updated the state: If the current thread were to proceed 101 * with its own update, it would overwrite the new state (which might 102 * already have been used by yet other threads). 103 * To prevent this, the method does not perform the update when a 104 * concurrent modification has been detected, and returns {@code false}. 105 * When this happens, the caller should fetch the new current state, 106 * redo its computation, and call this method again. 107 * 108 * @param expect Current values of the features, as assumed by the caller. 109 * Update will never succeed if the contents of this array does not match 110 * the values returned by {@link #getFeatures()}. 111 * @param update Features's new values. 112 * @return {@code true} if the update was successful, {@code false} 113 * otherwise. 114 * @throws DimensionMismatchException if the length of {@code update} is 115 * not the same as specified in the {@link #Neuron(long,double[]) 116 * constructor}. 117 */ 118 public boolean compareAndSetFeatures(double[] expect, 119 double[] update) { 120 if (update.length != size) { 121 throw new DimensionMismatchException(update.length, size); 122 } 123 124 // Get the internal reference. Note that this must not be a copy; 125 // otherwise the "compareAndSet" below will always fail. 126 final double[] current = features.get(); 127 if (!containSameValues(current, expect)) { 128 // Some other thread already modified the state. 129 return false; 130 } 131 132 if (features.compareAndSet(current, update.clone())) { 133 // The current thread could atomically update the state. 134 return true; 135 } else { 136 // Some other thread came first. 137 return false; 138 } 139 } 140 141 /** 142 * Checks whether the contents of both arrays is the same. 143 * 144 * @param current Current values. 145 * @param expect Expected values. 146 * @throws DimensionMismatchException if the length of {@code expected} 147 * is not the same as specified in the {@link #Neuron(long,double[]) 148 * constructor}. 149 * @return {@code true} if the arrays contain the same values. 150 */ 151 private boolean containSameValues(double[] current, 152 double[] expect) { 153 if (expect.length != size) { 154 throw new DimensionMismatchException(expect.length, size); 155 } 156 157 for (int i = 0; i < size; i++) { 158 if (!Precision.equals(current[i], expect[i])) { 159 return false; 160 } 161 } 162 return true; 163 } 164 165 /** 166 * Prevents proxy bypass. 167 * 168 * @param in Input stream. 169 */ 170 private void readObject(ObjectInputStream in) { 171 throw new IllegalStateException(); 172 } 173 174 /** 175 * Custom serialization. 176 * 177 * @return the proxy instance that will be actually serialized. 178 */ 179 private Object writeReplace() { 180 return new SerializationProxy(identifier, 181 features.get()); 182 } 183 184 /** 185 * Serialization. 186 */ 187 private static class SerializationProxy implements Serializable { 188 /** Serializable. */ 189 private static final long serialVersionUID = 20130207L; 190 /** Features. */ 191 private final double[] features; 192 /** Identifier. */ 193 private final long identifier; 194 195 /** 196 * @param identifier Identifier. 197 * @param features Features. 198 */ 199 SerializationProxy(long identifier, 200 double[] features) { 201 this.identifier = identifier; 202 this.features = features; 203 } 204 205 /** 206 * Custom serialization. 207 * 208 * @return the {@link Neuron} for which this instance is the proxy. 209 */ 210 private Object readResolve() { 211 return new Neuron(identifier, 212 features); 213 } 214 } 215 }