logger.severe("train method. desiredOutputs array is null");
throw new IllegalArgumentException ("desiredOutputs array is null");
    public double train (double[][] inputs,                         double[][] desiredOutputs,                         Monitor    monitor)    {        logger.fine("Network being trained");        stopping = false;        long startTime = System.currentTimeMillis();        // Check size consistencies.        if (inputs == null) {            logger.severe("train method. inputs array is null");        }        final int patternNb = inputs.length;        if (desiredOutputs == null) {            logger.severe("train method. desiredOutputs array is null");        }        // Allocate needed arrays        double[] gottenOutputs = new double[outputSize];        double[] hiddenGrads = new double[hiddenSize];        double[] outputGrads = new double[outputSize];        double[][] hiddenDeltas = createMatrix(hiddenSize, inputSize + 1, 0);        double[][] outputDeltas = createMatrix(outputSize, hiddenSize + 1, 0);        double[] hiddens = new double[hiddenSize];        // Mean Square Error        double mse = 0;        // Notify Monitor we are starting        if (monitor != null) {            // Compute the initial mse            for (int ip = 0; ip < patternNb; ip++) {                run(inputs[ip], hiddens, gottenOutputs);                for (int o = outputSize -1; o >= 0; o--) {                    double out = gottenOutputs[o];                    double dif = desiredOutputs[ip][o] - out;                    mse += (dif * dif);                }            }            mse /= patternNb;            mse = Math.sqrt(mse);            monitor.trainingStarted(0, mse);        }        for (int ie = 0; ie < epochs; ie++) {            // Have we been told to stop ?            if (stopping) {                logger.info("Network stopped.");                break;            }            // Compute the output layer error terms            mse = 0;            // Loop on all input patterns            for (int ip = 0; ip < patternNb; ip++) {                // Run the network with input values and current weights                run(inputs[ip], hiddens, gottenOutputs);                for (int o = outputSize -1; o >= 0; o--) {                    double out = gottenOutputs[o];                    double dif = desiredOutputs[ip][o] - out;                    mse += (dif * dif);                    outputGrads[o] = dif * out * (1 - out);                }                // Compute the hidden layer error terms                for (int h = hiddenSize -1; h >= 0; h--) {                    double sum = 0;                    double hid = hiddens[h];                    for (int o = outputSize -1; o >= 0; o--) {                        sum += (outputGrads[o] * outputWeights[o][h + 1]);                    }                    hiddenGrads[h] = sum * hid * (1 - hid);                }                // Now update the output weights                for (int o = outputSize -1; o >= 0; o--) {                    for (int h = hiddenSize -1; h >= 0; h--) {                        double dw = learningRate * outputGrads[o] * hiddens[h] + momentum * outputDeltas[o][h + 1];                        outputWeights[o][h + 1] += dw;                        outputDeltas[o][h + 1] = dw;                    }                    // Bias                    double dw = learningRate * outputGrads[o] + momentum * outputDeltas[o][0];                    outputWeights[o][0] += dw;                    outputDeltas[o][0] = dw;                }                // And the hidden weights                for (int h = hiddenSize -1; h >= 0; h--) {                    for (int i = inputSize -1; i >= 0; i--) {                        double dw = learningRate * hiddenGrads[h] * inputs[ip][i] + momentum * hiddenDeltas[h][i + 1];                        hiddenWeights[h][i + 1] += dw;                        hiddenDeltas[h][i + 1] = dw;                    }                    // Bias                    double dw = learningRate * hiddenGrads[h] + momentum * hiddenDeltas[h][0];                    hiddenWeights[h][0] += dw;                    hiddenDeltas[h][0] = dw;                }            } // for (int ip = 0; i < patternNb; i++)            // Compute true current mse            mse = 0d;            for (int ip = 0; ip < patternNb; ip++) {                run(inputs[ip], hiddens, gottenOutputs);                for (int o = outputSize -1; o >= 0; o--) {                    double out = gottenOutputs[o];                    double dif = desiredOutputs[ip][o] - out;                    mse += (dif * dif);                }            }            mse /= patternNb;            mse = Math.sqrt(mse);            if (monitor != null) {                monitor.epochEnded(ie, mse);            }            if (mse <= maxError) {                logger.info("Exiting Network training, remaining error limit reached");                logger.info("Remaining error was : " + mse);                break;            }        } // for (int ie = 0; ie < epochs; ie++)        if (logger.isFineEnabled()) {            long stopTime = System.currentTimeMillis();            logger.fine                (String.format                 ("Duration  %,d seconds, %d epochs on %d patterns",                  (stopTime - startTime)/1000, epochs, patternNb));        }        return mse;    }