Skip to content
Snippets Groups Projects
solution.cpp 7.17 KiB
#include "solution.h"

#include "config.h"
#include "input/data.h"
#include "input/time_window.h"
#include "lns/constraints/capacity/capacity_constraint.h"
#include "lns/constraints/constraint.h"
#include "lns/constraints/time_window/time_window_constraint.h"
#include "lns/solution/route.h"
#include "output/solution_checker.h"

#include <algorithm>
#include <bits/ranges_algo.h>
#include <bits/ranges_util.h>
#include <ranges>
#include <utility>
#include <vector>

void Solution::initPairBank()
{
    pairBank.clear();
    for (Pair const &pair: getData().getPairs())
    {
        pairBank.push_back(pair.getID());
    }
}

void Solution::initRoutes()
{
    routes.clear();
    for (unsigned int i = 0; i < NUMBER_VEHICLE; ++i)
    {
        routes.emplace_back();
    }
}

void Solution::initConstraints()
{
    constraints.clear();
    constraints.push_back(std::make_unique<CapacityConstraint>(*this));
    constraints.push_back(std::make_unique<TimeWindowConstraint>(*this));
}

void Solution::computeAndStoreSolutionCost()
{
    rawCost = computeSolutionCost();
    totalCost = rawCost + computePenalisation();
}

double Solution::computeSolutionCost() const
{
    double cost = 0.0;
    for (Route const &route: getRoutes())
    {
        cost += data::routeCost(data, route);
    }
    return cost;
}

double Solution::computePenalisation() const
{
    return getBank().size() * EXCLUSION_PENALTY + getNumberOfRoutes() * ROUTE_PENALTY;
}

int Solution::getNumberOfRoutes() const
{
    int count = 0;
    for (Route const &route: getRoutes())
    {
        if (!route.getRoute().empty())
        {
            count++;
        }
    }
    return count;
}

void Solution::init()
{
    initPairBank();
    initRoutes();
    initConstraints();
    computeAndStoreSolutionCost();
}

Solution::Solution(PDPTWData const &data, Solution::PairBank pairbank, std::vector<Route> routes, double rawCost,
                   double totalCost)
    : data(data), pairBank(std::move(pairbank)), routes(std::move(routes)), rawCost(rawCost), totalCost(totalCost)
{}

Solution::Solution(PDPTWData const &data) : data(data)
{
    init();
}

Solution Solution::emptySolution(PDPTWData const &data)
{
    Solution sol(data);
    return sol;
}

Solution::~Solution() noexcept = default;

Solution::Solution(Solution const &rhs) : Solution(rhs.getData())
{
    *this = rhs;
}

Solution &Solution::operator=(Solution const &rhs)
{
    if (this == &rhs) return *this;

    data = rhs.data;
    rawCost = rhs.rawCost;
    totalCost = rhs.totalCost;
    pairBank = rhs.pairBank;

    routes = rhs.routes;
    constraints.clear();
    std::ranges::transform(rhs.constraints, std::back_inserter(constraints), [this](auto const &constraintPtr) {
        return constraintPtr->clone(*this);
    });

    return *this;
}

Solution::Solution(Solution &&sol) noexcept : data(sol.data)
{
    *this = std::move(sol);
}

Solution &Solution::operator=(Solution &&sol) noexcept
{
    if (this == &sol)
    {
        return *this;
    }

    data = sol.data;
    rawCost = sol.rawCost;
    totalCost = sol.totalCost;

    pairBank = std::move(sol.pairBank);
    routes = std::move(sol.routes);
    constraints = std::move(sol.constraints);

    for (auto &constraint: constraints)
    {
        constraint->setSolution(*this);
    }

    return *this;
}

Solution::PairBank const &Solution::getBank() const
{
    return pairBank;
}

Solution::PairBank const &Solution::getPairBank() const
{
    return pairBank;
}

Solution::PairBank &Solution::getPairBank()
{
    return pairBank;
}

std::vector<Route> const &Solution::getRoutes() const
{
    return routes;
}

std::vector<Route> &Solution::getRoutes()
{
    return routes;
}

Route &Solution::getRoute(int routeIndex)
{
    if (routeIndex < 0 || routeIndex >= routes.size())
    {
        spdlog::error("Invalid route index: {}", routeIndex);
        throw std::out_of_range("Invalid route index.");
    }
    return routes.at(routeIndex);
}

Route const &Solution::getRoute(int routeIndex) const
{
    if (routeIndex < 0 || routeIndex >= routes.size())
    {
        spdlog::error("Invalid route index: {}", routeIndex);
        throw std::out_of_range("Invalid route index.");
    }
    return routes.at(routeIndex);
}

double Solution::getCost() const
{
    return rawCost + computePenalisation();
}

double Solution::getRawCost() const
{
    return rawCost;
}

PDPTWData const &Solution::getData() const
{
    return data.get();
}

std::vector<std::unique_ptr<Constraint>> const &Solution::getConstraints() const
{
    return constraints;
}

int Solution::requestsFulfilledCount() const
{
    int count = 0;
    for (Route const &route: getRoutes())
    {
        count += route.getRoute().size() / 2;
    }
    return count;
}

int Solution::getRouteIDOf(int locationID) const
{
    int routeIndex = 0;
    for (Route const &route: getRoutes())
    {
        std::vector<int> const &routeLocationIDs = route.getRoute();
        if (std::ranges::find(routeLocationIDs, locationID) != routeLocationIDs.end())
        {
            return routeIndex;
        }
        ++routeIndex;
    }
    // no routes contain the location
    return -1;
}

unsigned int Solution::missingPairCount() const
{
    return pairBank.size();
}

bool Solution::checkModification(AtomicRecreation const &modification) const
{
    ModificationCheckVariant const &checkVariant = modification.asCheckVariant();

    for (auto const &constraint: constraints)
    {
        if (!constraint->checkVariant(checkVariant))
        {
            return false;
        }
    }
    return true;
}

void Solution::beforeApplyModification(AtomicModification &modification) const
{
    // Pre-modification check
    check();
}

void Solution::afterApplyModification(AtomicModification &modification)
{
    // Constraint status update
    for (auto &constraint: constraints)
    {
        constraint->applyVariant(modification.asApplyVariant());
    }
}

void Solution::applyRecreateSolution(AtomicRecreation &modification)
{
    beforeApplyModification(modification);
    modification.modifySolution(*this);

    // Update the request bank
    if (int pairID = modification.getAddedPairs())
    {
        pairBank.erase(std::ranges::find(pairBank, pairID));
    }

    afterApplyModification(modification);
}

void Solution::applyDestructSolution(AtomicDestruction &modification)
{
    beforeApplyModification(modification);
    modification.modifySolution(*this);

    // Update the request bank
    std::vector<int> const &deletedPair = modification.getDeletedPairs();
    pairBank.insert(pairBank.end(), deletedPair.begin(), deletedPair.end());

    afterApplyModification(modification);
}

void Solution::check() const
{
    checker::checkSolutionCoherence(*this, getData());
}

void Solution::print() const
{
    std::cout << "\nRawCost: " << rawCost << "\n"
              << "TotalCost: " << totalCost << "\n";

    std::cout << "Pair Bank:\n";
    for (int const id: getBank())
    {
        std::cout << id << ", ";
    }

    std::cout << "\nRoutes:\n";
    int i = 0;
    for (Route const &route: getRoutes())
    {
        std::cout << "#" << i << ": ";
        route.print();
        ++i;
    }

    std::cout << "Constraints:\n";
    for (auto const &constraint: constraints)
    {
        constraint->print();
    }

    std::cout << "\n";
}