/** * @param samples The number of input vectors the error is sampled over * @return Mean-squared error of this origin over randomly selected points */ public float[] getError(int samples) { float[] result = new float[getDimensions()]; if (myNode instanceof NEFEnsemble) { NEFEnsemble ensemble = (NEFEnsemble) myNode; VectorGenerator vg = new RandomHypersphereVG(false, 1, 0); float[][] unscaled = vg.genVectors(samples, ensemble.getDimension()); float[][] input = new float[unscaled.length][]; for (int i = 0; i < input.length; i++) { input[i] = MU.prodElementwise(unscaled[i], ensemble.getRadii()); } float[][] idealOutput = NEFUtil.getOutput(this, input, SimulationMode.DIRECT); float[][] actualOutput = NEFUtil.getOutput(this, input, SimulationMode.CONSTANT_RATE); float[][] error = MU.transpose(MU.difference(actualOutput, idealOutput)); for (int i = 0; i < error.length; i++) { result[i] = MU.prod(error[i], error[i]) / error[i].length; } } else { ourLogger.warn( "Can't calculate error of a DecodedOrigin unless it belongs to an NEFEnsemble"); } return result; }
public DecodedOrigin clone(Node node) throws CloneNotSupportedException { if (!(node instanceof DecodableEnsemble)) { throw new CloneNotSupportedException("Error cloning DecodedOrigin: Invalid node type"); } try { DecodableEnsemble de = (DecodableEnsemble) node; DecodedOrigin result = (DecodedOrigin) super.clone(); result.setDecoders(MU.clone(myDecoders)); Function[] functions = new Function[myFunctions.length]; for (int i = 0; i < functions.length; i++) { functions[i] = myFunctions[i].clone(); } result.myFunctions = functions; result.myNodeOrigin = myNodeOrigin; result.myNodes = de.getNodes(); result.myNode = de; result.myOutput = (RealOutput) myOutput.clone(); if (myNoise != null) { result.setNoise(myNoise.clone()); } result.setMode(myMode); return result; } catch (CloneNotSupportedException e) { throw new CloneNotSupportedException("Error cloning DecodedOrigin: " + e.getMessage()); } }
/** @param decoders New decoding vectors (row per Node) */ public void setDecoders(float[][] decoders) { assert MU.isMatrix(decoders); assert myDecoders.length == decoders.length; assert myDecoders[0].length == decoders[0].length; myDecoders = decoders; }
/** * With this constructor decoding vectors are specified by the caller. * * @param node The parent Node * @param name As in other constructor * @param nodes As in other constructor * @param nodeOrigin Name of the Origin on each given node from which output is to be decoded * @param functions As in other constructor * @param decoders Decoding vectors which are scaled by the main output of each Node, and then * summed, to estimate the same function of the ensembles state vector that is defined by the * 'functions' arg. The 'functions' arg is still needed, because in DIRECT SimulationMode, * these functions are used directly. The 'decoders' arg allows the caller to provide decoders * that are generated with non-default methods or parameters (eg an unusual number of singular * values). Must be a matrix with one row per Node and one column per function. * @throws StructuralException If dimensions.length != neurons.length, decoders is not a matrix * (ie all elements with same length), or if the number of columns in decoders is not equal to * the number of functions */ public DecodedOrigin( Node node, String name, Node[] nodes, String nodeOrigin, Function[] functions, float[][] decoders) throws StructuralException { checkFunctionDimensions(functions); if (!MU.isMatrix(decoders)) { throw new StructuralException("Elements of decoders do not all have the same length"); } if (decoders[0].length != functions.length) { throw new StructuralException( "Number of decoding functions and dimension of decoding vectors must be the same"); } if (decoders.length != nodes.length) { throw new StructuralException("Number of decoding vectors and Neurons must be the same"); } myNode = node; myName = name; myNodes = nodes; myNodeOrigin = nodeOrigin; myFunctions = functions; myDecoders = decoders; myMode = SimulationMode.DEFAULT; myIntegrator = new EulerIntegrator(.001f); reset(false); }
private float[] getDynamicDecoder(int i, float input, float startTime, float endTime) { float[] result = myDecoders[i]; if (mySTPDynamicsTemplate != null) { // TODO: could use a NullDynamics here instead of null (to allow nulling in // config tree) // TODO: could recycle a mutable time series here to avoid object creation TimeSeries inputSeries = new TimeSeries1DImpl( new float[] {startTime, endTime}, new float[] {input, input}, Units.UNK); TimeSeries outputSeries = myIntegrator.integrate(mySTPDynamics[i], inputSeries); float scaleFactor = outputSeries.getValues()[outputSeries.getValues().length - 1][0]; mySTPHistory[i] = scaleFactor; result = MU.prod(result, scaleFactor); } return result; }
/** * With this constructor the target is a signal over time rather than a function. * * @param node The parent Node * @param name As in other constructor * @param nodes As in other constructor * @param nodeOrigin Name of the Origin on each given node from which output is to be decoded * @param targetSignal Signal over time that this origin should produce. * @param approximator A LinearApproximator that can be used to approximate new signals as a * weighted sum of the node outputs. */ public DecodedOrigin( Node node, String name, Node[] nodes, String nodeOrigin, TimeSeries targetSignal, LinearApproximator approximator) throws StructuralException { myNode = node; myName = name; myNodes = nodes; myNodeOrigin = nodeOrigin; myFunctions = new FixedSignalFunction[targetSignal.getDimension()]; for (int i = 0; i < targetSignal.getDimension(); i++) // these are only used in direct mode myFunctions[i] = new FixedSignalFunction(targetSignal.getValues(), i); myDecoders = findDecoders(nodes, MU.transpose(targetSignal.getValues()), approximator); myMode = SimulationMode.DEFAULT; myIntegrator = new EulerIntegrator(.001f); reset(false); }