<commit_before>
#include <iostream>
#include <fstream>
#include "core/opencl.h"
#include "core/vector.h"
#include "nn/network.h"
#include "nn/trainer.h"
#include "nn/errorCriterion.h"
#include "nn/classificationEvaluator.h"
#include "optimizers/gradientDescent.h"
#include "mnistDataset.h"

using namespace nnFit;

template<typename T>
void assertEquals(const std::vector<T> &x, const T *data, size_t size) {
    assert(x.size() == size);
    for (size_t i = 0; i < std::min(x.size(), size); ++i) {
        assert(x[i] == data[i]);
    }
}

template<typename T>
void assertEquals(const Vector &x, std::initializer_list<T> y) {
    std::vector<T> dest;
    x.copy(dest);
    x.device().queue().finish();
    assertEquals(dest, y.begin(), y.size());
}

template<typename T>
void assertEquals(const Vector &x, std::vector<T> y) {
    std::vector<T> dest;
    x.copy(dest);
    x.device().queue().finish();
    assertEquals(dest, y.data(), y.size());
}

static Device selectDevice() {
    auto devices = Device::findGPUs();
    for (auto &device : devices) {
        if (device.vendor().find("NVIDIA") != std::string::npos)
            return std::move(device);
        else if (device.vendor().find("AMD") != std::string::npos)
            return std::move(device);
    }
    if (devices.empty())
        return std::move(Device::findAll()[0]);
    return std::move(devices[0]);
}

void testVectors(Device &device) {
    Vector x(device, {1.0f,2.0f,3.0f,4.0f});
    Vector y(device, {0.0f,1.0f,5.0f,10.0f});
    Vector dest(device, x.size());

    add(dest, x, y);
    assertEquals(dest, {1.0f,3.0f,8.0f,14.0f});
    sub(dest, x, y);
    assertEquals(dest, {1.0f,1.0f,-2.0f,-6.0f});
    mul(dest, x, 2.0f);
    assertEquals(dest, {2.0f,4.0f,6.0f,8.0f});
    div(dest, x, 2.0f);
    assertEquals(dest, {0.5f,1.0f,1.5f,2.0f});
    elementwiseMul(dest, x, y);
    assertEquals(dest, {0.0f,2.0f,15.0f,40.0f});

    x.copy(dest);
    assertEquals(dest, {1.0f,2.0f,3.0f,4.0f});
    add(dest, y);
    assertEquals(dest, {1.0f,3.0f,8.0f,14.0f});

    for (int i = 0; i < 100; ++i) {
        dest.zeros();
        assertEquals(dest, {0.0f,0.0f,0.0f,0.0f});
        dest.ones();
        assertEquals(dest, {1.0f,1.0f,1.0f,1.0f});
    }
}

void testSum(Device &device) {
    Vector x(device, {1.0f,2.0f,3.0f,4.0f});
    Vector single(device, 1);
    partialSum(single, x);
    assertEquals(single, {10.0f});

    Vector y(device, {1.0f,2.0f,3.0f,4.0f,5.0f,6.0f,7.0f,8.0f,9.0f,10.0f});
    partialSum(x, y);
    assertEquals(x, {6.0f, 15.0f, 24.0f, 10.0f});

    Vector z(device, {1.0f,2.0f,3.0f,4.0f,5.0f,6.0f,7.0f,8.0f,9.0f,10.0f,11.0f,12.0f});
    partialSum(x, z);
    assertEquals(x, {6.0f, 15.0f, 24.0f, 33.0f});
}

void testBLAS(Device &device) {
    // Matrix identity
    Matrix eye(device, 4, 4);
    eye.identity();
    assertEquals(eye, {1.0f,0.0f,0.0f,0.0f, 0.0f,1.0f,0.0f,0.0f, 0.0f,0.0f,1.0f,0.0f, 0.0f,0.0f,0.0f,1.0f});

    // Matrix by vector
    {
        Matrix m(device, 400, 400);
        m.identity();
        Vector v(device, 400);
        v.fill(42.0f);
        Vector result(device, 400);
        mul(result, m, v);
        assertEquals(result, std::vector<float>(result.size(), 42.0f));
    }
    {
        Matrix m(device, 10, 400);
        m.ones();
        Vector v(device, 400);
        v.fill(2.0f);
        Vector result(device, 10);
        mul(result, m, v);
    }
}

void testBooleanOperations(Device &device) {
    Vector x(device, 4, ValueType(ValueType::Uint8));
    x.write({ uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(0) });
    Vector single(device, 1, ValueType(ValueType::Uint32));
    partialTrueCount(single, x);
    assertEquals(single, { uint32_t(2) });

    Vector y(device, 10, ValueType(ValueType::Uint8));
    y.write({ uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0) });
    Vector result(device, 4, ValueType(ValueType::Uint32));
    partialTrueCount(result, y);
    assertEquals(result, { uint32_t(2), uint32_t(1), uint32_t(2), uint32_t(0) });

    Vector z(device, 12, ValueType(ValueType::Uint8));
    z.write({ uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0), uint8_t(1), uint8_t(0) });
    partialTrueCount(result, z);
    assertEquals(result, { uint32_t(2), uint32_t(1), uint32_t(2), uint32_t(1) });
}

void testLayers(Device &device) {
    Network net(device);
    Layer layer(device, 2, 2, Layer::Linear);
    layer.neuronWeights().write({ 1.0f, 1.0f, 2.0f, 0.5f });
    layer.neuronBiases().write({ 0.0f, 1.0f });
    Vector input(device, { 1.0f, 2.0f });
    auto &output = layer.predict(net.context(), input);
    assertEquals(output, { 3.0f, 4.0f });
}

void assertEquals(const Vector &x, bool y) {
    std::vector<float> dest;
    x.copy(dest);
    x.device().queue().finish();
    assert(dest[0] > 0.5 == y);
};

void testLogicGates(Device &device) {
    Vector t00(device, {0.0f, 0.0f});
    Vector t10(device, {1.0f, 0.0f});
    Vector t01(device, {0.0f, 1.0f});
    Vector t11(device, {1.0f, 1.0f});

    auto assertEquals = [] (const Vector &x, bool y) {
        std::vector<float> dest;
        x.copy(dest);
        x.device().queue().finish();
        assert(dest[0] > 0.5 == y);
    };

    {
        // OR gate
        Network net(device);
        std::unique_ptr<Layer> layer(new Layer(device, 1, 2, Layer::Sigmoid));
        layer->neuronWeights().write({ 20.0f, 20.0f });
        layer->neuronBiases().write({ -10.0f });
        net.inputLayer(2).add(std::move(layer));
        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), true);
        assertEquals(net.predict(t01), true);
        assertEquals(net.predict(t11), true);
    }

    {
        // AND gate
        Network net(device);
        std::unique_ptr<Layer> layer(new Layer(device, 1, 2, Layer::Sigmoid));
        layer->neuronWeights().write({ 10.0f, 10.0f });
        layer->neuronBiases().write({ -10.0f });
        net.inputLayer(2).add(std::move(layer));
        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), false);
        assertEquals(net.predict(t01), false);
        assertEquals(net.predict(t11), true);
    }
}

void testBackprop(Device &device) {
    Network net(device);
    Layer lastLayer(device, 2, 3, Layer::Sigmoid);
    lastLayer.neuronWeights().write({ 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f });
    lastLayer.errorTerm().write({ 2.5f, 0.75f });
    Layer layer(device, 3, 1, Layer::Sigmoid);
    layer.derivative().write({ 1.0f, 1.0f, 1.0f });
    layer.computeErrorTerm(net.context(), lastLayer);
    assertEquals(layer.errorTerm(), { 5.75f, 9.0f, 12.25f });
}

void testTrainer(Device &device) {
    // Training set
    Matrix inputs(device, 4, 2, { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f });
    Matrix outputs(device, 4, 1, { 0.0f, 1.0f, 1.0f, 0.0f });

    SimpleDataset data(inputs, outputs);

    // Test set
    Vector t00(device, {0.0f, 0.0f});
    Vector t10(device, {1.0f, 0.0f});
    Vector t01(device, {0.0f, 1.0f});
    Vector t11(device, {1.0f, 1.0f});

    {
        // 2 - 3 Sigmoid - 1 Sigmoid XOR NN
        Network net(device);
        net.inputLayer(2).add(std::unique_ptr<Layer>(new Layer(device, 3, 2, Layer::Sigmoid))).add(std::unique_ptr<Layer>(new Layer(device, 1, 3, Layer::Sigmoid)));
        net.init(/* seed= */12);

        GradientDescent opt(device, 3.0);
        MSECriterion criterion;
        Trainer trainer(net, criterion, data);
        float previousError = 0.0f;
        trainer.afterIteration = [&] (size_t i, float error) {
            if (i != 0) {
                // Make sure the error is going down
                assert(previousError >= error);
            }
            previousError = error;
        };
        trainer.gradientDescent(opt, 300);

        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), true);
        assertEquals(net.predict(t01), true);
        assertEquals(net.predict(t00), false);
    }

    {
        // 2 - 5 ReLU - 1 Sigmoid XOR NN
        Network net(device);
        net.inputLayer(2).add(std::unique_ptr<Layer>(new Layer(device, 5, 2, Layer::RectifiedLinearUnit))).add(std::unique_ptr<Layer>(new Layer(device, 1, 5, Layer::Sigmoid)));
        net.init(/* seed= */12);

        GradientDescent opt(device, 0.3);
        CrossEntropyCriterion criterion;
        Trainer trainer(net, criterion, data);
        trainer.gradientDescent(opt, 300);

        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), true);
        assertEquals(net.predict(t01), true);
        assertEquals(net.predict(t00), false);
    }
}

void testMNIST(Device &device) {
    std::cout << "Loading MNIST dataset...\n";

    MNIST trainingSet(device);
    if (trainingSet.load("train-images.idx3-ubyte", "train-labels.idx1-ubyte"))
        return;

    MNIST testSet(device);
    if (testSet.load("t10k-images.idx3-ubyte", "t10k-labels.idx1-ubyte"))
        return;

    std::cout << "Finished loading MNIST dataset\n";

    Network net(device);
    size_t imageSize = trainingSet.imageWidth()*trainingSet.imageHeight();
    net.inputLayer(imageSize);
    net.add(std::unique_ptr<Layer>(new Layer(device, 400, imageSize, Layer::RectifiedLinearUnit)));
    net.add(std::unique_ptr<Layer>(new Layer(device, 10, 400, Layer::Sigmoid)));
    uint32_t seed = 12;
    std::cout << "Random initialization using seed '" << seed << "'\n";
    net.init(seed);
    std::cout << "Tuning network perfomance: \n";
    net.tune();

    std::cout << "Training network..\n";
    GradientDescent opt(device, 0.3);
    CrossEntropyCriterion criterion;
    Trainer trainer(net, criterion, trainingSet);
    ClassificationEvaluator evaluator(testSet);
    trainer.reshuffleIndices = true;
    trainer.profile = true;
    // Train & evaluate
    trainer.afterIteration = [&] (size_t i, float cost) {
        std::cout << "Evaluating perfomance after " << (i+1) << " iteration(s):\n";
        auto result = evaluator.evaluate(net);
        std::cout << "Cost (of last batch) " << cost << ", test set accuracy: " << result.correctPredictions << "/" << result.count << ", " << result.percentageOfCorrectPredictions() << "%\n";
    };
    trainer.miniBatchGradientDescent(opt, 30, 50);
}

int main(int argc, const char * argv[]) {
    auto device = selectDevice();
    device.init();
    std::cout << "Using device '" << device.name() << "' by '" << device.vendor() << "', version '" << device.version() << "'\n";
    CommandQueue queue(device);
    device.queue(queue);

    testVectors(device);
    testSum(device);
    testBLAS(device);
    testBooleanOperations(device);
    testLayers(device);
    testLogicGates(device);
    testBackprop(device);
    testTrainer(device);
    testMNIST(device);

    return 0;
}
<commit_msg>add unittests that covers the non float4 version of matrix-vector multiplication<commit_after>
#include <iostream>
#include <fstream>
#include "core/opencl.h"
#include "core/vector.h"
#include "nn/network.h"
#include "nn/trainer.h"
#include "nn/errorCriterion.h"
#include "nn/classificationEvaluator.h"
#include "optimizers/gradientDescent.h"
#include "mnistDataset.h"

using namespace nnFit;

template<typename T>
void assertEquals(const std::vector<T> &x, const T *data, size_t size) {
    assert(x.size() == size);
    for (size_t i = 0; i < std::min(x.size(), size); ++i) {
        assert(x[i] == data[i]);
    }
}

template<typename T>
void assertEquals(const Vector &x, std::initializer_list<T> y) {
    std::vector<T> dest;
    x.copy(dest);
    x.device().queue().finish();
    assertEquals(dest, y.begin(), y.size());
}

template<typename T>
void assertEquals(const Vector &x, std::vector<T> y) {
    std::vector<T> dest;
    x.copy(dest);
    x.device().queue().finish();
    assertEquals(dest, y.data(), y.size());
}

static Device selectDevice() {
    auto devices = Device::findGPUs();
    for (auto &device : devices) {
        if (device.vendor().find("NVIDIA") != std::string::npos)
            return std::move(device);
        else if (device.vendor().find("AMD") != std::string::npos)
            return std::move(device);
    }
    if (devices.empty())
        return std::move(Device::findAll()[0]);
    return std::move(devices[0]);
}

void testVectors(Device &device) {
    Vector x(device, {1.0f,2.0f,3.0f,4.0f});
    Vector y(device, {0.0f,1.0f,5.0f,10.0f});
    Vector dest(device, x.size());

    add(dest, x, y);
    assertEquals(dest, {1.0f,3.0f,8.0f,14.0f});
    sub(dest, x, y);
    assertEquals(dest, {1.0f,1.0f,-2.0f,-6.0f});
    mul(dest, x, 2.0f);
    assertEquals(dest, {2.0f,4.0f,6.0f,8.0f});
    div(dest, x, 2.0f);
    assertEquals(dest, {0.5f,1.0f,1.5f,2.0f});
    elementwiseMul(dest, x, y);
    assertEquals(dest, {0.0f,2.0f,15.0f,40.0f});

    x.copy(dest);
    assertEquals(dest, {1.0f,2.0f,3.0f,4.0f});
    add(dest, y);
    assertEquals(dest, {1.0f,3.0f,8.0f,14.0f});

    for (int i = 0; i < 100; ++i) {
        dest.zeros();
        assertEquals(dest, {0.0f,0.0f,0.0f,0.0f});
        dest.ones();
        assertEquals(dest, {1.0f,1.0f,1.0f,1.0f});
    }
}

void testSum(Device &device) {
    Vector x(device, {1.0f,2.0f,3.0f,4.0f});
    Vector single(device, 1);
    partialSum(single, x);
    assertEquals(single, {10.0f});

    Vector y(device, {1.0f,2.0f,3.0f,4.0f,5.0f,6.0f,7.0f,8.0f,9.0f,10.0f});
    partialSum(x, y);
    assertEquals(x, {6.0f, 15.0f, 24.0f, 10.0f});

    Vector z(device, {1.0f,2.0f,3.0f,4.0f,5.0f,6.0f,7.0f,8.0f,9.0f,10.0f,11.0f,12.0f});
    partialSum(x, z);
    assertEquals(x, {6.0f, 15.0f, 24.0f, 33.0f});
}

void testBLAS(Device &device) {
    // Matrix identity
    Matrix eye(device, 4, 4);
    eye.identity();
    assertEquals(eye, {1.0f,0.0f,0.0f,0.0f, 0.0f,1.0f,0.0f,0.0f, 0.0f,0.0f,1.0f,0.0f, 0.0f,0.0f,0.0f,1.0f});

    // Matrix by vector
    {
        Matrix m(device, 400, 400);
        m.identity();
        Vector v(device, 400);
        v.fill(42.0f);
        Vector result(device, 400);
        mul(result, m, v);
        assertEquals(result, std::vector<float>(result.size(), 42.0f));
    }
    {
        Matrix m(device, 10, 400);
        m.ones();
        Vector v(device, 400);
        v.fill(2.0f);
        Vector result(device, 10);
        mul(result, m, v);
    }
    {
        // Force the use of a 'float' version instead of 'float4'
        Matrix m(device, 5, 3);
        m.ones();
        Vector v(device, 3);
        v.fill(2.0f);
        Vector result(device, 5);
        mul(result, m, v);
        assertEquals(result, { 6.0f, 6.0f, 6.0f, 6.0f, 6.0f });
    }
}

void testBooleanOperations(Device &device) {
    Vector x(device, 4, ValueType(ValueType::Uint8));
    x.write({ uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(0) });
    Vector single(device, 1, ValueType(ValueType::Uint32));
    partialTrueCount(single, x);
    assertEquals(single, { uint32_t(2) });

    Vector y(device, 10, ValueType(ValueType::Uint8));
    y.write({ uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0) });
    Vector result(device, 4, ValueType(ValueType::Uint32));
    partialTrueCount(result, y);
    assertEquals(result, { uint32_t(2), uint32_t(1), uint32_t(2), uint32_t(0) });

    Vector z(device, 12, ValueType(ValueType::Uint8));
    z.write({ uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0), uint8_t(1), uint8_t(1), uint8_t(1), uint8_t(0), uint8_t(0), uint8_t(1), uint8_t(0) });
    partialTrueCount(result, z);
    assertEquals(result, { uint32_t(2), uint32_t(1), uint32_t(2), uint32_t(1) });
}

void testLayers(Device &device) {
    Network net(device);
    Layer layer(device, 2, 2, Layer::Linear);
    layer.neuronWeights().write({ 1.0f, 1.0f, 2.0f, 0.5f });
    layer.neuronBiases().write({ 0.0f, 1.0f });
    Vector input(device, { 1.0f, 2.0f });
    auto &output = layer.predict(net.context(), input);
    assertEquals(output, { 3.0f, 4.0f });
}

void assertEquals(const Vector &x, bool y) {
    std::vector<float> dest;
    x.copy(dest);
    x.device().queue().finish();
    assert(dest[0] > 0.5 == y);
};

void testLogicGates(Device &device) {
    Vector t00(device, {0.0f, 0.0f});
    Vector t10(device, {1.0f, 0.0f});
    Vector t01(device, {0.0f, 1.0f});
    Vector t11(device, {1.0f, 1.0f});

    auto assertEquals = [] (const Vector &x, bool y) {
        std::vector<float> dest;
        x.copy(dest);
        x.device().queue().finish();
        assert(dest[0] > 0.5 == y);
    };

    {
        // OR gate
        Network net(device);
        std::unique_ptr<Layer> layer(new Layer(device, 1, 2, Layer::Sigmoid));
        layer->neuronWeights().write({ 20.0f, 20.0f });
        layer->neuronBiases().write({ -10.0f });
        net.inputLayer(2).add(std::move(layer));
        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), true);
        assertEquals(net.predict(t01), true);
        assertEquals(net.predict(t11), true);
    }

    {
        // AND gate
        Network net(device);
        std::unique_ptr<Layer> layer(new Layer(device, 1, 2, Layer::Sigmoid));
        layer->neuronWeights().write({ 10.0f, 10.0f });
        layer->neuronBiases().write({ -10.0f });
        net.inputLayer(2).add(std::move(layer));
        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), false);
        assertEquals(net.predict(t01), false);
        assertEquals(net.predict(t11), true);
    }
}

void testBackprop(Device &device) {
    Network net(device);
    Layer lastLayer(device, 2, 3, Layer::Sigmoid);
    lastLayer.neuronWeights().write({ 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f });
    lastLayer.errorTerm().write({ 2.5f, 0.75f });
    Layer layer(device, 3, 1, Layer::Sigmoid);
    layer.derivative().write({ 1.0f, 1.0f, 1.0f });
    layer.computeErrorTerm(net.context(), lastLayer);
    assertEquals(layer.errorTerm(), { 5.75f, 9.0f, 12.25f });
}

void testTrainer(Device &device) {
    // Training set
    Matrix inputs(device, 4, 2, { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f });
    Matrix outputs(device, 4, 1, { 0.0f, 1.0f, 1.0f, 0.0f });

    SimpleDataset data(inputs, outputs);

    // Test set
    Vector t00(device, {0.0f, 0.0f});
    Vector t10(device, {1.0f, 0.0f});
    Vector t01(device, {0.0f, 1.0f});
    Vector t11(device, {1.0f, 1.0f});

    {
        // 2 - 3 Sigmoid - 1 Sigmoid XOR NN
        Network net(device);
        net.inputLayer(2).add(std::unique_ptr<Layer>(new Layer(device, 3, 2, Layer::Sigmoid))).add(std::unique_ptr<Layer>(new Layer(device, 1, 3, Layer::Sigmoid)));
        net.init(/* seed= */12);

        GradientDescent opt(device, 3.0);
        MSECriterion criterion;
        Trainer trainer(net, criterion, data);
        float previousError = 0.0f;
        trainer.afterIteration = [&] (size_t i, float error) {
            if (i != 0) {
                // Make sure the error is going down
                assert(previousError >= error);
            }
            previousError = error;
        };
        trainer.gradientDescent(opt, 300);

        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), true);
        assertEquals(net.predict(t01), true);
        assertEquals(net.predict(t00), false);
    }

    {
        // 2 - 5 ReLU - 1 Sigmoid XOR NN
        Network net(device);
        net.inputLayer(2).add(std::unique_ptr<Layer>(new Layer(device, 5, 2, Layer::RectifiedLinearUnit))).add(std::unique_ptr<Layer>(new Layer(device, 1, 5, Layer::Sigmoid)));
        net.init(/* seed= */12);

        GradientDescent opt(device, 0.3);
        CrossEntropyCriterion criterion;
        Trainer trainer(net, criterion, data);
        trainer.gradientDescent(opt, 300);

        assertEquals(net.predict(t00), false);
        assertEquals(net.predict(t10), true);
        assertEquals(net.predict(t01), true);
        assertEquals(net.predict(t00), false);
    }
}

void testMNIST(Device &device) {
    std::cout << "Loading MNIST dataset...\n";

    MNIST trainingSet(device);
    if (trainingSet.load("train-images.idx3-ubyte", "train-labels.idx1-ubyte"))
        return;

    MNIST testSet(device);
    if (testSet.load("t10k-images.idx3-ubyte", "t10k-labels.idx1-ubyte"))
        return;

    std::cout << "Finished loading MNIST dataset\n";

    Network net(device);
    size_t imageSize = trainingSet.imageWidth()*trainingSet.imageHeight();
    net.inputLayer(imageSize);
    net.add(std::unique_ptr<Layer>(new Layer(device, 400, imageSize, Layer::RectifiedLinearUnit)));
    net.add(std::unique_ptr<Layer>(new Layer(device, 10, 400, Layer::Sigmoid)));
    uint32_t seed = 12;
    std::cout << "Random initialization using seed '" << seed << "'\n";
    net.init(seed);
    std::cout << "Tuning network perfomance: \n";
    net.tune();

    std::cout << "Training network..\n";
    GradientDescent opt(device, 0.3);
    CrossEntropyCriterion criterion;
    Trainer trainer(net, criterion, trainingSet);
    ClassificationEvaluator evaluator(testSet);
    trainer.reshuffleIndices = true;
    trainer.profile = true;
    // Train & evaluate
    trainer.afterIteration = [&] (size_t i, float cost) {
        std::cout << "Evaluating perfomance after " << (i+1) << " iteration(s):\n";
        auto result = evaluator.evaluate(net);
        std::cout << "Cost (of last batch) " << cost << ", test set accuracy: " << result.correctPredictions << "/" << result.count << ", " << result.percentageOfCorrectPredictions() << "%\n";
    };
    trainer.miniBatchGradientDescent(opt, 30, 50);
}

int main(int argc, const char * argv[]) {
    auto device = selectDevice();
    device.init();
    std::cout << "Using device '" << device.name() << "' by '" << device.vendor() << "', version '" << device.version() << "'\n";
    CommandQueue queue(device);
    device.queue(queue);

    testVectors(device);
    testSum(device);
    testBLAS(device);
    testBooleanOperations(device);
    testLayers(device);
    testLogicGates(device);
    testBackprop(device);
    testTrainer(device);
    testMNIST(device);

    return 0;
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkThreshold.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkThreshold.h"

// Construct with lower threshold=0, upper threshold=1, and threshold
// function=upper AllScalars=1.
vtkThreshold::vtkThreshold()
{
  this->LowerThreshold = 0.0;
  this->UpperThreshold = 1.0;
  this->AllScalars = 1;
  this->ThresholdFunction = &vtkThreshold::Upper;
}

// Description:
// Criterion is cells whose scalars are less or equal to lower threshold.
void vtkThreshold::ThresholdByLower(float lower)
{
  if ( this->LowerThreshold != lower )
    {
    this->LowerThreshold = lower;
    this->ThresholdFunction = &vtkThreshold::Lower;
    this->Modified();
    }
}

// Description:
// Criterion is cells whose scalars are greater or equal to upper threshold.
void vtkThreshold::ThresholdByUpper(float upper)
{
  if ( this->UpperThreshold != upper )
    {
    this->UpperThreshold = upper;
    this->ThresholdFunction = &vtkThreshold::Upper;
    this->Modified();
    }
}

// Description:
// Criterion is cells whose scalars are between lower and upper thresholds.
void vtkThreshold::ThresholdBetween(float lower, float upper)
{
  if ( this->LowerThreshold != lower || this->UpperThreshold != upper )
    {
    this->LowerThreshold = lower;
    this->UpperThreshold = upper;
    this->ThresholdFunction = &vtkThreshold::Between;
    this->Modified();
    }
}

void vtkThreshold::Execute()
{
  int cellId, newCellId;
  vtkIdList *cellPts, *pointMap;
  vtkIdList *newCellPts = vtkIdList::New();
  vtkCell *cell;
  vtkPoints *newPoints;
  int i, ptId, newId, numPts, numCellPts;
  float *x;
  vtkDataSet *input=(vtkDataSet *)this->Input;
  vtkUnstructuredGrid *output= this->GetOutput();
  vtkPointData *pd=input->GetPointData(), *outPD=output->GetPointData();
  vtkCellData *cd=input->GetCellData(), *outCD=output->GetCellData();
  vtkScalars *pointScalars=pd->GetScalars(), *cellScalars=cd->GetScalars();
  int keepCell, usePointScalars;

  vtkDebugMacro(<< "Executing threshold filter");

  outPD = output->GetPointData();
  outPD->CopyAllocate(pd);
  outCD = output->GetCellData();
  outCD->CopyAllocate(pd);

  if ( !(pointScalars || cellScalars) )
    {
    vtkErrorMacro(<<"No scalar data to threshold");
    return;
    }

  numPts = input->GetNumberOfPoints();
  output->Allocate(input->GetNumberOfCells());
  newPoints = vtkPoints::New();
  newPoints->Allocate(numPts);

  pointMap = vtkIdList::New(); //maps old point ids into new
  pointMap->SetNumberOfIds(numPts);
  for (i=0; i < numPts; i++) pointMap->SetId(i,-1);

  // Determine which scalar data to use for thresholding
  if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_DEFAULT )
    {
    if ( pointScalars != NULL) usePointScalars = 1;
    else usePointScalars = 0;
    }
  else if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_USE_POINT_DATA )
    {
    usePointScalars = 1;
    }
  else
    {
    usePointScalars = 0;
    }

  // Check on scalar consistency
  if ( usePointScalars && !pointScalars )
    {
    vtkErrorMacro(<<"Can't use point scalars because there are none");
    return;
    }
  else if ( !usePointScalars && !cellScalars )
    {
    vtkErrorMacro(<<"Can't use cell scalars because there are none");
    return;
    }

  // Check that the scalars of each cell satisfy the threshold criterion
  for (cellId=0; cellId < input->GetNumberOfCells(); cellId++)
    {
    cell = input->GetCell(cellId);
    cellPts = cell->GetPointIds();
    numCellPts = cell->GetNumberOfPoints();

    if ( usePointScalars )
      {
      if (this->AllScalars)
	{
	keepCell = 1;
	for ( i=0; keepCell && (i < numCellPts); i++)
	  {
	  ptId = cellPts->GetId(i);
	  keepCell =
	    (this->*(this->ThresholdFunction))(pointScalars->GetScalar(ptId));
	  }
	}
      else
	{
	keepCell = 0;
	for ( i=0; (!keepCell) && (i < numCellPts); i++)
	  {
	  ptId = cellPts->GetId(i);
	  keepCell =
	    (this->*(this->ThresholdFunction))(pointScalars->GetScalar(ptId));
	  }
	}
      }
    else //use cell scalars
      {
      keepCell = (this->*(this->ThresholdFunction))(cellScalars->GetScalar(cellId));
      }

    if ( keepCell ) // satisfied thresholding
      {
      for (i=0; i < numCellPts; i++)
        {
        ptId = cellPts->GetId(i);
        if ( (newId = pointMap->GetId(ptId)) < 0 )
          {
          x = input->GetPoint(ptId);
          newId = newPoints->InsertNextPoint(x);
          pointMap->SetId(ptId,newId);
          outPD->CopyData(pd,ptId,newId);
          }
        newCellPts->InsertId(i,newId);
        }
      newCellId = output->InsertNextCell(cell->GetCellType(),*newCellPts);
      outCD->CopyData(cd,cellId,newCellId);
      newCellPts->Reset();
      } // satisfied thresholding
    } // for all cells

  vtkDebugMacro(<< "Extracted " << output->GetNumberOfCells()
                << " number of cells.");

  // now clean up / update ourselves
  pointMap->Delete();
  newCellPts->Delete();

  output->SetPoints(newPoints);
  newPoints->Delete();

  output->Squeeze();
}

// Description:
// Return the method for manipulating scalar data as a string.
char *vtkThreshold::GetAttributeModeAsString(void)
{
  if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_DEFAULT )
    {
    return "Default";
    }
  else if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_USE_POINT_DATA )
    {
    return "UsePointData";
    }
  else
    {
    return "UseCellData";
    }
}

void vtkThreshold::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkDataSetToUnstructuredGridFilter::PrintSelf(os,indent);

  os << indent << "Attribute Mode: " << this->GetAttributeModeAsString() << endl;
  os << indent << "All Scalars: " << this->AllScalars << "\n";;
  if ( this->ThresholdFunction == &vtkThreshold::Upper )
    os << indent << "Threshold By Upper\n";

  else if ( this->ThresholdFunction == &vtkThreshold::Lower )
    os << indent << "Threshold By Lower\n";

  else if ( this->ThresholdFunction == &vtkThreshold::Between )
    os << indent << "Threshold Between\n";

  os << indent << "Lower Threshold: " << this->LowerThreshold << "\n";;
  os << indent << "Upper Threshold: " << this->UpperThreshold << "\n";;
}
<commit_msg>ERR: AttriburteMOde was not initialized.  outCD->CopyAllocate(pd) changed to  outCD->CopyAllocate(cd)<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkThreshold.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1993-1998 Ken Martin, Will Schroeder, Bill Lorensen.

This software is copyrighted by Ken Martin, Will Schroeder and Bill Lorensen.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files. This copyright specifically does
not apply to the related textbook "The Visualization Toolkit" ISBN
013199837-4 published by Prentice Hall which is covered by its own copyright.

The authors hereby grant permission to use, copy, and distribute this
software and its documentation for any purpose, provided that existing
copyright notices are retained in all copies and that this notice is included
verbatim in any distributions. Additionally, the authors grant permission to
modify this software and its documentation for any purpose, provided that
such modifications are not distributed without the explicit consent of the
authors and that existing copyright notices are retained in all copies. Some
of the algorithms implemented by this software are patented, observe all
applicable patent law.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
#include "vtkThreshold.h"

// Construct with lower threshold=0, upper threshold=1, and threshold
// function=upper AllScalars=1.
vtkThreshold::vtkThreshold()
{
  this->LowerThreshold = 0.0;
  this->UpperThreshold = 1.0;
  this->AllScalars = 1;
  this->AttributeMode = VTK_ATTRIBUTE_MODE_USE_POINT_DATA;
  this->ThresholdFunction = &vtkThreshold::Upper;
}

// Description:
// Criterion is cells whose scalars are less or equal to lower threshold.
void vtkThreshold::ThresholdByLower(float lower)
{
  if ( this->LowerThreshold != lower )
    {
    this->LowerThreshold = lower;
    this->ThresholdFunction = &vtkThreshold::Lower;
    this->Modified();
    }
}

// Description:
// Criterion is cells whose scalars are greater or equal to upper threshold.
void vtkThreshold::ThresholdByUpper(float upper)
{
  if ( this->UpperThreshold != upper )
    {
    this->UpperThreshold = upper;
    this->ThresholdFunction = &vtkThreshold::Upper;
    this->Modified();
    }
}

// Description:
// Criterion is cells whose scalars are between lower and upper thresholds.
void vtkThreshold::ThresholdBetween(float lower, float upper)
{
  if ( this->LowerThreshold != lower || this->UpperThreshold != upper )
    {
    this->LowerThreshold = lower;
    this->UpperThreshold = upper;
    this->ThresholdFunction = &vtkThreshold::Between;
    this->Modified();
    }
}

void vtkThreshold::Execute()
{
  int cellId, newCellId;
  vtkIdList *cellPts, *pointMap;
  vtkIdList *newCellPts = vtkIdList::New();
  vtkCell *cell;
  vtkPoints *newPoints;
  int i, ptId, newId, numPts, numCellPts;
  float *x;
  vtkDataSet *input=(vtkDataSet *)this->Input;
  vtkUnstructuredGrid *output= this->GetOutput();
  vtkPointData *pd=input->GetPointData(), *outPD=output->GetPointData();
  vtkCellData *cd=input->GetCellData(), *outCD=output->GetCellData();
  vtkScalars *pointScalars=pd->GetScalars(), *cellScalars=cd->GetScalars();
  int keepCell, usePointScalars;

  vtkDebugMacro(<< "Executing threshold filter");

  outPD = output->GetPointData();
  outPD->CopyAllocate(pd);
  outCD = output->GetCellData();
  outCD->CopyAllocate(cd);

  if ( !(pointScalars || cellScalars) )
    {
    vtkErrorMacro(<<"No scalar data to threshold");
    return;
    }

  numPts = input->GetNumberOfPoints();
  output->Allocate(input->GetNumberOfCells());
  newPoints = vtkPoints::New();
  newPoints->Allocate(numPts);

  pointMap = vtkIdList::New(); //maps old point ids into new
  pointMap->SetNumberOfIds(numPts);
  for (i=0; i < numPts; i++) pointMap->SetId(i,-1);

  // Determine which scalar data to use for thresholding
  if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_DEFAULT )
    {
    if ( pointScalars != NULL) usePointScalars = 1;
    else usePointScalars = 0;
    }
  else if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_USE_POINT_DATA )
    {
    usePointScalars = 1;
    }
  else
    {
    usePointScalars = 0;
    }

  // Check on scalar consistency
  if ( usePointScalars && !pointScalars )
    {
    vtkErrorMacro(<<"Can't use point scalars because there are none");
    return;
    }
  else if ( !usePointScalars && !cellScalars )
    {
    vtkErrorMacro(<<"Can't use cell scalars because there are none");
    return;
    }

  // Check that the scalars of each cell satisfy the threshold criterion
  for (cellId=0; cellId < input->GetNumberOfCells(); cellId++)
    {
    cell = input->GetCell(cellId);
    cellPts = cell->GetPointIds();
    numCellPts = cell->GetNumberOfPoints();

    if ( usePointScalars )
      {
      if (this->AllScalars)
	{
	keepCell = 1;
	for ( i=0; keepCell && (i < numCellPts); i++)
	  {
	  ptId = cellPts->GetId(i);
	  keepCell =
	    (this->*(this->ThresholdFunction))(pointScalars->GetScalar(ptId));
	  }
	}
      else
	{
	keepCell = 0;
	for ( i=0; (!keepCell) && (i < numCellPts); i++)
	  {
	  ptId = cellPts->GetId(i);
	  keepCell =
	    (this->*(this->ThresholdFunction))(pointScalars->GetScalar(ptId));
	  }
	}
      }
    else //use cell scalars
      {
      keepCell = (this->*(this->ThresholdFunction))(cellScalars->GetScalar(cellId));
      }

    if ( keepCell ) // satisfied thresholding
      {
      for (i=0; i < numCellPts; i++)
        {
        ptId = cellPts->GetId(i);
        if ( (newId = pointMap->GetId(ptId)) < 0 )
          {
          x = input->GetPoint(ptId);
          newId = newPoints->InsertNextPoint(x);
          pointMap->SetId(ptId,newId);
          outPD->CopyData(pd,ptId,newId);
          }
        newCellPts->InsertId(i,newId);
        }
      newCellId = output->InsertNextCell(cell->GetCellType(),*newCellPts);
      outCD->CopyData(cd,cellId,newCellId);
      newCellPts->Reset();
      } // satisfied thresholding
    } // for all cells

  vtkDebugMacro(<< "Extracted " << output->GetNumberOfCells()
                << " number of cells.");

  // now clean up / update ourselves
  pointMap->Delete();
  newCellPts->Delete();

  output->SetPoints(newPoints);
  newPoints->Delete();

  output->Squeeze();
}

// Description:
// Return the method for manipulating scalar data as a string.
char *vtkThreshold::GetAttributeModeAsString(void)
{
  if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_DEFAULT )
    {
    return "Default";
    }
  else if ( this->AttributeMode == VTK_ATTRIBUTE_MODE_USE_POINT_DATA )
    {
    return "UsePointData";
    }
  else
    {
    return "UseCellData";
    }
}

void vtkThreshold::PrintSelf(ostream& os, vtkIndent indent)
{
  vtkDataSetToUnstructuredGridFilter::PrintSelf(os,indent);

  os << indent << "Attribute Mode: " << this->GetAttributeModeAsString() << endl;
  os << indent << "All Scalars: " << this->AllScalars << "\n";;
  if ( this->ThresholdFunction == &vtkThreshold::Upper )
    os << indent << "Threshold By Upper\n";

  else if ( this->ThresholdFunction == &vtkThreshold::Lower )
    os << indent << "Threshold By Lower\n";

  else if ( this->ThresholdFunction == &vtkThreshold::Between )
    os << indent << "Threshold Between\n";

  os << indent << "Lower Threshold: " << this->LowerThreshold << "\n";;
  os << indent << "Upper Threshold: " << this->UpperThreshold << "\n";;
}
<|endoftext|>


<commit_before>/**
* @file BallCandidateDetector.cpp
*
* Implementation of class BallCandidateDetector
*
*/

#include "BallCandidateDetector.h"
#include "Tools/CameraGeometry.h"

#include "Tools/PatchWork.h"
#include "Tools/CVClassifier.h"
#include "Tools/BlackSpotExtractor.h"

using namespace std;

BallCandidateDetector::BallCandidateDetector()
{
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:keyPoints", "draw key points extracted from integral image", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawCandidates", "draw ball candidates", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawCandidatesResizes", "draw ball candidates (resized)", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:refinePatches", "draw refined ball key points", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawPercepts", "draw ball percepts", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawPatchContrast", "draw patch contrast (only when contrast-check is in use!", false);

  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:lbpDetection", "draw ball percepts", false);

  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:extractPatches", "generate YUVC patches", false);

  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:keyPointsBlack", "draw black key points extracted from integral image", false);

  theBallKeyPointExtractor = registerModule<BallKeyPointExtractor>("BallKeyPointExtractor", true);
  getDebugParameterList().add(&params);
}

BallCandidateDetector::~BallCandidateDetector()
{
  getDebugParameterList().remove(&params);
}

void BallCandidateDetector::execute(CameraInfo::CameraID id)
{
  cameraID = id;
  getBallCandidates().reset();

  best.clear();
  // update parameter
  theBallKeyPointExtractor->getModuleT()->setParameter(params.keyDetector);
  theBallKeyPointExtractor->getModuleT()->setCameraId(cameraID);
  //theBallKeyPointExtractor->getModuleT()->calculateKeyPoints(best);
  theBallKeyPointExtractor->getModuleT()->calculateKeyPointsBetter(best);

  if(best.size() > 0) {
    calculateCandidates();
  }

  DEBUG_REQUEST("Vision:BallCandidateDetector:refinePatches",
    for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
      BestPatchList::Patch p = theBallKeyPointExtractor->getModuleT()->refineKeyPoint(*i);
      RECT_PX(ColorClasses::red, p.min.x, p.min.y, p.max.x, p.max.y);
    }
  );

  DEBUG_REQUEST("Vision:BallCandidateDetector:drawPercepts",
    for(MultiBallPercept::ConstABPIterator iter = getMultiBallPercept().begin(); iter != getMultiBallPercept().end(); iter++) {
      if((*iter).cameraId == cameraID) {
        CIRCLE_PX(ColorClasses::orange, (int)((*iter).centerInImage.x+0.5), (int)((*iter).centerInImage.y+0.5), (int)((*iter).radiusInImage+0.5));
      }
    }
  );

  DEBUG_REQUEST("Vision:BallCandidateDetector:extractPatches",
    extractPatches();
  );

  if(params.numberOfExportBestPatches > 0) {
    extractPatches();
  }

  DEBUG_REQUEST("Vision:BallCandidateDetector:keyPointsBlack",
    BestPatchList bbest;
    for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
      bbest.clear();
      BlackSpotExtractor::calculateKeyPointsBlackBetter(getBallDetectorIntegralImage(), bbest, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
      int idx = 0;
      for(BestPatchList::reverse_iterator j = bbest.rbegin(); j != bbest.rend(); ++j) {
        RECT_PX(ColorClasses::red, (*j).min.x, (*j).min.y, (*j).max.x, (*j).max.y);
        if(++idx > 3) {
          break;
        }
      }
    }
  );
}

void BallCandidateDetector::calculateCandidates()
{
  // todo needs a better place
  const int32_t FACTOR = getBallDetectorIntegralImage().FACTOR;

  //BestPatchList::iterator best_element = best.begin();
  std::vector<Vector2i> endPoints;

  // needed for calculating black key-points in the ball candidates
  BestPatchList bestBlackKey;

  // the used patch size
  const int patch_size = 16;

  // NOTE: patches are sorted in the ascending order, so start from the end to get the best patches
  int index = 0;
  for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if(getFieldPercept().getValidField().isInside((*i).min) && getFieldPercept().getValidField().isInside((*i).max))
    {
      //int radius = (int)((*i).radius + 0.5);

      // limit the max amount of evaluated keys
      if(index > params.maxNumberOfKeys) {
        break;
      }

      const Vector2i& min((*i).min);
      const Vector2i& max((*i).max);

      // (1) check green below
      bool checkGreenBelow = false;
      if(getImage().isInside(max.x, max.y+(max.y-min.y)/2) && getImage().isInside(min.x - FACTOR, min.y - FACTOR)) {
        double greenBelow = getBallDetectorIntegralImage().getDensityForRect(min.x/FACTOR, max.y/FACTOR, max.x/FACTOR, (max.y+(max.y-min.y)/2)/FACTOR, 1);
        if(greenBelow > params.heuristic.minGreenBelowRatio) {
          checkGreenBelow = true;
        }
      }

      // (2) check green inside
      bool checkGreenInside = false;
      int offsetY = (max.y-min.y)/FACTOR;
      int offsetX = (max.x-min.x)/FACTOR;
      double greenInside = getBallDetectorIntegralImage().getDensityForRect((min.x+offsetX)/FACTOR, (min.y+offsetY)/FACTOR, (max.x-offsetX)/FACTOR, (max.y-offsetY)/FACTOR, 1);
      if(greenInside < params.heuristic.maxGreenInsideRatio) {
        checkGreenInside = true;
      }

      // (3) check black dots
      bool checkBlackDots = false;
      /*
      if(max.y-min.y > params.heuristic.minBlackDetectionSize)
      {
        BlackSpotExtractor::calculateKeyPoints(getBallDetectorIntegralImage(), bestBlackKey, min.x, min.y, max.x, max.y);

        DEBUG_REQUEST("Vision:BallCandidateDetector:keyPoints",
          for(BestPatchList::iterator i = bestBlackKey.begin(); i != bestBlackKey.end(); ++i) {
            RECT_PX(ColorClasses::red, min.x, min.y, max.x, max.y);
          }
        );

        if( (int)bestBlackKey.size() >= params.heuristic.blackDotsMinCount ) {
          checkBlackDots = true;
        }
      } else {
        //TODO: what to do with small balls?
      }
      //*/

      // (4) check body parts
      // TODO

      // (5) check contrast
      if(params.contrastUse) {
          double stddev = calculateContrast(getImage(),getFieldColorPercept(),min.x,min.y,max.x,max.y,patch_size);

          DEBUG_REQUEST("Vision:BallCandidateDetector:drawPatchStdDev",
                        CANVAS(((cameraID == CameraInfo::Top)?"ImageTop":"ImageBottom"));
                  CIRCLE( (min.x + max.x)/2, (min.y + max.y)/2, stddev / 5.0);
          );
          // skip this patch, if contrast doesn't fullfill minimum
          if(stddev <= params.contrastMinimum) {
              continue;
          }
      }

      //if(checkGreenBelow && checkGreenInside)
      if(params.haarDetector.execute)
      {
        // hack: if the classifier has not been loaded yet
        if(!cvHaarClassifier.modelLoaded()) {
          cvHaarClassifier.loadModel(params.haarDetector.model_file);
        }

        BallCandidates::Patch patchedBorder(0);
        //int size = ((*i).max.x - (*i).min.x)/2;
        patchedBorder.min = (*i).min;// - Vector2i(size,size);
        patchedBorder.max = (*i).max;// + Vector2i(size,size);

        // add an additional border as post-processing
        int size = patchedBorder.max.x - patchedBorder.min.x;
        double radius = (double) size / 2.0;
        int postBorder = (int)(radius*params.postBorderFactorFar);
        if(size >= params.postMaxCloseSize) // HACK: use patch size as estimate if close or far away
        {
          postBorder = (int)(radius*params.postBorderFactorClose);
        }

        patchedBorder.min.x = patchedBorder.min.x - postBorder;
        patchedBorder.min.y = patchedBorder.min.y - postBorder;
        patchedBorder.max.x = patchedBorder.max.x + postBorder;
        patchedBorder.max.y = patchedBorder.max.y + postBorder;

        if(getImage().isInside(patchedBorder.min.x, patchedBorder.min.y) && getImage().isInside(patchedBorder.max.x, patchedBorder.max.y))
        {

            auto r = (patchedBorder.max.x - patchedBorder.min.x)/2;
            auto x = (patchedBorder.min.x + patchedBorder.max.x)/2;
            auto y = (patchedBorder.min.y + patchedBorder.max.y)/2;
            DEBUG_REQUEST("Vision:BallCandidateDetector:drawCandidatesResizes",
              RECT_PX(ColorClasses::pink, x-r, y-r, x+r, y+r);
            );

          PatchWork::subsampling(getImage(), patchedBorder.data, patchedBorder.min.x, patchedBorder.min.y, patchedBorder.max.x, patchedBorder.max.y, patch_size);
          if(cvHaarClassifier.classify(patchedBorder, params.haarDetector.minNeighbors, params.haarDetector.windowSize) > 0) {

            if(!params.blackKeysCheck.enable || blackKeysOK(*i)) {
              addBallPercept(Vector2i((min.x + max.x)/2, (min.y + max.y)/2), (max.x - min.x)/2);

            }

          }
        } else if(getImage().isInside(min.x, min.y) && getImage().isInside(max.x, max.y)) {
            BallCandidates::Patch p(0);
            p.min = (*i).min;
            p.max = (*i).max;

            PatchWork::subsampling(getImage(), p.data, min.x, min.y, max.x, max.y, patch_size);
            if(cvHaarClassifier.classify(p, params.haarDetector.minNeighbors, params.haarDetector.windowSize) > 0) {

              if(!params.blackKeysCheck.enable || blackKeysOK(*i)) {
                addBallPercept(Vector2i((min.x + max.x)/2, (min.y + max.y)/2), (max.x - min.x)/2);

              }
            }
        }

        index++;
      }

      DEBUG_REQUEST("Vision:BallCandidateDetector:drawCandidates",
        ColorClasses::Color c = ColorClasses::gray;
        if(checkGreenBelow && checkGreenInside) {
          c = ColorClasses::orange;
        } else if(checkGreenBelow || checkGreenInside) {
          c = ColorClasses::skyblue;
        }
        RECT_PX(c, min.x, min.y, max.x, max.y);

        if(checkBlackDots) {
          CIRCLE_PX(ColorClasses::red, (min.x + max.x)/2, (min.y + max.y)/2, (max.x - min.x)/2);
        }
      );

      // TODO: provide ball candidates based on above criteria
      //getBallCandidates();

      // an acceptable candidate found...
      if(checkGreenBelow && checkGreenInside) {
        //index++;
      }

    } // end if in field

  } // end for

} // end calculateCandidates

void BallCandidateDetector::extractPatches()
{
  int idx = 0;
  for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if(idx >= params.numberOfExportBestPatches) {
      break;
    }
    Vector2i min = (*i).min;
    Vector2i max = (*i).max;
    int offset = (max.x - min.x)/4;
    min.x -= offset;
    min.y -= offset;
    max.x += offset;
    max.y += offset;

    if(getFieldPercept().getValidField().isInside(min) && getFieldPercept().getValidField().isInside(max))
    {
      BallCandidates::PatchYUVClassified& q = getBallCandidates().nextFreePatchYUVClassified();
      q.min = min;
      q.max = max;
      PatchWork::subsampling(getImage(), getFieldColorPercept(), q.data, q.min.x, q.min.y, q.max.x, q.max.y, 24);

      idx++;
    }
  }
}

void BallCandidateDetector::addBallPercept(const Vector2i& center, double radius)
{
  const double ballRadius = 50.0;
  MultiBallPercept::BallPercept ballPercept;

  if(CameraGeometry::imagePixelToFieldCoord(
		  getCameraMatrix(),
		  getImage().cameraInfo,
		  center.x,
		  center.y,
		  ballRadius,
		  ballPercept.positionOnField))
  {
    ballPercept.cameraId = cameraID;
    ballPercept.centerInImage = center;
    ballPercept.radiusInImage = radius;

    getMultiBallPercept().add(ballPercept);
    getMultiBallPercept().frameInfoWhenBallWasSeen = getFrameInfo();
  }
}

double BallCandidateDetector::calculateContrast(const Image& image,  const FieldColorPercept& fielColorPercept, int x0, int y0, int x1, int y1, int size)
{
    x0 = std::max(0, x0);
    y0 = std::max(0, y0);
    x1 = std::min((int)image.width()-1, x1);
    y1 = std::min((int)image.height()-1, y1);

    double width_step = static_cast<double>(x1 - x0) / static_cast<double>(size);
    double height_step = static_cast<double>(y1 - y0) / static_cast<double>(size);

    double avg = 0;
    double cnt = 0;
    BallCandidates::ClassifiedPixel p;

    for(double x = x0 + width_step/2.0; x < x1; x += width_step)
    {
        for(double y = y0 + height_step/2.0; y < y1; y += height_step)
        {
            image.get((int)(x + 0.5), (int)(y + 0.5), p.pixel);
            if(!fielColorPercept.greenHSISeparator.isColor(p.pixel)) { // no green!
                avg += p.pixel.y;
                cnt++;
            }
        }
    }

    // we got only green ?!
    if(cnt == 0) { return 0; }

    // expected value -> average/mean
    avg /= cnt;

    double variance = 0;
    for(double x = x0 + width_step/2.0; x < x1; x += width_step)
    {
        for(double y = y0 + height_step/2.0; y < y1; y += height_step)
        {
            image.get((int)(x + 0.5), (int)(y + 0.5), p.pixel);
            if(!fielColorPercept.greenHSISeparator.isColor(p.pixel)) { // green!
                auto t = p.pixel.y;
                variance += (t-avg)*(t-avg);
            }
        }
    }
    variance /= cnt;
    return std::sqrt(variance);
}
<commit_msg>bug fix: forgot to rename debug request<commit_after>/**
* @file BallCandidateDetector.cpp
*
* Implementation of class BallCandidateDetector
*
*/

#include "BallCandidateDetector.h"
#include "Tools/CameraGeometry.h"

#include "Tools/PatchWork.h"
#include "Tools/CVClassifier.h"
#include "Tools/BlackSpotExtractor.h"

using namespace std;

BallCandidateDetector::BallCandidateDetector()
{
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:keyPoints", "draw key points extracted from integral image", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawCandidates", "draw ball candidates", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawCandidatesResizes", "draw ball candidates (resized)", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:refinePatches", "draw refined ball key points", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawPercepts", "draw ball percepts", false);
  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:drawPatchContrast", "draw patch contrast (only when contrast-check is in use!", false);

  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:lbpDetection", "draw ball percepts", false);

  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:extractPatches", "generate YUVC patches", false);

  DEBUG_REQUEST_REGISTER("Vision:BallCandidateDetector:keyPointsBlack", "draw black key points extracted from integral image", false);

  theBallKeyPointExtractor = registerModule<BallKeyPointExtractor>("BallKeyPointExtractor", true);
  getDebugParameterList().add(&params);
}

BallCandidateDetector::~BallCandidateDetector()
{
  getDebugParameterList().remove(&params);
}

void BallCandidateDetector::execute(CameraInfo::CameraID id)
{
  cameraID = id;
  getBallCandidates().reset();

  best.clear();
  // update parameter
  theBallKeyPointExtractor->getModuleT()->setParameter(params.keyDetector);
  theBallKeyPointExtractor->getModuleT()->setCameraId(cameraID);
  //theBallKeyPointExtractor->getModuleT()->calculateKeyPoints(best);
  theBallKeyPointExtractor->getModuleT()->calculateKeyPointsBetter(best);

  if(best.size() > 0) {
    calculateCandidates();
  }

  DEBUG_REQUEST("Vision:BallCandidateDetector:refinePatches",
    for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
      BestPatchList::Patch p = theBallKeyPointExtractor->getModuleT()->refineKeyPoint(*i);
      RECT_PX(ColorClasses::red, p.min.x, p.min.y, p.max.x, p.max.y);
    }
  );

  DEBUG_REQUEST("Vision:BallCandidateDetector:drawPercepts",
    for(MultiBallPercept::ConstABPIterator iter = getMultiBallPercept().begin(); iter != getMultiBallPercept().end(); iter++) {
      if((*iter).cameraId == cameraID) {
        CIRCLE_PX(ColorClasses::orange, (int)((*iter).centerInImage.x+0.5), (int)((*iter).centerInImage.y+0.5), (int)((*iter).radiusInImage+0.5));
      }
    }
  );

  DEBUG_REQUEST("Vision:BallCandidateDetector:extractPatches",
    extractPatches();
  );

  if(params.numberOfExportBestPatches > 0) {
    extractPatches();
  }

  DEBUG_REQUEST("Vision:BallCandidateDetector:keyPointsBlack",
    BestPatchList bbest;
    for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
      bbest.clear();
      BlackSpotExtractor::calculateKeyPointsBlackBetter(getBallDetectorIntegralImage(), bbest, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
      int idx = 0;
      for(BestPatchList::reverse_iterator j = bbest.rbegin(); j != bbest.rend(); ++j) {
        RECT_PX(ColorClasses::red, (*j).min.x, (*j).min.y, (*j).max.x, (*j).max.y);
        if(++idx > 3) {
          break;
        }
      }
    }
  );
}

void BallCandidateDetector::calculateCandidates()
{
  // todo needs a better place
  const int32_t FACTOR = getBallDetectorIntegralImage().FACTOR;

  //BestPatchList::iterator best_element = best.begin();
  std::vector<Vector2i> endPoints;

  // needed for calculating black key-points in the ball candidates
  BestPatchList bestBlackKey;

  // the used patch size
  const int patch_size = 16;

  // NOTE: patches are sorted in the ascending order, so start from the end to get the best patches
  int index = 0;
  for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if(getFieldPercept().getValidField().isInside((*i).min) && getFieldPercept().getValidField().isInside((*i).max))
    {
      //int radius = (int)((*i).radius + 0.5);

      // limit the max amount of evaluated keys
      if(index > params.maxNumberOfKeys) {
        break;
      }

      const Vector2i& min((*i).min);
      const Vector2i& max((*i).max);

      // (1) check green below
      bool checkGreenBelow = false;
      if(getImage().isInside(max.x, max.y+(max.y-min.y)/2) && getImage().isInside(min.x - FACTOR, min.y - FACTOR)) {
        double greenBelow = getBallDetectorIntegralImage().getDensityForRect(min.x/FACTOR, max.y/FACTOR, max.x/FACTOR, (max.y+(max.y-min.y)/2)/FACTOR, 1);
        if(greenBelow > params.heuristic.minGreenBelowRatio) {
          checkGreenBelow = true;
        }
      }

      // (2) check green inside
      bool checkGreenInside = false;
      int offsetY = (max.y-min.y)/FACTOR;
      int offsetX = (max.x-min.x)/FACTOR;
      double greenInside = getBallDetectorIntegralImage().getDensityForRect((min.x+offsetX)/FACTOR, (min.y+offsetY)/FACTOR, (max.x-offsetX)/FACTOR, (max.y-offsetY)/FACTOR, 1);
      if(greenInside < params.heuristic.maxGreenInsideRatio) {
        checkGreenInside = true;
      }

      // (3) check black dots
      bool checkBlackDots = false;
      /*
      if(max.y-min.y > params.heuristic.minBlackDetectionSize)
      {
        BlackSpotExtractor::calculateKeyPoints(getBallDetectorIntegralImage(), bestBlackKey, min.x, min.y, max.x, max.y);

        DEBUG_REQUEST("Vision:BallCandidateDetector:keyPoints",
          for(BestPatchList::iterator i = bestBlackKey.begin(); i != bestBlackKey.end(); ++i) {
            RECT_PX(ColorClasses::red, min.x, min.y, max.x, max.y);
          }
        );

        if( (int)bestBlackKey.size() >= params.heuristic.blackDotsMinCount ) {
          checkBlackDots = true;
        }
      } else {
        //TODO: what to do with small balls?
      }
      //*/

      // (4) check body parts
      // TODO

      // (5) check contrast
      if(params.contrastUse) {
          double stddev = calculateContrast(getImage(),getFieldColorPercept(),min.x,min.y,max.x,max.y,patch_size);

          DEBUG_REQUEST("Vision:BallCandidateDetector:drawPatchContrast",
                        CANVAS(((cameraID == CameraInfo::Top)?"ImageTop":"ImageBottom"));
                  CIRCLE( (min.x + max.x)/2, (min.y + max.y)/2, stddev / 5.0);
          );
          // skip this patch, if contrast doesn't fullfill minimum
          if(stddev <= params.contrastMinimum) {
              continue;
          }
      }

      //if(checkGreenBelow && checkGreenInside)
      if(params.haarDetector.execute)
      {
        // hack: if the classifier has not been loaded yet
        if(!cvHaarClassifier.modelLoaded()) {
          cvHaarClassifier.loadModel(params.haarDetector.model_file);
        }

        BallCandidates::Patch patchedBorder(0);
        //int size = ((*i).max.x - (*i).min.x)/2;
        patchedBorder.min = (*i).min;// - Vector2i(size,size);
        patchedBorder.max = (*i).max;// + Vector2i(size,size);

        // add an additional border as post-processing
        int size = patchedBorder.max.x - patchedBorder.min.x;
        double radius = (double) size / 2.0;
        int postBorder = (int)(radius*params.postBorderFactorFar);
        if(size >= params.postMaxCloseSize) // HACK: use patch size as estimate if close or far away
        {
          postBorder = (int)(radius*params.postBorderFactorClose);
        }

        patchedBorder.min.x = patchedBorder.min.x - postBorder;
        patchedBorder.min.y = patchedBorder.min.y - postBorder;
        patchedBorder.max.x = patchedBorder.max.x + postBorder;
        patchedBorder.max.y = patchedBorder.max.y + postBorder;

        if(getImage().isInside(patchedBorder.min.x, patchedBorder.min.y) && getImage().isInside(patchedBorder.max.x, patchedBorder.max.y))
        {

            auto r = (patchedBorder.max.x - patchedBorder.min.x)/2;
            auto x = (patchedBorder.min.x + patchedBorder.max.x)/2;
            auto y = (patchedBorder.min.y + patchedBorder.max.y)/2;
            DEBUG_REQUEST("Vision:BallCandidateDetector:drawCandidatesResizes",
              RECT_PX(ColorClasses::pink, x-r, y-r, x+r, y+r);
            );

          PatchWork::subsampling(getImage(), patchedBorder.data, patchedBorder.min.x, patchedBorder.min.y, patchedBorder.max.x, patchedBorder.max.y, patch_size);
          if(cvHaarClassifier.classify(patchedBorder, params.haarDetector.minNeighbors, params.haarDetector.windowSize) > 0) {

            if(!params.blackKeysCheck.enable || blackKeysOK(*i)) {
              addBallPercept(Vector2i((min.x + max.x)/2, (min.y + max.y)/2), (max.x - min.x)/2);

            }

          }
        } else if(getImage().isInside(min.x, min.y) && getImage().isInside(max.x, max.y)) {
            BallCandidates::Patch p(0);
            p.min = (*i).min;
            p.max = (*i).max;

            PatchWork::subsampling(getImage(), p.data, min.x, min.y, max.x, max.y, patch_size);
            if(cvHaarClassifier.classify(p, params.haarDetector.minNeighbors, params.haarDetector.windowSize) > 0) {

              if(!params.blackKeysCheck.enable || blackKeysOK(*i)) {
                addBallPercept(Vector2i((min.x + max.x)/2, (min.y + max.y)/2), (max.x - min.x)/2);

              }
            }
        }

        index++;
      }

      DEBUG_REQUEST("Vision:BallCandidateDetector:drawCandidates",
        ColorClasses::Color c = ColorClasses::gray;
        if(checkGreenBelow && checkGreenInside) {
          c = ColorClasses::orange;
        } else if(checkGreenBelow || checkGreenInside) {
          c = ColorClasses::skyblue;
        }
        RECT_PX(c, min.x, min.y, max.x, max.y);

        if(checkBlackDots) {
          CIRCLE_PX(ColorClasses::red, (min.x + max.x)/2, (min.y + max.y)/2, (max.x - min.x)/2);
        }
      );

      // TODO: provide ball candidates based on above criteria
      //getBallCandidates();

      // an acceptable candidate found...
      if(checkGreenBelow && checkGreenInside) {
        //index++;
      }

    } // end if in field

  } // end for

} // end calculateCandidates

void BallCandidateDetector::extractPatches()
{
  int idx = 0;
  for(BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if(idx >= params.numberOfExportBestPatches) {
      break;
    }
    Vector2i min = (*i).min;
    Vector2i max = (*i).max;
    int offset = (max.x - min.x)/4;
    min.x -= offset;
    min.y -= offset;
    max.x += offset;
    max.y += offset;

    if(getFieldPercept().getValidField().isInside(min) && getFieldPercept().getValidField().isInside(max))
    {
      BallCandidates::PatchYUVClassified& q = getBallCandidates().nextFreePatchYUVClassified();
      q.min = min;
      q.max = max;
      PatchWork::subsampling(getImage(), getFieldColorPercept(), q.data, q.min.x, q.min.y, q.max.x, q.max.y, 24);

      idx++;
    }
  }
}

void BallCandidateDetector::addBallPercept(const Vector2i& center, double radius)
{
  const double ballRadius = 50.0;
  MultiBallPercept::BallPercept ballPercept;

  if(CameraGeometry::imagePixelToFieldCoord(
		  getCameraMatrix(),
		  getImage().cameraInfo,
		  center.x,
		  center.y,
		  ballRadius,
		  ballPercept.positionOnField))
  {
    ballPercept.cameraId = cameraID;
    ballPercept.centerInImage = center;
    ballPercept.radiusInImage = radius;

    getMultiBallPercept().add(ballPercept);
    getMultiBallPercept().frameInfoWhenBallWasSeen = getFrameInfo();
  }
}

double BallCandidateDetector::calculateContrast(const Image& image,  const FieldColorPercept& fielColorPercept, int x0, int y0, int x1, int y1, int size)
{
    x0 = std::max(0, x0);
    y0 = std::max(0, y0);
    x1 = std::min((int)image.width()-1, x1);
    y1 = std::min((int)image.height()-1, y1);

    double width_step = static_cast<double>(x1 - x0) / static_cast<double>(size);
    double height_step = static_cast<double>(y1 - y0) / static_cast<double>(size);

    double avg = 0;
    double cnt = 0;
    BallCandidates::ClassifiedPixel p;

    for(double x = x0 + width_step/2.0; x < x1; x += width_step)
    {
        for(double y = y0 + height_step/2.0; y < y1; y += height_step)
        {
            image.get((int)(x + 0.5), (int)(y + 0.5), p.pixel);
            if(!fielColorPercept.greenHSISeparator.isColor(p.pixel)) { // no green!
                avg += p.pixel.y;
                cnt++;
            }
        }
    }

    // we got only green ?!
    if(cnt == 0) { return 0; }

    // expected value -> average/mean
    avg /= cnt;

    double variance = 0;
    for(double x = x0 + width_step/2.0; x < x1; x += width_step)
    {
        for(double y = y0 + height_step/2.0; y < y1; y += height_step)
        {
            image.get((int)(x + 0.5), (int)(y + 0.5), p.pixel);
            if(!fielColorPercept.greenHSISeparator.isColor(p.pixel)) { // green!
                auto t = p.pixel.y;
                variance += (t-avg)*(t-avg);
            }
        }
    }
    variance /= cnt;
    return std::sqrt(variance);
}
<|endoftext|>


<commit_before>/*! \file MatTools.cpp
    \brief Implements the MatTools class used by the Generic Repository
    \author Kathryn D. Huff
 */
#include <iostream>
#include <fstream>
#include <deque>
#include <time.h>
#include <assert.h>

#include "CycException.h"
#include "CycLimits.h"
#include "MatTools.h"
#include "Material.h"
#include "Logger.h"
#include "Timer.h"

using namespace std;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<IsoVector, double> MatTools::sum_mats(deque<mat_rsrc_ptr> mats){
  IsoVector vec;
  CompMapPtr sum_comp = CompMapPtr(new CompMap(MASS));
  double tot = 0;
  double kg = 0;
  map<Iso, vector<double> > to_sum;
  vector<double> tot_vec;

  if( !mats.empty() ){
    CompMapPtr comp_to_add;
    deque<mat_rsrc_ptr>::iterator mat;
    int iso;
    CompMap::const_iterator comp;
    for(mat = mats.begin(); mat != mats.end(); ++mat){
      comp_to_add = (*mat)->unnormalizeComp(MASS, KG);
      for(comp = (*comp_to_add).begin(); comp != (*comp_to_add).end(); ++comp) {
        iso = comp->first;
        if(to_sum.find(iso)==to_sum.end()) {
          to_sum.insert(make_pair(iso, vector<double>()));
        }
        to_sum[iso].push_back(comp->second);
      }
    }
    map<Iso, vector<double> >::const_iterator it;
    for(it=to_sum.begin(); it!=to_sum.end(); ++it) {
      iso = (*it).first;
      (*sum_comp)[iso] = KahanSum((*it).second);
      tot_vec.push_back((*sum_comp)[iso]);
    }
    tot = KahanSum(tot_vec);
  }
  vec = IsoVector(sum_comp);
  return make_pair(vec, tot);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::KahanSum(vector<double> input){
  // http://en.wikipedia.org/wiki/Kahan_summation_algorithm
  double y, t;
  double sum = 0.0;
  //A running compensation for lost low-order bits.
  double c = 0.0;
  for(vector<double>::iterator i = input.begin(); i!=input.end(); ++i){
    y = *i - c;
    //So far, so good: c is zero.
    t = sum + y;
    //Alas, sum is big, y small, so low-order digits of y are lost.
    c = (t - sum) - y;
    //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
    sum = t;
    //Algebraically, c should always be zero. Beware eagerly optimising compilers!
    //Next time around, the lost low part will be added to y in a fresh attempt.
  }
  return sum;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
mat_rsrc_ptr MatTools::extract(const CompMapPtr comp_to_rem, double kg_to_rem,
    deque<mat_rsrc_ptr>& mat_list, double threshold){
  comp_to_rem->massify();
  mat_rsrc_ptr left_over = mat_rsrc_ptr(new Material(comp_to_rem));
  left_over->setQuantity(0);
  // absorb them together.
  while(!mat_list.empty()) {
    left_over->absorb(mat_list.back());
    mat_list.pop_back();
  }
  mat_rsrc_ptr to_ret;
  if( left_over->mass(KG) >= threshold ) {
    to_ret = left_over->extract(comp_to_rem, kg_to_rem, KG, threshold);
    mat_list.push_back(left_over);
  } else {
    to_ret = mat_rsrc_ptr(left_over);
  }
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::comp_to_conc_map(CompMapPtr comp, double mass, double vol){
  MatTools::validate_finite_pos(vol);
  MatTools::validate_finite_pos(mass);
  comp->massify();

  IsoConcMap to_ret;
  if( vol==0 ) {
    to_ret = zeroConcMap();
  } else {
    int iso;
    double m_iso;
    CompMap::const_iterator it;
    it=(*comp).begin();
    while(it!= (*comp).end() ){
      iso = (*it).first;
      m_iso=((*it).second)*mass;
      to_ret.insert(make_pair(iso, m_iso/vol));
      ++it;
    }
  }
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::zeroConcMap(){
  IsoConcMap to_ret;
  to_ret[92235] = 0;
  return to_ret;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<CompMapPtr, double> MatTools::conc_to_comp_map(IsoConcMap conc, double vol){
  MatTools::validate_finite_pos(vol);

  CompMapPtr comp = CompMapPtr(new CompMap(MASS));
  vector<double> masses;
  double mass(0);
  int iso;
  double c_iso;
  double m_iso;
  CompMap::const_iterator it;
  it=conc.begin();
  while(it!= conc.end() ){
    iso = (*it).first;
    c_iso=((*it).second);
    m_iso = c_iso*vol;
    (*comp)[iso] = m_iso;
    masses.push_back(m_iso);
    ++it;
  }
  mass = MatTools::KahanSum(masses);
  (*comp).normalize();
  pair<CompMapPtr, double> to_ret = make_pair(CompMapPtr(comp), mass);
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_f(double V_T, double theta){
  validate_percent(theta);
  validate_finite_pos(V_T);
  return theta*V_T;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_ff(double V_T, double theta, double d){
  validate_percent(d);
  return d*V_f(V_T, theta);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_mf(double V_T, double theta, double d){
  return (V_f(V_T,theta) - V_ff(V_T, theta, d));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_s(double V_T, double theta){
  return (V_T - V_f(V_T, theta));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_ds(double V_T, double theta, double d){
  validate_percent(d);
  return d*V_s(V_T, theta);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_ms(double V_T, double theta, double d){
  return (V_s(V_T, theta) - V_ds(V_T, theta, d));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_percent(double per){
  if( per <= 1 && per >= 0 ){
    return;
  } else if ( per < 0) {
    throw CycRangeException("The value is not a valid percent. It is less than zero.");
  } else if ( per > 1) {
    throw CycRangeException("The value is not a valid percent. It is greater than one.");
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_finite_pos(double pos){
  if ( pos >= numeric_limits<double>::infinity() ) {
    throw CycRangeException("The value is not positive and finite. It is infinite.");
  }
  validate_pos(pos);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_pos(double pos){
  if ( pos < 0) {
    std::stringstream ss;
    ss << "The value "
       << pos
       << " is not positive and finite. It is less than zero." ;;
    throw CycRangeException(ss.str());
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_nonzero(double nonzero){
  if ( nonzero == 0 )
    throw CycRangeException("The value is zero.");
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::scaleConcMap(IsoConcMap C_0, double scalar){
  MatTools::validate_finite_pos(scalar);
  double orig;
  IsoConcMap::const_iterator it;
  for(it = C_0.begin(); it != C_0.end(); ++it) {
    orig = C_0[(*it).first];
    C_0[(*it).first] = orig*scalar;
  }
  return C_0;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::addConcMaps(IsoConcMap orig, IsoConcMap to_add){
  map<Iso, vector<double> > add_map;
  IsoConcMap to_ret;
  IsoConcMap::const_iterator it;
  for(it = orig.begin(); it != orig.end(); ++it) {
    Iso iso=(*it).first;
    add_map[iso].push_back((*it).second);
    if(to_add.find(iso) != to_add.end()) {
      add_map[iso].push_back(to_add[iso]);
    }
  }
  for(it = to_add.begin(); it != to_add.end(); ++it) {
    Iso iso=(*it).first;
    if(orig.find(iso) == orig.end()) {
      add_map[iso].push_back((*it).second);
    }
  }

  map<Iso, vector<double> >::const_iterator term;
  for( term=add_map.begin(); term!=add_map.end(); ++term){
    Iso iso=(*term).first;
    to_ret[iso] = MatTools::KahanSum((*term).second);
  }
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int MatTools::isoToElem(int iso) {
  int N = iso % 1000;
  return (iso-N)/1000;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vector<double> MatTools::linspace(double a, double b, int n) {
  vector<double> array(0);  // optional preallocation
  double step = (b-a)/(n-1);
  while(a <= b) {
    array.push_back(a);
    a += step;           // could recode to better handle rounding errors
  }
  return array;
}

<commit_msg>recodes linspace to better handle rounding errors<commit_after>/*! \file MatTools.cpp
    \brief Implements the MatTools class used by the Generic Repository
    \author Kathryn D. Huff
 */
#include <iostream>
#include <fstream>
#include <deque>
#include <time.h>
#include <assert.h>

#include "CycException.h"
#include "CycLimits.h"
#include "MatTools.h"
#include "Material.h"
#include "Logger.h"
#include "Timer.h"

using namespace std;

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<IsoVector, double> MatTools::sum_mats(deque<mat_rsrc_ptr> mats){
  IsoVector vec;
  CompMapPtr sum_comp = CompMapPtr(new CompMap(MASS));
  double tot = 0;
  double kg = 0;
  map<Iso, vector<double> > to_sum;
  vector<double> tot_vec;

  if( !mats.empty() ){
    CompMapPtr comp_to_add;
    deque<mat_rsrc_ptr>::iterator mat;
    int iso;
    CompMap::const_iterator comp;
    for(mat = mats.begin(); mat != mats.end(); ++mat){
      comp_to_add = (*mat)->unnormalizeComp(MASS, KG);
      for(comp = (*comp_to_add).begin(); comp != (*comp_to_add).end(); ++comp) {
        iso = comp->first;
        if(to_sum.find(iso)==to_sum.end()) {
          to_sum.insert(make_pair(iso, vector<double>()));
        }
        to_sum[iso].push_back(comp->second);
      }
    }
    map<Iso, vector<double> >::const_iterator it;
    for(it=to_sum.begin(); it!=to_sum.end(); ++it) {
      iso = (*it).first;
      (*sum_comp)[iso] = KahanSum((*it).second);
      tot_vec.push_back((*sum_comp)[iso]);
    }
    tot = KahanSum(tot_vec);
  }
  vec = IsoVector(sum_comp);
  return make_pair(vec, tot);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::KahanSum(vector<double> input){
  // http://en.wikipedia.org/wiki/Kahan_summation_algorithm
  double y, t;
  double sum = 0.0;
  //A running compensation for lost low-order bits.
  double c = 0.0;
  for(vector<double>::iterator i = input.begin(); i!=input.end(); ++i){
    y = *i - c;
    //So far, so good: c is zero.
    t = sum + y;
    //Alas, sum is big, y small, so low-order digits of y are lost.
    c = (t - sum) - y;
    //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
    sum = t;
    //Algebraically, c should always be zero. Beware eagerly optimising compilers!
    //Next time around, the lost low part will be added to y in a fresh attempt.
  }
  return sum;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
mat_rsrc_ptr MatTools::extract(const CompMapPtr comp_to_rem, double kg_to_rem,
    deque<mat_rsrc_ptr>& mat_list, double threshold){
  comp_to_rem->massify();
  mat_rsrc_ptr left_over = mat_rsrc_ptr(new Material(comp_to_rem));
  left_over->setQuantity(0);
  // absorb them together.
  while(!mat_list.empty()) {
    left_over->absorb(mat_list.back());
    mat_list.pop_back();
  }
  mat_rsrc_ptr to_ret;
  if( left_over->mass(KG) >= threshold ) {
    to_ret = left_over->extract(comp_to_rem, kg_to_rem, KG, threshold);
    mat_list.push_back(left_over);
  } else {
    to_ret = mat_rsrc_ptr(left_over);
  }
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::comp_to_conc_map(CompMapPtr comp, double mass, double vol){
  MatTools::validate_finite_pos(vol);
  MatTools::validate_finite_pos(mass);
  comp->massify();

  IsoConcMap to_ret;
  if( vol==0 ) {
    to_ret = zeroConcMap();
  } else {
    int iso;
    double m_iso;
    CompMap::const_iterator it;
    it=(*comp).begin();
    while(it!= (*comp).end() ){
      iso = (*it).first;
      m_iso=((*it).second)*mass;
      to_ret.insert(make_pair(iso, m_iso/vol));
      ++it;
    }
  }
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::zeroConcMap(){
  IsoConcMap to_ret;
  to_ret[92235] = 0;
  return to_ret;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
pair<CompMapPtr, double> MatTools::conc_to_comp_map(IsoConcMap conc, double vol){
  MatTools::validate_finite_pos(vol);

  CompMapPtr comp = CompMapPtr(new CompMap(MASS));
  vector<double> masses;
  double mass(0);
  int iso;
  double c_iso;
  double m_iso;
  CompMap::const_iterator it;
  it=conc.begin();
  while(it!= conc.end() ){
    iso = (*it).first;
    c_iso=((*it).second);
    m_iso = c_iso*vol;
    (*comp)[iso] = m_iso;
    masses.push_back(m_iso);
    ++it;
  }
  mass = MatTools::KahanSum(masses);
  (*comp).normalize();
  pair<CompMapPtr, double> to_ret = make_pair(CompMapPtr(comp), mass);
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_f(double V_T, double theta){
  validate_percent(theta);
  validate_finite_pos(V_T);
  return theta*V_T;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_ff(double V_T, double theta, double d){
  validate_percent(d);
  return d*V_f(V_T, theta);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_mf(double V_T, double theta, double d){
  return (V_f(V_T,theta) - V_ff(V_T, theta, d));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_s(double V_T, double theta){
  return (V_T - V_f(V_T, theta));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_ds(double V_T, double theta, double d){
  validate_percent(d);
  return d*V_s(V_T, theta);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
double MatTools::V_ms(double V_T, double theta, double d){
  return (V_s(V_T, theta) - V_ds(V_T, theta, d));
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_percent(double per){
  if( per <= 1 && per >= 0 ){
    return;
  } else if ( per < 0) {
    throw CycRangeException("The value is not a valid percent. It is less than zero.");
  } else if ( per > 1) {
    throw CycRangeException("The value is not a valid percent. It is greater than one.");
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_finite_pos(double pos){
  if ( pos >= numeric_limits<double>::infinity() ) {
    throw CycRangeException("The value is not positive and finite. It is infinite.");
  }
  validate_pos(pos);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_pos(double pos){
  if ( pos < 0) {
    std::stringstream ss;
    ss << "The value "
       << pos
       << " is not positive and finite. It is less than zero." ;;
    throw CycRangeException(ss.str());
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void MatTools::validate_nonzero(double nonzero){
  if ( nonzero == 0 )
    throw CycRangeException("The value is zero.");
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::scaleConcMap(IsoConcMap C_0, double scalar){
  MatTools::validate_finite_pos(scalar);
  double orig;
  IsoConcMap::const_iterator it;
  for(it = C_0.begin(); it != C_0.end(); ++it) {
    orig = C_0[(*it).first];
    C_0[(*it).first] = orig*scalar;
  }
  return C_0;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IsoConcMap MatTools::addConcMaps(IsoConcMap orig, IsoConcMap to_add){
  map<Iso, vector<double> > add_map;
  IsoConcMap to_ret;
  IsoConcMap::const_iterator it;
  for(it = orig.begin(); it != orig.end(); ++it) {
    Iso iso=(*it).first;
    add_map[iso].push_back((*it).second);
    if(to_add.find(iso) != to_add.end()) {
      add_map[iso].push_back(to_add[iso]);
    }
  }
  for(it = to_add.begin(); it != to_add.end(); ++it) {
    Iso iso=(*it).first;
    if(orig.find(iso) == orig.end()) {
      add_map[iso].push_back((*it).second);
    }
  }

  map<Iso, vector<double> >::const_iterator term;
  for( term=add_map.begin(); term!=add_map.end(); ++term){
    Iso iso=(*term).first;
    to_ret[iso] = MatTools::KahanSum((*term).second);
  }
  return to_ret;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int MatTools::isoToElem(int iso) {
  int N = iso % 1000;
  return (iso-N)/1000;
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
vector<double> MatTools::linspace(double a, double b, int n) {
  vector<double> array(0);  // optional preallocation
  double step = (b-a)/(n-1);
  for( i=a; i<=b; ++i){
    array.push_back(a+i*step)
  }
  return array;
}

<|endoftext|>


<commit_before>#include <Bull/Core/IO/OutStringStream.hpp>
#include <Bull/Core/IO/TextWriter.hpp>

#include <Bull/Network/Address/IpAddressImpl.hpp>
#include <Bull/Network/Address/IpAddressV4.hpp>

namespace Bull
{
    IpAddressV4 IpAddressV4::Any = IpAddressV4(0, 0, 0, 0);
    IpAddressV4 IpAddressV4::None;
    IpAddressV4 IpAddressV4::Loopback = IpAddressV4(127, 0, 0, 1);
    IpAddressV4 IpAddressV4::Broadcast = IpAddressV4(255, 255, 255, 255);

    IpAddressV4::IpAddressV4() :
        IpAddress(4, false)
    {
        /// Nothing
    }

    IpAddressV4::IpAddressV4(Uint32 address) :
        IpAddress(4)
    {
        at(0) = (address & 0x000000ff) >> 0;
        at(1) = (address & 0x0000ff00) >> 8;
        at(2) = (address & 0x00ff0000) >> 16;
        at(3) = (address & 0xff000000) >> 24;
    }

    IpAddressV4::IpAddressV4(Uint8 a, Uint8 b, Uint8 c, Uint8 d) :
        IpAddress(4)
    {
        at(0) = a;
        at(1) = b;
        at(2) = c;
        at(3) = d;
    }

    Uint32 IpAddressV4::toInt() const
    {
        return (at(0) << 24) | (at(1) << 16) | (at(2) << 8) | at(3);
    }

    String IpAddressV4::toString() const
    {
        OutStringStream oss;
        TextWriter writer(oss);

        for(std::size_t i = 0; i < getByteCount(); i++)
        {
            writer << at(i);

            if(i < 3)
            {
                writer << '.';
            }
        }

        return oss.toString();
    }

    NetProtocol IpAddressV4::getProtocol() const
    {
        return NetProtocol_Ipv4;
    }
}<commit_msg>[Network/IpAddressV4] Fix toString<commit_after>#include <Bull/Core/IO/OutStringStream.hpp>
#include <Bull/Core/IO/TextWriter.hpp>
#include <Bull/Core/Utility/StringUtils.hpp>

#include <Bull/Network/Address/IpAddressImpl.hpp>
#include <Bull/Network/Address/IpAddressV4.hpp>

namespace Bull
{
    IpAddressV4 IpAddressV4::Any = IpAddressV4(0, 0, 0, 0);
    IpAddressV4 IpAddressV4::None;
    IpAddressV4 IpAddressV4::Loopback = IpAddressV4(127, 0, 0, 1);
    IpAddressV4 IpAddressV4::Broadcast = IpAddressV4(255, 255, 255, 255);

    IpAddressV4::IpAddressV4() :
        IpAddress(4, false)
    {
        /// Nothing
    }

    IpAddressV4::IpAddressV4(Uint32 address) :
        IpAddress(4)
    {
        at(0) = (address & 0x000000ff) >> 0;
        at(1) = (address & 0x0000ff00) >> 8;
        at(2) = (address & 0x00ff0000) >> 16;
        at(3) = (address & 0xff000000) >> 24;
    }

    IpAddressV4::IpAddressV4(Uint8 a, Uint8 b, Uint8 c, Uint8 d) :
        IpAddress(4)
    {
        at(0) = a;
        at(1) = b;
        at(2) = c;
        at(3) = d;
    }

    Uint32 IpAddressV4::toInt() const
    {
        return (at(0) << 24) | (at(1) << 16) | (at(2) << 8) | at(3);
    }

    String IpAddressV4::toString() const
    {
        OutStringStream oss;
        TextWriter writer(oss);

        for(std::size_t i = 0; i < getByteCount(); i++)
        {
            writer << StringUtils::number(at(i));

            if(i < 3)
            {
                writer << '.';
            }
        }

        return oss.toString();
    }

    NetProtocol IpAddressV4::getProtocol() const
    {
        return NetProtocol_Ipv4;
    }
}<|endoftext|>