static float visualizeNetwork(NeuralNetwork& neuralNetwork, const Image& referenceImage,
    const std::string& outputPath)
{
    // save the downsampled reference
    Image reference = referenceImage;
    
    reference.setPath(rename(outputPath));
    reference.save();

    NeuronVisualizer visualizer(&neuralNetwork);
    
    Image image = referenceImage;
    
    image.setPath(outputPath);
    
    visualizer.visualizeNeuron(image, 0);

    lucius::util::log("TestVisualization") << "Reference response: "
        << neuralNetwork.runInputs(referenceImage.convertToStandardizedMatrix(
            neuralNetwork.getInputCount(),
            neuralNetwork.getInputBlockingFactor(), image.colorComponents())).toString();
    lucius::util::log("TestVisualization") << "Visualized response: "
        << neuralNetwork.runInputs(image.convertToStandardizedMatrix(
            neuralNetwork.getInputCount(),
            neuralNetwork.getInputBlockingFactor(), image.colorComponents())).toString();
    
    image.save();

    return 0.0f;
}
static void visualizeNeuron(const NeuralNetwork& network, Image& image, unsigned int outputNeuron)
{
	Matrix matrix;

	std::string solverClass = util::KnobDatabase::getKnobValue(
		"NeuronVisualizer::SolverClass", "Differentiable");
	
	if(solverClass == "Differentiable")
	{
		matrix = optimizeWithDerivative(&network, image, outputNeuron);
	}
	else if(solverClass == "NonDifferentiable")
	{
		matrix = optimizeWithoutDerivative(&network, image, outputNeuron);
	}
	else if(solverClass == "Analytical")
	{
		matrix = optimizeAnalytically(&network, image, outputNeuron);
	}
	else
	{
		throw std::runtime_error("Invalid neuron visializer solver class " + solverClass);
	}

	size_t x = 0;
	size_t y = 0;
	
	util::getNearestToSquareFactors(x, y, network.getInputBlockingFactor());

	updateImage(image, matrix, x, y);
}
static float testNetwork(NeuralNetwork& neuralNetwork, const Image& image,
    float noiseMagnitude, size_t iterations, size_t batchSize,
    std::default_random_engine& engine)
{
    float accuracy = 0.0f;

    iterations = std::max(iterations, 1UL);

    lucius::util::log("TestVisualization") << "Testing the accuracy of the trained network.\n";

    for(size_t i = 0; i != iterations; ++i)
    {
        lucius::util::log("TestVisualization") << " Iteration " << i << " out of "
            << iterations << "\n";
        
        ImageVector batch = generateBatch(image, noiseMagnitude,
            batchSize, engine);
        
        Matrix input = batch.convertToStandardizedMatrix(
            neuralNetwork.getInputCount(),
            neuralNetwork.getInputBlockingFactor(), image.colorComponents());
        
        Matrix reference = generateReference(batch);
        
        lucius::util::log("TestVisualization") << "  Input:     " << input.toString();
        lucius::util::log("TestVisualization") << "  Reference: " << reference.toString();
        
        accuracy += neuralNetwork.computeAccuracy(input, reference);
    }
    
    return accuracy * 100.0f / iterations;
}
static void trainNetwork(NeuralNetwork& neuralNetwork, const Image& image,
    float noiseMagnitude, size_t iterations, size_t batchSize,
    std::default_random_engine& engine)
{
    lucius::util::log("TestVisualization") << "Training the network.\n";
    for(size_t i = 0; i != iterations; ++i)
    {
        lucius::util::log("TestVisualization") << " Iteration " << i << " out of "
            << iterations << "\n";
        ImageVector batch = generateBatch(image, noiseMagnitude,
            batchSize, engine);
        
        Matrix input = batch.convertToStandardizedMatrix(
            neuralNetwork.getInputCount(),
            neuralNetwork.getInputBlockingFactor(), image.colorComponents());
        
        Matrix reference = generateReference(batch);
        
        lucius::util::log("TestVisualization") << "  Input:     " << input.toString();
        lucius::util::log("TestVisualization") << "  Reference: " << reference.toString();
        
        neuralNetwork.train(input, reference);
    }
}