CEP 3 - Using the Mamba solver in conda
Title | Using the Mamba solver in conda |
Status | Accepted |
Author(s) | Jannis Leidel <jleidel@anaconda.com>, Jaime Rodríguez-Guerra <jrodriguez@quansight.com> |
Created | Jul 20, 2021 |
Updated | Jul 22, 2021 |
Discussion | https://github.com/conda/ceps/pulls/2 |
Implementation | NA |
Abstract
Conda's current default dependency solver based on pycosat has a number of downsides that led to community efforts writing a faster and more modern solver based on libsolv. The result was the basis for the alternative conda implementation called Mamba.
The intention of this CEP is to propose an experimental feature for conda to add support for the solver used in Mamba, eventually based on the plugin architecture proposed in CEP 2.
Motivation
Conda's functionality and usability is predicated on the runtime characteristics of its low-level dependency resolver since it's a major part of its architecture -- next to the code infrastructure for the command line interface, the environment management and the handling of repo and channel data.
Speed, accuracy, debuggability and other impacts on the user
experience are heavily reliant on the underlying functionality of the
solver and conda's ability to interpret and communicate it to the user.
For example long running calls of the conda install
command to resolve
complex environments and dependency trees directly impacts users'
real world experience of the package manager.
In addition, there is a wide-ranging proliferation of community efforts to improve the usability issues with conda outside the regular conda code project which makes it harder to retain a consistent technical and community roadmap. As such the experimental integration of the Mamba solver is to be considered but the first step to affect lasting change in the conda community and recognize the innovative solutions that have been worked on by community members.
Specification
Conda contains a solver registry in the conda.resolve
module, currently
(4.10.x) listing three solvers:
- pycosat (default), a wrapper around the PicoSAT solver
- pycryptosat, a wrapper around the CryptoMiniSat solver
- pysat, a generic wrapper for a number of SAT solvers, Glucose4 is used
These solvers expose a pure SAT interface: they are conda-agnostic and
only understand SAT clauses, generated by the Clauses
classes.
libsolv
and mamba
do not expose a pure SAT interface, so adding
support for this solver requires modifications higher in the abstraction
tree.
conda.core.solve
sits high up in the conda logic, right below the
command-line interface for the install
, update
, remove
and
create
commands. There, a class named Solver
is in charge of
collecting the index metadata, prefix state and other variables before
sending the list of required MatchSpec
objects to the underlying
conda.resolve.Resolve
instance.
The Resolve
class is too tied to the Clauses
class, and some of its
abstractions do not hold true for libsolv
. As a result, we need to
operate at the Solver
level. We have to subclass Solver
and replace its
Solver.solve_final_state()
method, which only requires us to return an
iterable of PackageRecord
objects that is then diffed with the initial
environment state and turned into a transaction by the other methods in
the base class.
The resulting subclass would live in a separate project
conda-libmamba-solver and only depends on libmambapy
(Mamba's
Python wrapper) and conda
itself. To enable the experimental solver,
we add a new key to the Context
class (experimental_solver
) and a
command-line flag (--experimental-solver
), which can take any of the
values added to a new Enum
in conda.base.constants
(ExperimentalSolverChoice
). The adequate solver class would be
returned by a helper function added to conda.core.solve
(_get_solver_class()
) once the context
has been initialized.
In a way, this means that the new solver class is pre-registered as a plugin, but must be installed separately. In the future, when the plugin system described in CEP 2 is fully implemented, this manual integration could be replaced by the necessary plugin hooks.
The default choice for the solver class must stay the same as long as the Mamba solver plugin is marked as experimental. Only after considerable functional and integration testing to verify the performance and solving improvements, it could be considered as a default candidate -- and only in a new major version of conda.
The metrics that define the success of this experiment are to be determined as part of the work on this CEP in coordination with the work on a separate CEP for the minimum viable solving behavior and the related test suite.
Backwards Compatibility
Given the differences in implementations and algorithms between the already supported solvers, it's to be expected that Mamba's solver will also produce slightly different solutions, depending to the requested set of dependencies.
To guarantee an acceptable degree of backwards compatibility with
conda
's behavior, we need to make the experimental solver
comply with the existing conda
integration test suite.
Any potential backward incompatibilities discovered as part of this work require formal documentation to be shared with the conda users and also inform the decision making around the status as an experimental solver.
In the future, the authors would like to see if it would be possible to compile an essential technical specification and prose documentation of the minimum viable solver logic, possibly accompanied by an automatic test suite of expected outcomes. Given the intricate nuances and implicit behavior of the existing logic, this task is deferred to a new CEP, yet to be drafted.
Reference
- conda: the conda package manager
- CEP 2: Add plugin architecture to conda
- Libsolv: the SAT solver that Mamba uses behind the scenes
- PicoSAT: the SAT solver wrapped by pycosat
- Mamba: the conda community reimplementation
- conda-libmamba-solver: The experimental libmamba solver integrations, packaged separately
Copyright
All CEPs are explicitly CC0 1.0 Universal.