Work in progress
This page of the documentation is not yet finished and only contains a draft of the content.
Technical specification: solver state
This document is a technical specification, which might not be the best way to learn about how the solver works. For that, refer to conda install and Solvers.
Solver API will pass a collection of
MatchSpec objects (from now on, we will refer to
specs) to the underlying SAT solver. How this list is built from the prefix state
and context options is not a straightforward process, but an elaborate logic. This is better
understood if we examine the ingredients that participate in the construction of
will label them like this:
These groups below will not change during the solver attempts:
MatchSpecobjects the user is explicitly asking for.
installed: Installed packages, expressed as
PrefixRecordobjects. Empty if the environment did not exist.
history: Specs asked in the past: the
History. Empty if the environment did not exist.
aggressive_updates: Packages included in the aggressive updates list. These packages are always included in any requests to make sure they stay up-to-date under all circumstances.
pinned: Packages pinned to a specific version, either via
.condarcor defined in a
virtual: System properties exposed as virtual packages (e.g.
__glibc=2.17). They can’t really be installed or uninstalled, but they do participate in the solver by adding runtime constraints.
do_not_remove: A fixed list of packages that receive special treatment by the solver due to poor metadata in the early days of conda packaging. A legacy leftover.
This one group does change during the solver lifetime:
conflicting: Specs that are suspected to be a conflict for the solver.
Also, two more sources that are not obvious at first. These are not labeled as a source, but they
do participate in the
In new environments, packages included in the
MatchSpecobjects are injected in each
conda createcommand, so the solver will see them as explicitly requested by the user (
Specs added by command line modifiers. The specs here present aren’t new (they are already in other categories), but they might end up in the
specslist only when a flag is added. For example,
update --allwill add all the installed packages to the
specslist, with no version constraint. Without this flag, the installed packages will still end up in the
specslist, but with full constraints (
--freeze-installeddefaults for the first attempt) unless:
Frozen attempt failed.
--update-specs(or any other
UpdateModifier) was passed, overriding
See? It gets involved. We will also use this vocabulary to help narrow down the type of change being done:
specs: map of package name to its currently corresponding
spec: specific instance of a
Exact or frozen
specwhere both the
buildfields are constrained with
==operators (exact match).
Fully constrained or tight
buildare populated, but not necessarily with equality operators. It can also be inequalities (
<, etc.) and fuzzy matches (
specwhere only the
versionfield is populated. The
Name-only, bare, or unconstrained
buildfields. Just the name of the package.
targetfield populated. Extracted from the comments in the solver logic:
targetis a reference to the package currently existing in the environment. Setting
targetinstructs the solver to not disturb that package if it’s not necessary. If the spec.name is being modified by inclusion in
specs_to_add, we don’t set
target, since we want the solver to modify/update that package.
TL;DR: when working with
to minimize the version change, set
to freeze the package, set all the components of
specobject does not have an adjective, it should be assumed it’s being added to the
specsmap unmodified, as it came from its origin.
Pools (collections of
Installed pool: The installed packages, grouped by name. Each group should only contain one record!
Explicit pool: The full index, but reduced for the specs in
The following sections will get dry and to the point. They will state what output to expect from
a given set of initial conditions. At least we’ll try. Take into account that the
is kept around across attempts! In other words, the
specs list is only really empty in the first
attempt; if this fails, the subsequent attempts will only overwrite (update) the existing one. In
practice, this should only affect how constrained packages are. The names should be the same.
It will also depend on whether we are adding (
conda install|create|update) or removing
conda remove) packages. There’s a common initialization part for both, but after that the
logic is separate.
Note: This happens in
This happens regardless of the type of command we are using (
Add specs from
history, if any.
Add specs from
do_not_remove, but only if:
There’s no spec for that name in
A package with that name is not installed.
virtualpackages as unconstrained
Add all those installed packages, as unconstrained
specs, that satisfy any of these conditions:
The history is empty (in that case, all installed packages are added)
The package name is part of
The package was not installed by
conda, but by pip or other PyPI tools instead.
Preparing the index
At this point, the populated
specs and the
requested specs are merged together. This temporary
collection is used to determine how to reduce the index.
Processing specs for
Generate the explicit pool for the requested specs (via
Detect potential conflicts (via (
specs that match installed records
Check that each of
specsmatch a single installed package or none! If there are two or more matches, it means that the environment is in bad shape and is basically broken. If the
specmatches one installed package (let’s call it installed match), we will modify the original
We will turn the
specinto an exact (frozen) spec if:
The installed match is unmanageable (installed by pip, virtual, etc.)
There’s no history, we are not in
The spec is not a potential conflict, and
The package name cannot be found in the explicit pool index or, if it is, the installed match can be found in that explicit pool (to guarantee it will be found instead of creating one more conflict just because).
We relax the
specto a name-only
specif it’s part of the aggressive updates list.
We turn it into a targeted
The spec is in
history. In that case, we take its historic spec counterpart and set the target to the installed match version and build.
None of the above conditions were met. In other words, we’ll try our best to match the installed package if none of the above applies, but if we fail we’ll stick to whatever was already present in the