Current File : /home/inlingua/miniconda3/pkgs/libmamba-2.0.5-haf1ee3a_1/include/mamba/solver/problems_graph.hpp |
// Copyright (c) 2019, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#ifndef MAMBA_PROBLEMS_GRAPH_HPP
#define MAMBA_PROBLEMS_GRAPH_HPP
#include <array>
#include <functional>
#include <initializer_list>
#include <ostream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
#include <fmt/color.h>
#include "mamba/specs/match_spec.hpp"
#include "mamba/specs/package_info.hpp"
#include "mamba/util/flat_set.hpp"
#include "mamba/util/graph.hpp"
namespace mamba::solver
{
namespace libsolv
{
class Database;
}
template <typename T>
class conflict_map : private std::unordered_map<T, util::flat_set<T>>
{
public:
using Base = std::unordered_map<T, util::flat_set<T>>;
using typename Base::const_iterator;
using typename Base::key_type;
using typename Base::value_type;
conflict_map() = default;
conflict_map(std::initializer_list<std::pair<T, T>> conflicts_pairs);
using Base::empty;
using Base::size;
[[nodiscard]] auto has_conflict(const key_type& a) const -> bool;
[[nodiscard]] auto conflicts(const key_type& a) const -> const util::flat_set<T>&;
[[nodiscard]] auto in_conflict(const key_type& a, const key_type& b) const -> bool;
using Base::cbegin;
using Base::cend;
auto begin() const noexcept -> const_iterator;
auto end() const noexcept -> const_iterator;
using Base::clear;
auto add(const key_type& a, const key_type& b) -> bool;
auto remove(const key_type& a, const key_type& b) -> bool;
auto remove(const key_type& a) -> bool;
private:
auto base() const -> const Base&;
auto remove_asym(const key_type& a, const key_type& b) -> bool;
template <typename TT>
friend auto operator==(const conflict_map<TT>& lhs, const conflict_map<TT>& rhs) -> bool;
template <typename TT>
friend auto operator!=(const conflict_map<TT>& lhs, const conflict_map<TT>& rhs) -> bool;
};
template <typename T>
auto operator==(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool;
template <typename T>
auto operator!=(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool;
/**
* A directed graph of the packages involved in a libsolv conflict.
*/
class ProblemsGraph
{
public:
struct RootNode
{
};
struct PackageNode : specs::PackageInfo
{
};
struct UnresolvedDependencyNode : specs::MatchSpec
{
};
struct ConstraintNode : specs::MatchSpec
{
};
using node_t = std::variant<RootNode, PackageNode, UnresolvedDependencyNode, ConstraintNode>;
using edge_t = specs::MatchSpec;
using graph_t = util::DiGraph<node_t, edge_t>;
using node_id = graph_t::node_id;
using conflicts_t = conflict_map<node_id>;
ProblemsGraph(graph_t graph, conflicts_t conflicts, node_id root_node);
[[nodiscard]] auto graph() const noexcept -> const graph_t&;
[[nodiscard]] auto conflicts() const noexcept -> const conflicts_t&;
[[nodiscard]] auto root_node() const noexcept -> node_id;
private:
graph_t m_graph;
conflicts_t m_conflicts;
node_id m_root_node;
};
/**
* Hand-crafted hurisitics to simplify conflicts in messy situations.
*/
auto simplify_conflicts(const ProblemsGraph& pbs) -> ProblemsGraph;
class CompressedProblemsGraph
{
public:
using RootNode = ProblemsGraph::RootNode;
/**
* A rough comparison for nodes.
*
* We need to be able to compare nodes for using them in a set but we do not have
* proper version parsing.
* Ideally we would like proper comparison for printing packages in order.
*
* As with ``NamedList`` below, the implementation is currently kept private for
* needed types.
*/
template <typename T>
struct RoughCompare
{
auto operator()(const T& a, const T& b) const -> bool;
};
/**
* A list of objects with a name, version, and build member.
*
* For simplicity, the implementation is kept in the translation unit with
* specialization for needed types.
*/
template <typename T, typename Allocator = std::allocator<T>>
class NamedList : private util::flat_set<T, RoughCompare<T>, Allocator>
{
public:
using Base = util::flat_set<T, RoughCompare<T>, Allocator>;
using typename Base::allocator_type;
using typename Base::const_iterator;
using typename Base::const_reverse_iterator;
using typename Base::value_type;
NamedList() = default;
template <typename InputIterator>
NamedList(InputIterator first, InputIterator last);
using Base::empty;
using Base::size;
auto front() const noexcept -> const value_type&;
auto back() const noexcept -> const value_type&;
using Base::cbegin;
using Base::cend;
using Base::crbegin;
using Base::crend;
auto begin() const noexcept -> const_iterator;
auto end() const noexcept -> const_iterator;
auto rbegin() const noexcept -> const_reverse_iterator;
auto rend() const noexcept -> const_reverse_iterator;
[[nodiscard]] auto name() const -> const std::string&;
[[nodiscard]] auto versions_trunc(
std::string_view sep = "|",
std::string_view etc = "...",
std::size_t threshold = 5,
bool remove_duplicates = true
) const -> std::pair<std::string, std::size_t>;
[[nodiscard]] auto build_strings_trunc(
std::string_view sep = "|",
std::string_view etc = "...",
std::size_t threshold = 5,
bool remove_duplicates = true
) const -> std::pair<std::string, std::size_t>;
[[nodiscard]] auto versions_and_build_strings_trunc(
std::string_view sep = "|",
std::string_view etc = "...",
std::size_t threshold = 5,
bool remove_duplicates = true
) const -> std::pair<std::string, std::size_t>;
using Base::clear;
using Base::reserve;
void insert(const value_type& e);
void insert(value_type&& e);
template <typename InputIterator>
void insert(InputIterator first, InputIterator last);
private:
template <typename T_>
void insert_impl(T_&& e);
};
using PackageListNode = NamedList<ProblemsGraph::PackageNode>;
using UnresolvedDependencyListNode = NamedList<ProblemsGraph::UnresolvedDependencyNode>;
using ConstraintListNode = NamedList<ProblemsGraph::ConstraintNode>;
using node_t = std::variant<
RootNode, //
PackageListNode,
UnresolvedDependencyListNode,
ConstraintListNode>;
using edge_t = NamedList<specs::MatchSpec>;
using graph_t = util::DiGraph<node_t, edge_t>;
using node_id = graph_t::node_id;
using conflicts_t = conflict_map<node_id>;
using merge_criteria_t = std::function<
bool(const ProblemsGraph&, ProblemsGraph::node_id, ProblemsGraph::node_id)>;
static auto
from_problems_graph(const ProblemsGraph& pbs, const merge_criteria_t& merge_criteria = {})
-> CompressedProblemsGraph;
CompressedProblemsGraph(graph_t graph, conflicts_t conflicts, node_id root_node);
[[nodiscard]] auto graph() const noexcept -> const graph_t&;
[[nodiscard]] auto conflicts() const noexcept -> const conflicts_t&;
[[nodiscard]] auto root_node() const noexcept -> node_id;
private:
graph_t m_graph;
conflicts_t m_conflicts;
node_id m_root_node;
};
/**
* Formatting options for error message functions.
*/
struct ProblemsMessageFormat
{
fmt::text_style unavailable = fmt::fg(fmt::terminal_color::red);
fmt::text_style available = fmt::fg(fmt::terminal_color::green);
std::array<std::string_view, 4> indents = { "│ ", " ", "├─ ", "└─ " };
};
auto print_problem_tree_msg(
std::ostream& out,
const CompressedProblemsGraph& pbs,
const ProblemsMessageFormat& format = {}
) -> std::ostream&;
auto problem_tree_msg( //
const CompressedProblemsGraph& pbs,
const ProblemsMessageFormat& format = {}
) -> std::string;
/************************************
* Implementation of conflict_map *
************************************/
template <typename T>
conflict_map<T>::conflict_map(std::initializer_list<std::pair<T, T>> conflicts_pairs)
{
for (const auto& [a, b] : conflicts_pairs)
{
add(a, b);
}
}
template <typename T>
auto conflict_map<T>::has_conflict(const key_type& a) const -> bool
{
return Base::find(a) != end();
}
template <typename T>
auto conflict_map<T>::conflicts(const key_type& a) const -> const util::flat_set<T>&
{
return Base::at(a);
}
template <typename T>
auto conflict_map<T>::in_conflict(const key_type& a, const key_type& b) const -> bool
{
return has_conflict(a) && Base::at(a).contains(b);
}
template <typename T>
auto conflict_map<T>::begin() const noexcept -> const_iterator
{
return Base::begin();
}
template <typename T>
auto conflict_map<T>::end() const noexcept -> const_iterator
{
return Base::end();
}
template <typename T>
auto conflict_map<T>::add(const key_type& a, const key_type& b) -> bool
{
auto [_, inserted] = Base::operator[](a).insert(b);
if (a != b)
{
Base::operator[](b).insert(a);
}
return inserted;
}
template <typename T>
auto conflict_map<T>::base() const -> const Base&
{
return static_cast<const Base&>(*this);
}
template <typename T>
auto conflict_map<T>::remove_asym(const key_type& a, const key_type& b) -> bool
{
auto iter = Base::find(a);
if (iter == Base::end())
{
return false;
}
auto& cflcts = iter->second;
const bool erased = cflcts.erase(b);
if (cflcts.empty())
{
Base::erase(a);
}
return erased;
};
template <typename T>
auto conflict_map<T>::remove(const key_type& a, const key_type& b) -> bool
{
return remove_asym(a, b) && ((a == b) || remove_asym(b, a));
}
template <typename T>
auto conflict_map<T>::remove(const key_type& a) -> bool
{
auto a_iter = Base::find(a);
if (a_iter == Base::end())
{
return false;
}
for (const auto& b : a_iter->second)
{
if (a != b) // Cannot modify while we iterate on it
{
remove_asym(b, a);
}
}
Base::erase(a);
return true;
}
template <typename T>
auto operator==(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool
{
return lhs.base() == rhs.base();
}
template <typename T>
auto operator!=(const conflict_map<T>& lhs, const conflict_map<T>& rhs) -> bool
{
return !(lhs == rhs);
}
/*********************************
* Implementation of NamedList *
*********************************/
template <typename T, typename A>
template <typename InputIterator>
void CompressedProblemsGraph::NamedList<T, A>::insert(InputIterator first, InputIterator last)
{
for (; first < last; ++first)
{
insert(*first);
}
}
}
#endif