<commit_before>#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "archive.hh"
#include "builtins/buildenv.hh"
#include "flake/flakeref.hh"
#include "nix-env/user-env.hh"

#include <nlohmann/json.hpp>
#include <regex>

using namespace nix;

struct ProfileElementSource
{
    FlakeRef originalRef;
    // FIXME: record original attrpath.
    FlakeRef resolvedRef;
    std::string attrPath;
    // FIXME: output names
};

struct ProfileElement
{
    StorePathSet storePaths;
    std::optional<ProfileElementSource> source;
    bool active = true;
    // FIXME: priority
};

struct ProfileManifest
{
    std::vector<ProfileElement> elements;

    ProfileManifest() { }

    ProfileManifest(EvalState & state, const Path & profile)
    {
        auto manifestPath = profile + "/manifest.json";

        if (pathExists(manifestPath)) {
            auto json = nlohmann::json::parse(readFile(manifestPath));

            auto version = json.value("version", 0);
            if (version != 1)
                throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);

            for (auto & e : json["elements"]) {
                ProfileElement element;
                for (auto & p : e["storePaths"])
                    element.storePaths.insert(state.store->parseStorePath((std::string) p));
                element.active = e["active"];
                if (e.value("uri", "") != "") {
                    element.source = ProfileElementSource{
                        FlakeRef(e["originalUri"]),
                        FlakeRef(e["uri"]),
                        e["attrPath"]
                    };
                }
                elements.emplace_back(std::move(element));
            }
        }

        else if (pathExists(profile + "/manifest.nix")) {
            // FIXME: needed because of pure mode; ugly.
            if (state.allowedPaths) {
                state.allowedPaths->insert(state.store->followLinksToStore(profile));
                state.allowedPaths->insert(state.store->followLinksToStore(profile + "/manifest.nix"));
            }

            auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));

            for (auto & drvInfo : drvInfos) {
                ProfileElement element;
                element.storePaths = singleton(state.store->parseStorePath(drvInfo.queryOutPath()));
                elements.emplace_back(std::move(element));
            }
        }
    }

    std::string toJSON(Store & store) const
    {
        auto array = nlohmann::json::array();
        for (auto & element : elements) {
            auto paths = nlohmann::json::array();
            for (auto & path : element.storePaths)
                paths.push_back(store.printStorePath(path));
            nlohmann::json obj;
            obj["storePaths"] = paths;
            obj["active"] = element.active;
            if (element.source) {
                obj["originalUri"] = element.source->originalRef.to_string();
                obj["uri"] = element.source->resolvedRef.to_string();
                obj["attrPath"] = element.source->attrPath;
            }
            array.push_back(obj);
        }
        nlohmann::json json;
        json["version"] = 1;
        json["elements"] = array;
        return json.dump();
    }

    StorePath build(ref<Store> store)
    {
        auto tempDir = createTempDir();

        StorePathSet references;

        Packages pkgs;
        for (auto & element : elements) {
            for (auto & path : element.storePaths) {
                if (element.active)
                    pkgs.emplace_back(store->printStorePath(path), true, 5);
                references.insert(path.clone());
            }
        }

        buildProfile(tempDir, std::move(pkgs));

        writeFile(tempDir + "/manifest.json", toJSON(*store));

        /* Add the symlink tree to the store. */
        StringSink sink;
        dumpPath(tempDir, sink);

        ValidPathInfo info(store->makeFixedOutputPath(true, info.narHash, "profile", references));
        info.references = std::move(references);
        info.narHash = hashString(htSHA256, *sink.s);
        info.narSize = sink.s->size();
        info.ca = makeFixedOutputCA(true, info.narHash);

        store->addToStore(info, sink.s);

        return std::move(info.path);
    }
};

struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{
    std::string description() override
    {
        return "install a package into a profile";
    }

    Examples examples() override
    {
        return {
            Example{
                "To install a package from Nixpkgs:",
                "nix profile install nixpkgs#hello"
            },
            Example{
                "To install a package from a specific branch of Nixpkgs:",
                "nix profile install nixpkgs/release-19.09#hello"
            },
            Example{
                "To install a package from a specific revision of Nixpkgs:",
                "nix profile install nixpkgs/1028bb33859f8dfad7f98e1c8d185f3d1aaa7340#hello"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest manifest(*getEvalState(), *profile);

        std::vector<StorePathWithOutputs> pathsToBuild;

        for (auto & installable : installables) {
            if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
                auto [attrPath, resolvedRef, drv] = installable2->toDerivation();

                ProfileElement element;
                element.storePaths = singleton(drv.outPath.clone()); // FIXME
                element.source = ProfileElementSource{
                    installable2->flakeRef,
                    resolvedRef,
                    attrPath,
                };

                pathsToBuild.emplace_back(drv.drvPath.clone(), StringSet{"out"}); // FIXME

                manifest.elements.emplace_back(std::move(element));
            } else
                throw Error("'nix profile install' does not support argument '%s'", installable->what());
        }

        store->buildPaths(pathsToBuild);

        updateProfile(manifest.build(store));
    }
};

class MixProfileElementMatchers : virtual Args
{
    std::vector<std::string> _matchers;

public:

    MixProfileElementMatchers()
    {
        expectArgs("elements", &_matchers);
    }

    typedef std::variant<size_t, Path, std::regex> Matcher;

    std::vector<Matcher> getMatchers(ref<Store> store)
    {
        std::vector<Matcher> res;

        for (auto & s : _matchers) {
            size_t n;
            if (string2Int(s, n))
                res.push_back(n);
            else if (store->isStorePath(s))
                res.push_back(s);
            else
                res.push_back(std::regex(s, std::regex::extended | std::regex::icase));
        }

        return res;
    }

    bool matches(const Store & store, const ProfileElement & element, size_t pos, const std::vector<Matcher> & matchers)
    {
        for (auto & matcher : matchers) {
            if (auto n = std::get_if<size_t>(&matcher)) {
                if (*n == pos) return true;
            } else if (auto path = std::get_if<Path>(&matcher)) {
                if (element.storePaths.count(store.parseStorePath(*path))) return true;
            } else if (auto regex = std::get_if<std::regex>(&matcher)) {
                if (element.source
                    && std::regex_match(element.source->attrPath, *regex))
                    return true;
            }
        }

        return false;
    }
};

struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElementMatchers
{
    std::string description() override
    {
        return "remove packages from a profile";
    }

    Examples examples() override
    {
        return {
            Example{
                "To remove a package by attribute path:",
                "nix profile remove packages.x86_64-linux.hello"
            },
            Example{
                "To remove all packages:",
                "nix profile remove '.*'"
            },
            Example{
                "To remove a package by store path:",
                "nix profile remove /nix/store/rr3y0c6zyk7kjjl8y19s4lsrhn4aiq1z-hello-2.10"
            },
            Example{
                "To remove a package by position:",
                "nix profile remove 3"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest oldManifest(*getEvalState(), *profile);

        auto matchers = getMatchers(store);

        ProfileManifest newManifest;

        for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
            auto & element(oldManifest.elements[i]);
            if (!matches(*store, element, i, matchers))
                newManifest.elements.push_back(std::move(element));
        }

        // FIXME: warn about unused matchers?

        printInfo("removed %d packages, kept %d packages",
            oldManifest.elements.size() - newManifest.elements.size(),
            newManifest.elements.size());

        updateProfile(newManifest.build(store));
    }
};

struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProfileElementMatchers
{
    std::string description() override
    {
        return "upgrade packages using their most recent flake";
    }

    Examples examples() override
    {
        return {
            Example{
                "To upgrade all packages that were installed using a mutable flake reference:",
                "nix profile upgrade '.*'"
            },
            Example{
                "To upgrade a specific package:",
                "nix profile upgrade packages.x86_64-linux.hello"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest manifest(*getEvalState(), *profile);

        auto matchers = getMatchers(store);

        // FIXME: code duplication
        std::vector<StorePathWithOutputs> pathsToBuild;

        for (size_t i = 0; i < manifest.elements.size(); ++i) {
            auto & element(manifest.elements[i]);
            if (element.source
                && !element.source->originalRef.isImmutable()
                && matches(*store, element, i, matchers))
            {
                Activity act(*logger, lvlChatty, actUnknown,
                    fmt("checking '%s' for updates", element.source->attrPath));

                InstallableFlake installable(*this, FlakeRef(element.source->originalRef), {element.source->attrPath});

                auto [attrPath, resolvedRef, drv] = installable.toDerivation();

                if (element.source->resolvedRef == resolvedRef) continue;

                printInfo("upgrading '%s' from flake '%s' to '%s'",
                    element.source->attrPath, element.source->resolvedRef, resolvedRef);

                element.storePaths = singleton(drv.outPath.clone()); // FIXME
                element.source = ProfileElementSource{
                    installable.flakeRef,
                    resolvedRef,
                    attrPath,
                };

                pathsToBuild.emplace_back(drv.drvPath, StringSet{"out"}); // FIXME
            }
        }

        store->buildPaths(pathsToBuild);

        updateProfile(manifest.build(store));
    }
};

struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile
{
    std::string description() override
    {
        return "list installed packages";
    }

    Examples examples() override
    {
        return {
            Example{
                "To show what packages are installed in the default profile:",
                "nix profile info"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest manifest(*getEvalState(), *profile);

        for (size_t i = 0; i < manifest.elements.size(); ++i) {
            auto & element(manifest.elements[i]);
            std::cout << fmt("%d %s %s %s\n", i,
                element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-",
                element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-",
                concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
        }
    }
};

struct CmdProfile : virtual MultiCommand, virtual Command
{
    CmdProfile()
        : MultiCommand({
              {"install", []() { return make_ref<CmdProfileInstall>(); }},
              {"remove", []() { return make_ref<CmdProfileRemove>(); }},
              {"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
              {"info", []() { return make_ref<CmdProfileInfo>(); }},
          })
    { }

    std::string description() override
    {
        return "manage Nix profiles";
    }

    void run() override
    {
        if (!command)
            throw UsageError("'nix profile' requires a sub-command.");
        command->prepare();
        command->run();
    }

    void printHelp(const string & programName, std::ostream & out) override
    {
        MultiCommand::printHelp(programName, out);
    }
};

static auto r1 = registerCommand<CmdProfile>("profile");

<commit_msg>Fix 'nix profile'<commit_after>#include "command.hh"
#include "common-args.hh"
#include "shared.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "archive.hh"
#include "builtins/buildenv.hh"
#include "flake/flakeref.hh"
#include "nix-env/user-env.hh"

#include <nlohmann/json.hpp>
#include <regex>

using namespace nix;

struct ProfileElementSource
{
    FlakeRef originalRef;
    // FIXME: record original attrpath.
    FlakeRef resolvedRef;
    std::string attrPath;
    // FIXME: output names
};

struct ProfileElement
{
    StorePathSet storePaths;
    std::optional<ProfileElementSource> source;
    bool active = true;
    // FIXME: priority
};

struct ProfileManifest
{
    std::vector<ProfileElement> elements;

    ProfileManifest() { }

    ProfileManifest(EvalState & state, const Path & profile)
    {
        auto manifestPath = profile + "/manifest.json";

        if (pathExists(manifestPath)) {
            auto json = nlohmann::json::parse(readFile(manifestPath));

            auto version = json.value("version", 0);
            if (version != 1)
                throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);

            for (auto & e : json["elements"]) {
                ProfileElement element;
                for (auto & p : e["storePaths"])
                    element.storePaths.insert(state.store->parseStorePath((std::string) p));
                element.active = e["active"];
                if (e.value("uri", "") != "") {
                    element.source = ProfileElementSource{
                        FlakeRef(e["originalUri"]),
                        FlakeRef(e["uri"]),
                        e["attrPath"]
                    };
                }
                elements.emplace_back(std::move(element));
            }
        }

        else if (pathExists(profile + "/manifest.nix")) {
            // FIXME: needed because of pure mode; ugly.
            if (state.allowedPaths) {
                state.allowedPaths->insert(state.store->followLinksToStore(profile));
                state.allowedPaths->insert(state.store->followLinksToStore(profile + "/manifest.nix"));
            }

            auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));

            for (auto & drvInfo : drvInfos) {
                ProfileElement element;
                element.storePaths = singleton(state.store->parseStorePath(drvInfo.queryOutPath()));
                elements.emplace_back(std::move(element));
            }
        }
    }

    std::string toJSON(Store & store) const
    {
        auto array = nlohmann::json::array();
        for (auto & element : elements) {
            auto paths = nlohmann::json::array();
            for (auto & path : element.storePaths)
                paths.push_back(store.printStorePath(path));
            nlohmann::json obj;
            obj["storePaths"] = paths;
            obj["active"] = element.active;
            if (element.source) {
                obj["originalUri"] = element.source->originalRef.to_string();
                obj["uri"] = element.source->resolvedRef.to_string();
                obj["attrPath"] = element.source->attrPath;
            }
            array.push_back(obj);
        }
        nlohmann::json json;
        json["version"] = 1;
        json["elements"] = array;
        return json.dump();
    }

    StorePath build(ref<Store> store)
    {
        auto tempDir = createTempDir();

        StorePathSet references;

        Packages pkgs;
        for (auto & element : elements) {
            for (auto & path : element.storePaths) {
                if (element.active)
                    pkgs.emplace_back(store->printStorePath(path), true, 5);
                references.insert(path.clone());
            }
        }

        buildProfile(tempDir, std::move(pkgs));

        writeFile(tempDir + "/manifest.json", toJSON(*store));

        /* Add the symlink tree to the store. */
        StringSink sink;
        dumpPath(tempDir, sink);

        auto narHash = hashString(htSHA256, *sink.s);

        ValidPathInfo info(store->makeFixedOutputPath(true, narHash, "profile", references));
        info.references = std::move(references);
        info.narHash = narHash;
        info.narSize = sink.s->size();
        info.ca = makeFixedOutputCA(true, info.narHash);

        store->addToStore(info, sink.s);

        return std::move(info.path);
    }
};

struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{
    std::string description() override
    {
        return "install a package into a profile";
    }

    Examples examples() override
    {
        return {
            Example{
                "To install a package from Nixpkgs:",
                "nix profile install nixpkgs#hello"
            },
            Example{
                "To install a package from a specific branch of Nixpkgs:",
                "nix profile install nixpkgs/release-19.09#hello"
            },
            Example{
                "To install a package from a specific revision of Nixpkgs:",
                "nix profile install nixpkgs/1028bb33859f8dfad7f98e1c8d185f3d1aaa7340#hello"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest manifest(*getEvalState(), *profile);

        std::vector<StorePathWithOutputs> pathsToBuild;

        for (auto & installable : installables) {
            if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
                auto [attrPath, resolvedRef, drv] = installable2->toDerivation();

                ProfileElement element;
                element.storePaths = singleton(drv.outPath.clone()); // FIXME
                element.source = ProfileElementSource{
                    installable2->flakeRef,
                    resolvedRef,
                    attrPath,
                };

                pathsToBuild.emplace_back(drv.drvPath.clone(), StringSet{"out"}); // FIXME

                manifest.elements.emplace_back(std::move(element));
            } else
                throw Error("'nix profile install' does not support argument '%s'", installable->what());
        }

        store->buildPaths(pathsToBuild);

        updateProfile(manifest.build(store));
    }
};

class MixProfileElementMatchers : virtual Args
{
    std::vector<std::string> _matchers;

public:

    MixProfileElementMatchers()
    {
        expectArgs("elements", &_matchers);
    }

    typedef std::variant<size_t, Path, std::regex> Matcher;

    std::vector<Matcher> getMatchers(ref<Store> store)
    {
        std::vector<Matcher> res;

        for (auto & s : _matchers) {
            size_t n;
            if (string2Int(s, n))
                res.push_back(n);
            else if (store->isStorePath(s))
                res.push_back(s);
            else
                res.push_back(std::regex(s, std::regex::extended | std::regex::icase));
        }

        return res;
    }

    bool matches(const Store & store, const ProfileElement & element, size_t pos, const std::vector<Matcher> & matchers)
    {
        for (auto & matcher : matchers) {
            if (auto n = std::get_if<size_t>(&matcher)) {
                if (*n == pos) return true;
            } else if (auto path = std::get_if<Path>(&matcher)) {
                if (element.storePaths.count(store.parseStorePath(*path))) return true;
            } else if (auto regex = std::get_if<std::regex>(&matcher)) {
                if (element.source
                    && std::regex_match(element.source->attrPath, *regex))
                    return true;
            }
        }

        return false;
    }
};

struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElementMatchers
{
    std::string description() override
    {
        return "remove packages from a profile";
    }

    Examples examples() override
    {
        return {
            Example{
                "To remove a package by attribute path:",
                "nix profile remove packages.x86_64-linux.hello"
            },
            Example{
                "To remove all packages:",
                "nix profile remove '.*'"
            },
            Example{
                "To remove a package by store path:",
                "nix profile remove /nix/store/rr3y0c6zyk7kjjl8y19s4lsrhn4aiq1z-hello-2.10"
            },
            Example{
                "To remove a package by position:",
                "nix profile remove 3"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest oldManifest(*getEvalState(), *profile);

        auto matchers = getMatchers(store);

        ProfileManifest newManifest;

        for (size_t i = 0; i < oldManifest.elements.size(); ++i) {
            auto & element(oldManifest.elements[i]);
            if (!matches(*store, element, i, matchers))
                newManifest.elements.push_back(std::move(element));
        }

        // FIXME: warn about unused matchers?

        printInfo("removed %d packages, kept %d packages",
            oldManifest.elements.size() - newManifest.elements.size(),
            newManifest.elements.size());

        updateProfile(newManifest.build(store));
    }
};

struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProfileElementMatchers
{
    std::string description() override
    {
        return "upgrade packages using their most recent flake";
    }

    Examples examples() override
    {
        return {
            Example{
                "To upgrade all packages that were installed using a mutable flake reference:",
                "nix profile upgrade '.*'"
            },
            Example{
                "To upgrade a specific package:",
                "nix profile upgrade packages.x86_64-linux.hello"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest manifest(*getEvalState(), *profile);

        auto matchers = getMatchers(store);

        // FIXME: code duplication
        std::vector<StorePathWithOutputs> pathsToBuild;

        for (size_t i = 0; i < manifest.elements.size(); ++i) {
            auto & element(manifest.elements[i]);
            if (element.source
                && !element.source->originalRef.isImmutable()
                && matches(*store, element, i, matchers))
            {
                Activity act(*logger, lvlChatty, actUnknown,
                    fmt("checking '%s' for updates", element.source->attrPath));

                InstallableFlake installable(*this, FlakeRef(element.source->originalRef), {element.source->attrPath});

                auto [attrPath, resolvedRef, drv] = installable.toDerivation();

                if (element.source->resolvedRef == resolvedRef) continue;

                printInfo("upgrading '%s' from flake '%s' to '%s'",
                    element.source->attrPath, element.source->resolvedRef, resolvedRef);

                element.storePaths = singleton(drv.outPath.clone()); // FIXME
                element.source = ProfileElementSource{
                    installable.flakeRef,
                    resolvedRef,
                    attrPath,
                };

                pathsToBuild.emplace_back(drv.drvPath, StringSet{"out"}); // FIXME
            }
        }

        store->buildPaths(pathsToBuild);

        updateProfile(manifest.build(store));
    }
};

struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile
{
    std::string description() override
    {
        return "list installed packages";
    }

    Examples examples() override
    {
        return {
            Example{
                "To show what packages are installed in the default profile:",
                "nix profile info"
            },
        };
    }

    void run(ref<Store> store) override
    {
        ProfileManifest manifest(*getEvalState(), *profile);

        for (size_t i = 0; i < manifest.elements.size(); ++i) {
            auto & element(manifest.elements[i]);
            std::cout << fmt("%d %s %s %s\n", i,
                element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-",
                element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-",
                concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
        }
    }
};

struct CmdProfile : virtual MultiCommand, virtual Command
{
    CmdProfile()
        : MultiCommand({
              {"install", []() { return make_ref<CmdProfileInstall>(); }},
              {"remove", []() { return make_ref<CmdProfileRemove>(); }},
              {"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
              {"info", []() { return make_ref<CmdProfileInfo>(); }},
          })
    { }

    std::string description() override
    {
        return "manage Nix profiles";
    }

    void run() override
    {
        if (!command)
            throw UsageError("'nix profile' requires a sub-command.");
        command->prepare();
        command->run();
    }

    void printHelp(const string & programName, std::ostream & out) override
    {
        MultiCommand::printHelp(programName, out);
    }
};

static auto r1 = registerCommand<CmdProfile>("profile");

<|endoftext|>


<commit_before>#include <iostream>
#include <chrono>
#include "trainer.h"
#include "errorCriterion.h"
#include "optimizers/optimizer.h"

using namespace nnFit;

Trainer::Trainer(Network &network, ErrorCriterion &criterion, Dataset &data) : network(network), criterion(criterion), data(data), trainingExampleCount(data.size()), input(network.device(), data.inputSize()), output(network.device(), data.outputSize()) {
    reshuffleIndices = false;
    profile = false;
}

void Trainer::gradientDescent(Optimizer &opt, size_t iterations) {
    train(opt, iterations, trainingExampleCount);
}

void Trainer::miniBatchGradientDescent(Optimizer &opt, size_t iterations, size_t miniBatchSize) {
    train(opt, iterations, miniBatchSize);
}

void Trainer::train(Optimizer &opt, size_t iterations, size_t miniBatchSize) {
    Vector errors(network.device(), data.outputSize());
    auto weightsAndGradients = network.weightsAndGradients();
    size_t batchCount = trainingExampleCount/miniBatchSize + (trainingExampleCount % miniBatchSize == 0? 0 : 1);

    std::vector<size_t> indices(trainingExampleCount);
    for (size_t i = 0; i < indices.size(); ++i)
        indices[i] = i;

    std::chrono::high_resolution_clock::time_point iterationStart;
    for (size_t iteration = 0; iteration < iterations; ++iteration) {
        float iterationError = 0.0f;
        if (profile)
            iterationStart = std::chrono::high_resolution_clock::now();
        // Shuffle indices if needed
        if (reshuffleIndices)
            std::random_shuffle(indices.begin(), indices.end());

        for (size_t batch = 0; batch < batchCount; ++batch) {
            size_t count = std::min(batch*miniBatchSize+miniBatchSize, trainingExampleCount) - batch*miniBatchSize;

            // Reset gradients and errors
            for (auto &i : weightsAndGradients) {
                i.second->zeros();
            }
            errors.zeros();

            // Train
            for (size_t i = batch*miniBatchSize, end = i + count; i < end; ++i) {
                data.get(indices[i], 1, input, output);

                const auto &prediction = network.feedforward(input);
                criterion.computeError(network.context(), prediction, output, errors);
                criterion.computeLastLayerError(network.context(), network.lastLayer(), output);
                network.backpropagate();
            }

            // Sum the error
            Vector errorSum(network.device(), 1);
            partialSum(errorSum, errors);

            std::vector<float> errs;
            errorSum.copy(errs);
            float err = errs[0];
            iterationError += err;
            err/=float(count);

            // gradients = gradients / numberOfTrainingExamples
            // Scale the gradients while optimizing to avoid redundant division step.
            opt.optimize(weightsAndGradients, count);
        }
        if (profile) {
            network.device().queue().finish();
            auto now = std::chrono::high_resolution_clock::now();
            auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now - iterationStart).count();
            std::cout << "One training iteration ran for " << seconds << "s\n";
        }
        if (afterIteration) {
            afterIteration(iteration, iterationError/trainingExampleCount);
        }
    }
}<commit_msg>Move the iteration error computation out of the batch loop<commit_after>#include <iostream>
#include <chrono>
#include "trainer.h"
#include "errorCriterion.h"
#include "optimizers/optimizer.h"

using namespace nnFit;

Trainer::Trainer(Network &network, ErrorCriterion &criterion, Dataset &data) : network(network), criterion(criterion), data(data), trainingExampleCount(data.size()), input(network.device(), data.inputSize()), output(network.device(), data.outputSize()) {
    reshuffleIndices = false;
    profile = false;
}

void Trainer::gradientDescent(Optimizer &opt, size_t iterations) {
    train(opt, iterations, trainingExampleCount);
}

void Trainer::miniBatchGradientDescent(Optimizer &opt, size_t iterations, size_t miniBatchSize) {
    train(opt, iterations, miniBatchSize);
}

void Trainer::train(Optimizer &opt, size_t iterations, size_t miniBatchSize) {
    Vector errors(network.device(), data.outputSize());
    Vector errorSum(network.device(), 1);
    std::vector<float> errs;

    auto weightsAndGradients = network.weightsAndGradients();
    size_t batchCount = trainingExampleCount/miniBatchSize + (trainingExampleCount % miniBatchSize == 0? 0 : 1);

    std::vector<size_t> indices(trainingExampleCount);
    for (size_t i = 0; i < indices.size(); ++i)
        indices[i] = i;

    std::chrono::high_resolution_clock::time_point iterationStart;
    for (size_t iteration = 0; iteration < iterations; ++iteration) {
        // Reset errors
        errors.zeros();
        if (profile)
            iterationStart = std::chrono::high_resolution_clock::now();
        // Shuffle indices if needed
        if (reshuffleIndices)
            std::random_shuffle(indices.begin(), indices.end());

        for (size_t batch = 0; batch < batchCount; ++batch) {
            size_t count = std::min(batch*miniBatchSize+miniBatchSize, trainingExampleCount) - batch*miniBatchSize;

            // Reset gradients
            for (auto &i : weightsAndGradients) {
                i.second->zeros();
            }

            // Train
            for (size_t i = batch*miniBatchSize, end = i + count; i < end; ++i) {
                data.get(indices[i], 1, input, output);

                const auto &prediction = network.feedforward(input);
                criterion.computeError(network.context(), prediction, output, errors);
                criterion.computeLastLayerError(network.context(), network.lastLayer(), output);
                network.backpropagate();
            }

            // gradients = gradients / numberOfTrainingExamples
            // Scale the gradients while optimizing to avoid redundant division step.
            opt.optimize(weightsAndGradients, count);
        }

        // Compute the iteration error.
        partialSum(errorSum, errors);
        errorSum.copy(errs);
        float iterationError = errs[0] / float(trainingExampleCount);

        if (profile) {
            network.device().queue().finish();
            auto now = std::chrono::high_resolution_clock::now();
            auto seconds = std::chrono::duration_cast<std::chrono::seconds>(now - iterationStart).count();
            std::cout << "One training iteration ran for " << seconds << "s\n";
        }
        if (afterIteration) {
            afterIteration(iteration, iterationError);
        }
    }
}<|endoftext|>


<commit_before>/*++
Copyright (c) 2014 Microsoft Corporation

Module Name:

    maxsres.cpp

Abstract:

    MaxRes (weighted) max-sat algorithm by Nina and Bacchus, AAAI 2014.

Author:

    Nikolaj Bjorner (nbjorner) 2014-20-7

Notes:

--*/

#include "solver.h"
#include "maxsmt.h"
#include "maxres.h"
#include "ast_pp.h"
#include "mus.h"

using namespace opt;

class maxres : public maxsmt_solver_base {
    expr_ref_vector  m_B;
    expr_ref_vector  m_asms;
    obj_map<expr, rational> m_asm2weight;
    ptr_vector<expr> m_new_core;
    mus              m_mus;
    expr_ref_vector  m_trail;

public:
    maxres(ast_manager& m, opt_solver* s, params_ref& p,
           vector<rational> const& ws, expr_ref_vector const& soft):
        maxsmt_solver_base(s, m, p, ws, soft),
        m_B(m), m_asms(m),
        m_mus(m_s, m),
        m_trail(m)
    {
    }

    virtual ~maxres() {}

    bool is_literal(expr* l) {
        return
            is_uninterp_const(l) ||
            (m.is_not(l, l) && is_uninterp_const(l));
    }

    void add_soft(expr* e, rational const& w) {
        TRACE("opt", tout << mk_pp(e, m) << "\n";);
        expr_ref asum(m), fml(m);
        app_ref cls(m);
        rational weight(0);
        if (m_asm2weight.find(e, weight)) {
            weight += w;
            m_asm2weight.insert(e, weight);
            return;
        }
        if (is_literal(e)) {
            asum = e;
        }
        else {
            asum = mk_fresh_bool("soft");
            fml = m.mk_iff(asum, e);
            m_s->assert_expr(fml);
        }
        new_assumption(asum, w);
        m_upper += w;
    }

    void new_assumption(expr* e, rational const& w) {
        TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";);
        m_asm2weight.insert(e, w);
        m_asms.push_back(e);
        m_trail.push_back(e);
    }

    lbool operator()() {
        solver::scoped_push _sc(*m_s.get());
        init();
        init_local();
        enable_bvsat();
        while (true) {
            TRACE("opt",
                  display_vec(tout, m_asms.size(), m_asms.c_ptr());
                  m_s->display(tout);
                  tout << "\n";
                  display(tout);
                  );
            lbool is_sat = m_s->check_sat(m_asms.size(), m_asms.c_ptr());
            if (m_cancel) {
                return l_undef;
            }
            switch (is_sat) {
            case l_true: {
                m_s->get_model(m_model);
                expr_ref tmp(m);
                DEBUG_CODE(
                    for (unsigned i = 0; i < m_asms.size(); ++i) {
                        VERIFY(m_model->eval(m_asms[i].get(), tmp));
                        SASSERT(m.is_true(tmp));
                    });
                for (unsigned i = 0; i < m_soft.size(); ++i) {
                    VERIFY(m_model->eval(m_soft[i].get(), tmp));
                    m_assignment[i] = m.is_true(tmp);
                }
                m_upper = m_lower;
                return l_true;
            }
            case l_false:
                is_sat = process_unsat();
                if (is_sat != l_true) return is_sat;
                break;
            case l_undef:
                return l_undef;
            default:
                break;
            }
            IF_VERBOSE(1, verbose_stream() << "(opt.max_res [" << m_lower << ":" << m_upper << "])\n";);
        }
        return l_true;
    }

    lbool get_cores(vector<ptr_vector<expr> >& cores) {
        // assume m_s is unsat.
        lbool is_sat = l_false;
        expr_ref_vector asms(m_asms);
        cores.reset();
        ptr_vector<expr> core;
        while (is_sat == l_false) {
            core.reset();
            m_s->get_unsat_core(core);
            is_sat = minimize_core(core);
            if (is_sat != l_true) {
                break;
            }
            cores.push_back(core);
            // TBD: ad hoc to avoid searching for large cores..
            if (core.size() >= 3) {
                break;
            }
            remove_soft(core, asms);
            TRACE("opt",
                  display_vec(tout << "core: ", core.size(), core.c_ptr());
                  display_vec(tout << "assumptions: ", asms.size(), asms.c_ptr()););
            is_sat = m_s->check_sat(asms.size(), asms.c_ptr());
        }
        TRACE("opt",
              tout << "num cores: " << cores.size() << "\n";
              for (unsigned i = 0; i < cores.size(); ++i) {
                  for (unsigned j = 0; j < cores[i].size(); ++j) {
                      tout << mk_pp(cores[i][j], m) << " ";
                  }
                  tout << "\n";
              }
              tout << "num satisfying: " << asms.size() << "\n";);

        return is_sat;
    }

    lbool process_unsat() {
        vector<ptr_vector<expr> > cores;
        lbool is_sat = get_cores(cores);
        if (is_sat != l_true) {
            return is_sat;
        }
        if (cores.empty()) {
            return l_false;
        }
        for (unsigned i = 0; is_sat == l_true && i < cores.size(); ++i) {
            is_sat = process_unsat(cores[i]);
        }
        return is_sat;
    }

    lbool process_unsat(ptr_vector<expr>& core) {
        expr_ref fml(m);
        remove_core(core);
        rational w = split_core(core);
        TRACE("opt", display_vec(tout << "minimized core: ", core.size(), core.c_ptr()););
        max_resolve(core, w);
        fml = m.mk_not(m.mk_and(m_B.size(), m_B.c_ptr()));
        m_s->assert_expr(fml);
        m_lower += w;
        return l_true;
    }

    lbool minimize_core(ptr_vector<expr>& core) {
        if (m_sat_enabled) {
            return l_true;
        }
        m_mus.reset();
        for (unsigned i = 0; i < core.size(); ++i) {
            m_mus.add_soft(core[i]);
        }
        unsigned_vector mus_idx;
        lbool is_sat = m_mus.get_mus(mus_idx);
        if (is_sat != l_true) {
            return is_sat;
        }
        m_new_core.reset();
        for (unsigned i = 0; i < mus_idx.size(); ++i) {
            m_new_core.push_back(core[mus_idx[i]]);
        }
        core.reset();
        core.append(m_new_core);
        return l_true;
    }

    rational get_weight(expr* e) {
        return m_asm2weight.find(e);
    }

    rational split_core(ptr_vector<expr> const& core) {

        // find the minimal weight:
        SASSERT(!core.empty());
        rational w = get_weight(core[0]);
        for (unsigned i = 1; i < core.size(); ++i) {
            rational w2 = get_weight(core[i]);
            if (w2 < w) {
                w = w2;
            }
        }
        // add fresh soft clauses for weights that are above w.
        for (unsigned i = 0; i < core.size(); ++i) {
            rational w2 = get_weight(core[i]);
            if (w2 > w) {
                rational w3 = w2 - w;
                new_assumption(core[i], w3);
            }
        }
        return w;
    }

    void display_vec(std::ostream& out, unsigned sz, expr* const* args) {
        for (unsigned i = 0; i < sz; ++i) {
            out << mk_pp(args[i], m) << " : " << get_weight(args[i]) << " ";
        }
        out << "\n";
    }

    void display(std::ostream& out) {
        for (unsigned i = 0; i < m_asms.size(); ++i) {
            expr* a = m_asms[i].get();
            out << mk_pp(a, m) << " : " << get_weight(a) << "\n";
        }
    }

    void max_resolve(ptr_vector<expr>& core, rational const& w) {
        SASSERT(!core.empty());
        expr_ref fml(m), asum(m);
        app_ref cls(m), d(m), dd(m);
        m_B.reset();
        m_B.append(core.size(), core.c_ptr());
        d = m.mk_true();
        //
        // d_0 := true
        // d_i := b_{i-1} and d_{i-1}    for i = 1...sz-1
        // soft (b_i or !d_i)
        //   == (b_i or !(!b_{i-1} or d_{i-1}))
        //   == (b_i or b_0 & b_1 & ... & b_{i-1})
        //
        // Soft constraint is satisfied if previous soft constraint
        // holds or if it is the first soft constraint to fail.
        //
        // Soundness of this rule can be established using MaxRes
        //
        for (unsigned i = 1; i < core.size(); ++i) {
            expr* b_i = m_B[i-1].get();
            expr* b_i1 = m_B[i].get();
            if (i > 2) {
                dd = mk_fresh_bool("d");
                fml = m.mk_implies(dd, d);
                m_s->assert_expr(fml);
                fml = m.mk_implies(dd, b_i);
                m_s->assert_expr(fml);
                d = dd;
            }
            else {
                d = m.mk_and(b_i, d);
            }
            asum = mk_fresh_bool("a");
            cls = m.mk_or(b_i1, d);
            fml = m.mk_implies(asum, cls);
            new_assumption(asum, w);
            m_s->assert_expr(fml);
        }
    }

    void remove_soft(ptr_vector<expr> const& core, expr_ref_vector& asms) {
        for (unsigned i = 0; i < asms.size(); ++i) {
            if (core.contains(asms[i].get())) {
                asms[i] = asms.back();
                asms.pop_back();
                --i;
            }
        }
    }

    void remove_core(ptr_vector<expr> const& core) {
        remove_soft(core, m_asms);
    }

    virtual void set_cancel(bool f) {
        maxsmt_solver_base::set_cancel(f);
        m_mus.set_cancel(f);
    }

    void init_local() {
        m_upper.reset();
        m_lower.reset();
        m_trail.reset();
        for (unsigned i = 0; i < m_soft.size(); ++i) {
            add_soft(m_soft[i].get(), m_weights[i]);
        }
    }

};

opt::maxsmt_solver_base* opt::mk_maxres(ast_manager& m, opt_solver* s, params_ref& p,
                                        vector<rational> const& ws, expr_ref_vector const& soft) {
    return alloc(maxres, m, s, p, ws, soft);
}

<commit_msg>moving dual solver to maxres<commit_after>/*++
Copyright (c) 2014 Microsoft Corporation

Module Name:

    maxsres.cpp

Abstract:

    MaxRes (weighted) max-sat algorithm by Nina and Bacchus, AAAI 2014.

Author:

    Nikolaj Bjorner (nbjorner) 2014-20-7

Notes:

--*/

#include "solver.h"
#include "maxsmt.h"
#include "maxres.h"
#include "ast_pp.h"
#include "mus.h"

using namespace opt;

class maxres : public maxsmt_solver_base {
public:
    enum strategy_t {
        s_mus,
        s_mus_mss,
        s_mss
    };
private:
    expr_ref_vector  m_B;
    expr_ref_vector  m_asms;
    obj_map<expr, rational> m_asm2weight;
    ptr_vector<expr> m_new_core;
    mus              m_mus;
    expr_ref_vector  m_trail;
    strategy_t       m_st;

public:
    maxres(ast_manager& m, opt_solver* s, params_ref& p,
           vector<rational> const& ws, expr_ref_vector const& soft,
           strategy_t st):
        maxsmt_solver_base(s, m, p, ws, soft),
        m_B(m), m_asms(m),
        m_mus(m_s, m),
        m_trail(m),
        m_st(st)
    {
    }

    virtual ~maxres() {}

    bool is_literal(expr* l) {
        return
            is_uninterp_const(l) ||
            (m.is_not(l, l) && is_uninterp_const(l));
    }

    void add_soft(expr* e, rational const& w) {
        TRACE("opt", tout << mk_pp(e, m) << "\n";);
        expr_ref asum(m), fml(m);
        app_ref cls(m);
        rational weight(0);
        if (m_asm2weight.find(e, weight)) {
            weight += w;
            m_asm2weight.insert(e, weight);
            return;
        }
        if (is_literal(e)) {
            asum = e;
        }
        else {
            asum = mk_fresh_bool("soft");
            fml = m.mk_iff(asum, e);
            m_s->assert_expr(fml);
        }
        new_assumption(asum, w);
        m_upper += w;
    }

    void new_assumption(expr* e, rational const& w) {
        TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";);
        m_asm2weight.insert(e, w);
        m_asms.push_back(e);
        m_trail.push_back(e);
    }

    lbool mus_solver() {
        solver::scoped_push _sc(*m_s.get());
        init();
        init_local();
        enable_bvsat();
        while (true) {
            TRACE("opt",
                  display_vec(tout, m_asms.size(), m_asms.c_ptr());
                  m_s->display(tout);
                  tout << "\n";
                  display(tout);
                  );
            lbool is_sat = m_s->check_sat(m_asms.size(), m_asms.c_ptr());
            if (m_cancel) {
                return l_undef;
            }
            switch (is_sat) {
            case l_true: {
                m_s->get_model(m_model);
                expr_ref tmp(m);
                DEBUG_CODE(
                    for (unsigned i = 0; i < m_asms.size(); ++i) {
                        VERIFY(m_model->eval(m_asms[i].get(), tmp));
                        SASSERT(m.is_true(tmp));
                    });
                for (unsigned i = 0; i < m_soft.size(); ++i) {
                    VERIFY(m_model->eval(m_soft[i].get(), tmp));
                    m_assignment[i] = m.is_true(tmp);
                }
                m_upper = m_lower;
                return l_true;
            }
            case l_false:
                is_sat = process_unsat();
                if (is_sat != l_true) return is_sat;
                break;
            case l_undef:
                return l_undef;
            default:
                break;
            }
            IF_VERBOSE(1, verbose_stream() << "(opt.max_res [" << m_lower << ":" << m_upper << "])\n";);
        }
        return l_true;
    }

    lbool mus_mss_solver() {
        solver::scoped_push _sc(*m_s.get());
        init();
        init_local();
        enable_bvsat();
        enable_sls();
        lbool was_sat = l_false;
        ptr_vector<expr> soft_compl;
        vector<ptr_vector<expr> > cores;
        while (m_lower < m_upper) {
            TRACE("opt",
                  display_vec(tout, m_asms.size(), m_asms.c_ptr());
                  m_s->display(tout);
                  tout << "\n";
                  display(tout);
                  );
            lbool is_sat = m_s->check_sat(0, 0);
            if (m_cancel) {
                return l_undef;
            }
            if (is_sat == l_true) {
                was_sat = l_true;
                is_sat = extend_model(soft_compl);
                switch (is_sat) {
                case l_undef:
                    break;
                case l_false:
                    is_sat = process_unsat();
                    break;
                case l_true:
                    is_sat = process_sat(soft_compl);
                    break;
                }
            }
            switch (is_sat) {
            case l_undef:
                return l_undef;
            case l_false:
                m_lower = m_upper;
                return was_sat;
            case l_true:
                break;
            }
        }
        return was_sat;
    }

    lbool mss_solver() {
        NOT_IMPLEMENTED_YET();
        return l_undef;
    }

    lbool operator()() {
        switch(m_st) {
        case s_mus:
            return mus_solver();
        case s_mus_mss:
            return mus_mss_solver();
        case s_mss:
            return mss_solver();
        }
    }

    lbool get_cores(vector<ptr_vector<expr> >& cores) {
        // assume m_s is unsat.
        lbool is_sat = l_false;
        expr_ref_vector asms(m_asms);
        cores.reset();
        ptr_vector<expr> core;
        while (is_sat == l_false) {
            core.reset();
            m_s->get_unsat_core(core);
            is_sat = minimize_core(core);
            if (is_sat != l_true) {
                break;
            }
            cores.push_back(core);
            // TBD: ad hoc to avoid searching for large cores..
            if (core.size() >= 3) {
                break;
            }
            remove_soft(core, asms);
            TRACE("opt",
                  display_vec(tout << "core: ", core.size(), core.c_ptr());
                  display_vec(tout << "assumptions: ", asms.size(), asms.c_ptr()););
            is_sat = m_s->check_sat(asms.size(), asms.c_ptr());
        }
        TRACE("opt",
              tout << "num cores: " << cores.size() << "\n";
              for (unsigned i = 0; i < cores.size(); ++i) {
                  for (unsigned j = 0; j < cores[i].size(); ++j) {
                      tout << mk_pp(cores[i][j], m) << " ";
                  }
                  tout << "\n";
              }
              tout << "num satisfying: " << asms.size() << "\n";);

        return is_sat;
    }

    lbool process_sat(ptr_vector<expr>& corr_set) {
        expr_ref fml(m), tmp(m);
        TRACE("opt", display_vec(tout << "corr_set: ", corr_set.size(), corr_set.c_ptr()););
        SASSERT(!corr_set.empty()); // we should somehow stop if all soft are satisfied.
        if (corr_set.empty()) {
            return l_false;
        }

        remove_core(corr_set);
        rational w = split_core(corr_set);
        TRACE("opt", display_vec(tout << " corr_set: ", corr_set.size(), corr_set.c_ptr()););
        dual_max_resolve(corr_set, w);
        return l_true;
    }

    lbool process_unsat() {
        vector<ptr_vector<expr> > cores;
        lbool is_sat = get_cores(cores);
        if (is_sat != l_true) {
            return is_sat;
        }
        if (cores.empty()) {
            return l_false;
        }
        for (unsigned i = 0; is_sat == l_true && i < cores.size(); ++i) {
            is_sat = process_unsat(cores[i]);
        }
        return is_sat;
    }

    lbool process_unsat(ptr_vector<expr>& core) {
        expr_ref fml(m);
        remove_core(core);
        rational w = split_core(core);
        TRACE("opt", display_vec(tout << "minimized core: ", core.size(), core.c_ptr()););
        max_resolve(core, w);
        fml = m.mk_not(m.mk_and(m_B.size(), m_B.c_ptr()));
        m_s->assert_expr(fml);
        m_lower += w;
        return l_true;
    }

    lbool minimize_core(ptr_vector<expr>& core) {
        if (m_sat_enabled) {
            return l_true;
        }
        m_mus.reset();
        for (unsigned i = 0; i < core.size(); ++i) {
            m_mus.add_soft(core[i]);
        }
        unsigned_vector mus_idx;
        lbool is_sat = m_mus.get_mus(mus_idx);
        if (is_sat != l_true) {
            return is_sat;
        }
        m_new_core.reset();
        for (unsigned i = 0; i < mus_idx.size(); ++i) {
            m_new_core.push_back(core[mus_idx[i]]);
        }
        core.reset();
        core.append(m_new_core);
        return l_true;
    }

    rational get_weight(expr* e) {
        return m_asm2weight.find(e);
    }

    rational split_core(ptr_vector<expr> const& core) {

        // find the minimal weight:
        SASSERT(!core.empty());
        rational w = get_weight(core[0]);
        for (unsigned i = 1; i < core.size(); ++i) {
            rational w2 = get_weight(core[i]);
            if (w2 < w) {
                w = w2;
            }
        }
        // add fresh soft clauses for weights that are above w.
        for (unsigned i = 0; i < core.size(); ++i) {
            rational w2 = get_weight(core[i]);
            if (w2 > w) {
                rational w3 = w2 - w;
                new_assumption(core[i], w3);
            }
        }
        return w;
    }

    void display_vec(std::ostream& out, unsigned sz, expr* const* args) {
        for (unsigned i = 0; i < sz; ++i) {
            out << mk_pp(args[i], m) << " : " << get_weight(args[i]) << " ";
        }
        out << "\n";
    }

    void display(std::ostream& out) {
        for (unsigned i = 0; i < m_asms.size(); ++i) {
            expr* a = m_asms[i].get();
            out << mk_pp(a, m) << " : " << get_weight(a) << "\n";
        }
    }

    void max_resolve(ptr_vector<expr>& core, rational const& w) {
        SASSERT(!core.empty());
        expr_ref fml(m), asum(m);
        app_ref cls(m), d(m), dd(m);
        m_B.reset();
        m_B.append(core.size(), core.c_ptr());
        d = m.mk_true();
        //
        // d_0 := true
        // d_i := b_{i-1} and d_{i-1}    for i = 1...sz-1
        // soft (b_i or !d_i)
        //   == (b_i or !(!b_{i-1} or d_{i-1}))
        //   == (b_i or b_0 & b_1 & ... & b_{i-1})
        //
        // Soft constraint is satisfied if previous soft constraint
        // holds or if it is the first soft constraint to fail.
        //
        // Soundness of this rule can be established using MaxRes
        //
        for (unsigned i = 1; i < core.size(); ++i) {
            expr* b_i = m_B[i-1].get();
            expr* b_i1 = m_B[i].get();
            if (i > 2) {
                dd = mk_fresh_bool("d");
                fml = m.mk_implies(dd, d);
                m_s->assert_expr(fml);
                fml = m.mk_implies(dd, b_i);
                m_s->assert_expr(fml);
                d = dd;
            }
            else {
                d = m.mk_and(b_i, d);
            }
            asum = mk_fresh_bool("a");
            cls = m.mk_or(b_i1, d);
            fml = m.mk_implies(asum, cls);
            new_assumption(asum, w);
            m_s->assert_expr(fml);
        }
    }

    // satc are the complements of a (maximal) satisfying assignment.
    void dual_max_resolve(ptr_vector<expr>& satc, rational const& w) {
        SASSERT(!satc.empty());
        expr_ref fml(m), asum(m);
        app_ref cls(m), d(m), dd(m);
        m_B.reset();
        m_B.append(satc.size(), satc.c_ptr());
        d = m.mk_false();
        //
        // d_0 := false
        // d_i := b_{i-1} or d_{i-1}    for i = 1...sz-1
        // soft (b_i and d_i)
        //   == (b_i and (b_0 or b_1 or ... or b_{i-1}))
        //
        // asm => b_i
        // asm => d_{i-1} or b_{i-1}
        // d_i => d_{i-1} or b_{i-1}
        for (unsigned i = 1; i < satc.size(); ++i) {
            expr* b_i = m_B[i-1].get();
            expr* b_i1 = m_B[i].get();
            cls = m.mk_or(b_i, d);
            if (i > 2) {
                d = mk_fresh_bool("d");
                fml = m.mk_implies(d, cls);
                m_s->assert_expr(fml);
            }
            else {
                d = cls;
            }
            asum = mk_fresh_bool("a");
            fml = m.mk_implies(asum, b_i1);
            m_s->assert_expr(fml);
            fml = m.mk_implies(asum, cls);
            m_s->assert_expr(fml);
            new_assumption(asum, w);
        }
        fml = m.mk_or(m_B.size(), m_B.c_ptr());
        m_s->assert_expr(fml);
    }

    //
    // The hard constraints are satisfiable.
    // Extend the current model to satisfy as many
    // soft constraints as possible until either
    // hitting an unsatisfiable subset of size < 1/2*#assumptions,
    // or producing a maximal satisfying assignment exceeding
    // number of soft constraints >= 1/2*#assumptions.
    // In both cases, soft constraints that are not satisfied
    // is <= 1/2*#assumptions. In this way, the new modified assumptions
    // account for at most 1/2 of the current assumptions.
    // The core reduction algorithms also need to take into account
    // at most 1/2 of the assumptions for minimization.
    //

    lbool extend_model(ptr_vector<expr>& soft_compl) {
        ptr_vector<expr> asms;
        model_ref mdl;
        expr_ref tmp(m);
        m_s->get_model(mdl);
        unsigned num_true = update_model(mdl, asms, soft_compl);
        for (unsigned j = 0; j < m_asms.size(); ++j) {
            expr* fml = m_asms[j].get();
            VERIFY(mdl->eval(fml, tmp));
            if (m.is_false(tmp)) {
                asms.push_back(fml);
                lbool is_sat = m_s->check_sat(asms.size(), asms.c_ptr());
                asms.pop_back();
                switch (is_sat) {
                case l_false:
                    if (num_true*2 < m_asms.size()) {
                        return l_false;
                    }
                    break;
                case l_true:
                    m_s->get_model(mdl);
                    num_true = update_model(mdl, asms, soft_compl);
                    break;
                case l_undef:
                    return l_undef;
                }
            }
        }
        return l_true;
    }

    unsigned update_model(model_ref& mdl, ptr_vector<expr>& asms, ptr_vector<expr>& soft_compl) {
        expr_ref tmp(m);
        asms.reset();
        soft_compl.reset();
        rational weight = m_lower;
        unsigned num_true = 0;
        for (unsigned i = 0; i < m_asms.size(); ++i) {
            expr* fml = m_asms[i].get();
            VERIFY(mdl->eval(fml, tmp));
            SASSERT(m.is_false(tmp) || m.is_true(tmp));
            if (m.is_false(tmp)) {
                weight += get_weight(fml);
                soft_compl.push_back(fml);
            }
            else {
                ++num_true;
                asms.push_back(fml);
            }
        }
        if (weight < m_upper) {
            m_upper = weight;
            m_model = mdl;
            for (unsigned i = 0; i < m_soft.size(); ++i) {
                expr_ref tmp(m);
                VERIFY(m_model->eval(m_soft[i].get(), tmp));
                m_assignment[i] = m.is_true(tmp);
            }
            IF_VERBOSE(1, verbose_stream() <<
                       "(opt.dual_max_res [" << m_lower << ":" << m_upper << "])\n";);
        }
        return num_true;
    }

    void remove_soft(ptr_vector<expr> const& core, expr_ref_vector& asms) {
        for (unsigned i = 0; i < asms.size(); ++i) {
            if (core.contains(asms[i].get())) {
                asms[i] = asms.back();
                asms.pop_back();
                --i;
            }
        }
    }

    void remove_core(ptr_vector<expr> const& core) {
        remove_soft(core, m_asms);
    }

    virtual void set_cancel(bool f) {
        maxsmt_solver_base::set_cancel(f);
        m_mus.set_cancel(f);
    }

    void init_local() {
        m_upper.reset();
        m_lower.reset();
        m_trail.reset();
        for (unsigned i = 0; i < m_soft.size(); ++i) {
            add_soft(m_soft[i].get(), m_weights[i]);
        }
    }

};

opt::maxsmt_solver_base* opt::mk_maxres(ast_manager& m, opt_solver* s, params_ref& p,
                                        vector<rational> const& ws, expr_ref_vector const& soft) {
    return alloc(maxres, m, s, p, ws, soft, maxres::s_mus);
}

<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
#include <osg/Notify>
#include <osg/ApplicationUsage>
#include <osg/ref_ptr>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <sstream>
#include <iostream>

#include <ctype.h>

#define OSG_INIT_SINGLETON_PROXY(ProxyName, Func) static struct ProxyName{ ProxyName() { Func; } } s_##ProxyName;

namespace osg
{

class NullStreamBuffer : public std::streambuf
{
private:
    std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
    {
        return n;
    }
};

struct NullStream : public std::ostream
{
public:
    NullStream():
        std::ostream(new NullStreamBuffer)
    { _buffer = dynamic_cast<NullStreamBuffer *>(rdbuf()); }

    ~NullStream()
    {
        rdbuf(0);
        delete _buffer;
    }

protected:
    NullStreamBuffer* _buffer;
};

/** Stream buffer calling notify handler when buffer is synchronized (usually on std::endl).
 * Stream stores last notification severity to pass it to handler call.
 */
struct NotifyStreamBuffer : public std::stringbuf
{
    NotifyStreamBuffer() : _severity(osg::NOTICE)
    {
    }

    void setNotifyHandler(osg::NotifyHandler *handler) { _handler = handler; }
    osg::NotifyHandler *getNotifyHandler() const { return _handler.get(); }

    /** Sets severity for next call of notify handler */
    void setCurrentSeverity(osg::NotifySeverity severity) { _severity = severity; }
    osg::NotifySeverity getCurrentSeverity() const { return _severity; }

private:

    int sync()
    {
        sputc(0); // string termination
        if (_handler.valid())
            _handler->notify(_severity, pbase());
        pubseekpos(0, std::ios_base::out); // or str(std::string())
        return 0;
    }

    osg::ref_ptr<osg::NotifyHandler> _handler;
    osg::NotifySeverity _severity;
};

struct NotifyStream : public std::ostream
{
public:
    NotifyStream():
        std::ostream(new NotifyStreamBuffer)
    { _buffer = dynamic_cast<NotifyStreamBuffer *>(rdbuf()); }

    void setCurrentSeverity(osg::NotifySeverity severity)
    {
        _buffer->setCurrentSeverity(severity);
    }

    osg::NotifySeverity getCurrentSeverity() const
    {
        return _buffer->getCurrentSeverity();
    }

    ~NotifyStream()
    {
        rdbuf(0);
        delete _buffer;
    }

protected:
    NotifyStreamBuffer* _buffer;
};

}

using namespace osg;

static osg::ApplicationUsageProxy Notify_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE, "OSG_NOTIFY_LEVEL <mode>", "FATAL | WARN | NOTICE | DEBUG_INFO | DEBUG_FP | DEBUG | INFO | ALWAYS");

struct NotifySingleton
{
    NotifySingleton()
    {
        // _notifyLevel
        // =============

        _notifyLevel = osg::NOTICE; // Default value

        char* OSGNOTIFYLEVEL=getenv("OSG_NOTIFY_LEVEL");
        if (!OSGNOTIFYLEVEL) OSGNOTIFYLEVEL=getenv("OSGNOTIFYLEVEL");
        if(OSGNOTIFYLEVEL)
        {

            std::string stringOSGNOTIFYLEVEL(OSGNOTIFYLEVEL);

            // Convert to upper case
            for(std::string::iterator i=stringOSGNOTIFYLEVEL.begin();
                i!=stringOSGNOTIFYLEVEL.end();
                ++i)
            {
                *i=toupper(*i);
            }

            if(stringOSGNOTIFYLEVEL.find("ALWAYS")!=std::string::npos)          _notifyLevel=osg::ALWAYS;
            else if(stringOSGNOTIFYLEVEL.find("FATAL")!=std::string::npos)      _notifyLevel=osg::FATAL;
            else if(stringOSGNOTIFYLEVEL.find("WARN")!=std::string::npos)       _notifyLevel=osg::WARN;
            else if(stringOSGNOTIFYLEVEL.find("NOTICE")!=std::string::npos)     _notifyLevel=osg::NOTICE;
            else if(stringOSGNOTIFYLEVEL.find("DEBUG_INFO")!=std::string::npos) _notifyLevel=osg::DEBUG_INFO;
            else if(stringOSGNOTIFYLEVEL.find("DEBUG_FP")!=std::string::npos)   _notifyLevel=osg::DEBUG_FP;
            else if(stringOSGNOTIFYLEVEL.find("DEBUG")!=std::string::npos)      _notifyLevel=osg::DEBUG_INFO;
            else if(stringOSGNOTIFYLEVEL.find("INFO")!=std::string::npos)       _notifyLevel=osg::INFO;
            else std::cout << "Warning: invalid OSG_NOTIFY_LEVEL set ("<<stringOSGNOTIFYLEVEL<<")"<<std::endl;

        }

        // Setup standard notify handler
        osg::NotifyStreamBuffer *buffer = dynamic_cast<osg::NotifyStreamBuffer *>(_notifyStream.rdbuf());
        if (buffer && !buffer->getNotifyHandler())
            buffer->setNotifyHandler(new StandardNotifyHandler);
    }

    osg::NotifySeverity _notifyLevel;
    osg::NullStream     _nullStream;
    osg::NotifyStream   _notifyStream;
};

static NotifySingleton& getNotifySingleton()
{
    static NotifySingleton s_NotifySingleton;
    return s_NotifySingleton;
}

bool osg::initNotifyLevel()
{
    getNotifySingleton();
    return true;
}

// Use a proxy to force the initialization of the the NotifySingleton during static initialization
OSG_INIT_SINGLETON_PROXY(NotifySingletonProxy, osg::initNotifyLevel())

void osg::setNotifyLevel(osg::NotifySeverity severity)
{
    getNotifySingleton()._notifyLevel = severity;
}

osg::NotifySeverity osg::getNotifyLevel()
{
    return getNotifySingleton()._notifyLevel;
}

void osg::setNotifyHandler(osg::NotifyHandler *handler)
{
    osg::NotifyStreamBuffer *buffer = static_cast<osg::NotifyStreamBuffer*>(getNotifySingleton()._notifyStream.rdbuf());
    if (buffer) buffer->setNotifyHandler(handler);
}

osg::NotifyHandler* osg::getNotifyHandler()
{
    osg::NotifyStreamBuffer *buffer = static_cast<osg::NotifyStreamBuffer *>(getNotifySingleton()._notifyStream.rdbuf());
    return buffer ? buffer->getNotifyHandler() : 0;
}

#ifndef OSG_NOTIFY_DISABLED
bool osg::isNotifyEnabled( osg::NotifySeverity severity )
{
    return severity<=getNotifySingleton()._notifyLevel;
}
#endif

std::ostream& osg::notify(const osg::NotifySeverity severity)
{
    if (osg::isNotifyEnabled(severity))
    {
        getNotifySingleton()._notifyStream.setCurrentSeverity(severity);
        return getNotifySingleton()._notifyStream;
    }
    return getNotifySingleton()._nullStream;
}

void osg::StandardNotifyHandler::notify(osg::NotifySeverity severity, const char *message)
{
    if (severity <= osg::WARN)
        fputs(message, stderr);
    else
        fputs(message, stdout);
}

#if defined(WIN32) && !defined(__CYGWIN__)

#ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

void osg::WinDebugNotifyHandler::notify(osg::NotifySeverity severity, const char *message)
{
    OutputDebugStringA(message);
}

#endif
<commit_msg>From Lionel Lagarde, "When a function do:<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
#include <osg/Notify>
#include <osg/ApplicationUsage>
#include <osg/ref_ptr>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <sstream>
#include <iostream>

#include <ctype.h>

#define OSG_INIT_SINGLETON_PROXY(ProxyName, Func) static struct ProxyName{ ProxyName() { Func; } } s_##ProxyName;

namespace osg
{

class NullStreamBuffer : public std::streambuf
{
private:
    std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
    {
        return n;
    }
};

struct NullStream : public std::ostream
{
public:
    NullStream():
        std::ostream(new NullStreamBuffer)
    { _buffer = dynamic_cast<NullStreamBuffer *>(rdbuf()); }

    ~NullStream()
    {
        rdbuf(0);
        delete _buffer;
    }

protected:
    NullStreamBuffer* _buffer;
};

/** Stream buffer calling notify handler when buffer is synchronized (usually on std::endl).
 * Stream stores last notification severity to pass it to handler call.
 */
struct NotifyStreamBuffer : public std::stringbuf
{
    NotifyStreamBuffer() : _severity(osg::NOTICE)
    {
    }

    void setNotifyHandler(osg::NotifyHandler *handler) { _handler = handler; }
    osg::NotifyHandler *getNotifyHandler() const { return _handler.get(); }

    /** Sets severity for next call of notify handler */
    void setCurrentSeverity(osg::NotifySeverity severity)
    {
        if (_severity != severity)
        {
            sync();
            _severity = severity;
        }
    }

    osg::NotifySeverity getCurrentSeverity() const { return _severity; }

private:

    int sync()
    {
        sputc(0); // string termination
        if (_handler.valid())
            _handler->notify(_severity, pbase());
        pubseekpos(0, std::ios_base::out); // or str(std::string())
        return 0;
    }

    osg::ref_ptr<osg::NotifyHandler> _handler;
    osg::NotifySeverity _severity;
};

struct NotifyStream : public std::ostream
{
public:
    NotifyStream():
        std::ostream(new NotifyStreamBuffer)
    { _buffer = dynamic_cast<NotifyStreamBuffer *>(rdbuf()); }

    void setCurrentSeverity(osg::NotifySeverity severity)
    {
        _buffer->setCurrentSeverity(severity);
    }

    osg::NotifySeverity getCurrentSeverity() const
    {
        return _buffer->getCurrentSeverity();
    }

    ~NotifyStream()
    {
        rdbuf(0);
        delete _buffer;
    }

protected:
    NotifyStreamBuffer* _buffer;
};

}

using namespace osg;

static osg::ApplicationUsageProxy Notify_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE, "OSG_NOTIFY_LEVEL <mode>", "FATAL | WARN | NOTICE | DEBUG_INFO | DEBUG_FP | DEBUG | INFO | ALWAYS");

struct NotifySingleton
{
    NotifySingleton()
    {
        // _notifyLevel
        // =============

        _notifyLevel = osg::NOTICE; // Default value

        char* OSGNOTIFYLEVEL=getenv("OSG_NOTIFY_LEVEL");
        if (!OSGNOTIFYLEVEL) OSGNOTIFYLEVEL=getenv("OSGNOTIFYLEVEL");
        if(OSGNOTIFYLEVEL)
        {

            std::string stringOSGNOTIFYLEVEL(OSGNOTIFYLEVEL);

            // Convert to upper case
            for(std::string::iterator i=stringOSGNOTIFYLEVEL.begin();
                i!=stringOSGNOTIFYLEVEL.end();
                ++i)
            {
                *i=toupper(*i);
            }

            if(stringOSGNOTIFYLEVEL.find("ALWAYS")!=std::string::npos)          _notifyLevel=osg::ALWAYS;
            else if(stringOSGNOTIFYLEVEL.find("FATAL")!=std::string::npos)      _notifyLevel=osg::FATAL;
            else if(stringOSGNOTIFYLEVEL.find("WARN")!=std::string::npos)       _notifyLevel=osg::WARN;
            else if(stringOSGNOTIFYLEVEL.find("NOTICE")!=std::string::npos)     _notifyLevel=osg::NOTICE;
            else if(stringOSGNOTIFYLEVEL.find("DEBUG_INFO")!=std::string::npos) _notifyLevel=osg::DEBUG_INFO;
            else if(stringOSGNOTIFYLEVEL.find("DEBUG_FP")!=std::string::npos)   _notifyLevel=osg::DEBUG_FP;
            else if(stringOSGNOTIFYLEVEL.find("DEBUG")!=std::string::npos)      _notifyLevel=osg::DEBUG_INFO;
            else if(stringOSGNOTIFYLEVEL.find("INFO")!=std::string::npos)       _notifyLevel=osg::INFO;
            else std::cout << "Warning: invalid OSG_NOTIFY_LEVEL set ("<<stringOSGNOTIFYLEVEL<<")"<<std::endl;

        }

        // Setup standard notify handler
        osg::NotifyStreamBuffer *buffer = dynamic_cast<osg::NotifyStreamBuffer *>(_notifyStream.rdbuf());
        if (buffer && !buffer->getNotifyHandler())
            buffer->setNotifyHandler(new StandardNotifyHandler);
    }

    osg::NotifySeverity _notifyLevel;
    osg::NullStream     _nullStream;
    osg::NotifyStream   _notifyStream;
};

static NotifySingleton& getNotifySingleton()
{
    static NotifySingleton s_NotifySingleton;
    return s_NotifySingleton;
}

bool osg::initNotifyLevel()
{
    getNotifySingleton();
    return true;
}

// Use a proxy to force the initialization of the the NotifySingleton during static initialization
OSG_INIT_SINGLETON_PROXY(NotifySingletonProxy, osg::initNotifyLevel())

void osg::setNotifyLevel(osg::NotifySeverity severity)
{
    getNotifySingleton()._notifyLevel = severity;
}

osg::NotifySeverity osg::getNotifyLevel()
{
    return getNotifySingleton()._notifyLevel;
}

void osg::setNotifyHandler(osg::NotifyHandler *handler)
{
    osg::NotifyStreamBuffer *buffer = static_cast<osg::NotifyStreamBuffer*>(getNotifySingleton()._notifyStream.rdbuf());
    if (buffer) buffer->setNotifyHandler(handler);
}

osg::NotifyHandler* osg::getNotifyHandler()
{
    osg::NotifyStreamBuffer *buffer = static_cast<osg::NotifyStreamBuffer *>(getNotifySingleton()._notifyStream.rdbuf());
    return buffer ? buffer->getNotifyHandler() : 0;
}

#ifndef OSG_NOTIFY_DISABLED
bool osg::isNotifyEnabled( osg::NotifySeverity severity )
{
    return severity<=getNotifySingleton()._notifyLevel;
}
#endif

std::ostream& osg::notify(const osg::NotifySeverity severity)
{
    if (osg::isNotifyEnabled(severity))
    {
        getNotifySingleton()._notifyStream.setCurrentSeverity(severity);
        return getNotifySingleton()._notifyStream;
    }
    return getNotifySingleton()._nullStream;
}

void osg::StandardNotifyHandler::notify(osg::NotifySeverity severity, const char *message)
{
    if (severity <= osg::WARN)
        fputs(message, stderr);
    else
        fputs(message, stdout);
}

#if defined(WIN32) && !defined(__CYGWIN__)

#ifndef WIN32_LEAN_AND_MEAN
    #define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

void osg::WinDebugNotifyHandler::notify(osg::NotifySeverity severity, const char *message)
{
    OutputDebugStringA(message);
}

#endif
<|endoftext|>


<commit_before>#include <hash_map.h>
#include "yatf/include/yatf.h"

using namespace yacppl;

TEST(hash_map, can_create_empty) {
    hash_map<int, int> map;
    REQUIRE_EQ(map.size(), 0u);
}

TEST(hash_map, can_add_elements) {
    hash_map<unsigned, int> map;
    map.insert(make_pair(23u, 43));
    REQUIRE_EQ(map.size(), 1u);
    {
        auto pair = map[23u];
        REQUIRE_EQ(pair->first, 23u);
        REQUIRE_EQ(pair->second, 43);
    }
    map.insert(make_pair(2053u, 4167));
    REQUIRE_EQ(map.size(), 2u);
    {
        auto pair = map[2053u];
        REQUIRE_EQ(pair->first, 2053u);
        REQUIRE_EQ(pair->second, 4167);
    }
    {
        auto pair = map[23u];
        REQUIRE_EQ(pair->first, 23u);
        REQUIRE_EQ(pair->second, 43);
    }
}

TEST(hash_map, cannot_get_nonexistent_element) {
    hash_map<unsigned, int> map;
    for (auto i = 0u; i < 1024u; ++i) {
        auto pair = map[i];
        REQUIRE_FALSE(pair);
    }
    map.insert(make_pair(23u, 43));
    for (auto i = 0u; i < 23u; ++i) {
        auto pair = map[i];
        REQUIRE_FALSE(pair);
    }
}

TEST(hash_map, can_erase) {
    hash_map<unsigned, int> map;
    map.insert(make_pair(914324u, 32));
    map.erase(914324u);
    REQUIRE_EQ(map.size(), 0u);
    auto node = map[914324u];
    REQUIRE_FALSE(node);
}

namespace {

template <typename T>
void check_type() {
    hash_map<T, char> map;
    map.insert(make_pair(T(), 'a'));
    auto pair = map[T()];
    REQUIRE(pair);
    REQUIRE_EQ(pair->second, 'a');
}

} // namspace

TEST(hash_map, works_with_simple_types_keys) {
    check_type<signed char>();
    check_type<unsigned char>();
    check_type<short>();
    check_type<unsigned short>();
    check_type<int>();
    check_type<unsigned int>();
    check_type<long>();
    check_type<long long>();
}

<commit_msg>Add next test for hash_map::erase<commit_after>#include <hash_map.h>
#include "yatf/include/yatf.h"

using namespace yacppl;

TEST(hash_map, can_create_empty) {
    hash_map<int, int> map;
    REQUIRE_EQ(map.size(), 0u);
}

TEST(hash_map, can_add_elements) {
    hash_map<unsigned, int> map;
    map.insert(make_pair(23u, 43));
    REQUIRE_EQ(map.size(), 1u);
    {
        auto pair = map[23u];
        REQUIRE_EQ(pair->first, 23u);
        REQUIRE_EQ(pair->second, 43);
    }
    map.insert(make_pair(2053u, 4167));
    REQUIRE_EQ(map.size(), 2u);
    {
        auto pair = map[2053u];
        REQUIRE_EQ(pair->first, 2053u);
        REQUIRE_EQ(pair->second, 4167);
    }
    {
        auto pair = map[23u];
        REQUIRE_EQ(pair->first, 23u);
        REQUIRE_EQ(pair->second, 43);
    }
}

TEST(hash_map, cannot_get_nonexistent_element) {
    hash_map<unsigned, int> map;
    for (auto i = 0u; i < 1024u; ++i) {
        auto pair = map[i];
        REQUIRE_FALSE(pair);
    }
    map.insert(make_pair(23u, 43));
    for (auto i = 0u; i < 23u; ++i) {
        auto pair = map[i];
        REQUIRE_FALSE(pair);
    }
}

TEST(hash_map, can_erase) {
    hash_map<unsigned, int> map;
    map.insert(make_pair(914324u, 32));
    REQUIRE_EQ(map.size(), 1u);
    map.erase(914324u);
    REQUIRE_EQ(map.size(), 0u);
    auto node = map[914324u];
    REQUIRE_FALSE(node);
    map.insert(make_pair(914324u, 9));
    map.insert(make_pair(0u, 823));
    map.insert(make_pair(324u, -125));
    map.insert(make_pair(13u, 0));
    map.insert(make_pair(923482455u, 1994));
    REQUIRE_EQ(map.size(), 5u);
    map.erase(13u);
    REQUIRE_EQ(map.size(), 4u);
    REQUIRE_FALSE(map[13u]);
    map.erase(0u);
    REQUIRE_EQ(map.size(), 3u);
    REQUIRE_FALSE(map[0u]);
    map.erase(324u);
    REQUIRE_EQ(map.size(), 2u);
    REQUIRE_FALSE(map[324u]);
    map.erase(914324u);
    REQUIRE_EQ(map.size(), 1u);
    REQUIRE_FALSE(map[914324u]);
    map.erase(923482455u);
    REQUIRE_EQ(map.size(), 0u);
    REQUIRE_FALSE(map[923482455u]);
}

TEST(hash_map, cannot_erase_nonexisting_keys) {
    hash_map<unsigned, int> map;
    for (auto i = 0u; i < 1024u; ++i) {
        map.erase(i);
        REQUIRE_EQ(map.size(), 0u);
    }
    map.insert(make_pair(23u, 43));
    for (auto i = 0u; i < 23u; ++i) {
        map.erase(i);
        REQUIRE_EQ(map.size(), 1u);
    }
}

namespace {

template <typename T>
void check_type() {
    hash_map<T, char> map;
    map.insert(make_pair(T(), 'a'));
    auto pair = map[T()];
    REQUIRE(pair);
    REQUIRE_EQ(pair->second, 'a');
}

} // namspace

TEST(hash_map, works_with_simple_types_keys) {
    check_type<signed char>();
    check_type<unsigned char>();
    check_type<short>();
    check_type<unsigned short>();
    check_type<int>();
    check_type<unsigned int>();
    check_type<long>();
    check_type<long long>();
}

<|endoftext|>