static Matrix optimizeWithDerivative(const NeuralNetwork* network,
	const Image& image, unsigned int neuron)
{
	unsigned int iterations = util::KnobDatabase::getKnobValue(
		"NeuronVisualizer::SolverIterations", 1);
	float range = util::KnobDatabase::getKnobValue("NeuronVisualizer::InputRange", 0.01f);

	float  bestCost = std::numeric_limits<float>::max();
	Matrix bestInputs;

	util::log("NeuronVisualizer") << "Searching for lowest cost inputs...\n";

	for(unsigned int iteration = 0; iteration < iterations; ++iteration)
	{
		util::log("NeuronVisualizer") << " Iteration " << iteration << "\n";

		auto randomInputs = generateRandomImage(network, image, iteration, range);

		float newCost = std::numeric_limits<float>::max();
		auto newInputs = optimizeWithDerivative(newCost, network, randomInputs, neuron);

		if(newCost < bestCost)
		{
			bestInputs = newInputs;
			bestCost   = newCost;
			util::log("NeuronVisualizer") << " updated best cost: " << bestCost << "\n";
		}

		range /= 2.0f;
	}
	
	return bestInputs;
}
static ImageVector generateBatch(const Image& image, float noiseMagnitude,
    size_t batchSize, std::default_random_engine& engine)
{
    ImageVector images;

    std::bernoulli_distribution distribution(0.5f);

    for(size_t i = 0; i != batchSize; ++i)
    {
        bool generateRandom = distribution(engine);

        if(generateRandom)
        {
            images.push_back(generateRandomImage(
                image.y(), image.x(), image.colorComponents(), engine));
        }
        else
        {
            images.push_back(addRandomNoiseToImage(image,
                noiseMagnitude, engine));
        }
    }
    
    return images;
}
static Matrix optimizeWithoutDerivative(const NeuralNetwork* network,
	const Image& image, unsigned int neuron)
{
	Matrix bestSoFar = generateRandomImage(network, image, 0, 0.00f);
	float  bestCost  = computeCost(network, neuron, bestSoFar);
	
	std::string solverType = util::KnobDatabase::getKnobValue(
		"NeuronVisualizer::SolverType", "SimulatedAnnealingSolver");
	
	auto solver =
		optimizer::GeneralNondifferentiableSolverFactory::create(solverType);
	
	assert(solver != nullptr);

	CostFunction costFunction(network, neuron, bestCost);

	bestCost = solver->solve(bestSoFar, costFunction);
	
	delete solver;
	
	return bestSoFar;
}