Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve solver error message by finding a minimal conflict set #5647

Open
grayjay opened this issue Oct 28, 2018 · 1 comment
Open

Improve solver error message by finding a minimal conflict set #5647

grayjay opened this issue Oct 28, 2018 · 1 comment

Comments

@grayjay
Copy link
Collaborator

grayjay commented Oct 28, 2018

Currently cabal generates the summarized solver log by rerunning the dependency solver and having it prefer goals from the first run's final conflict set. The final conflict set may not be a minimal conflict set, though, which means that some goals could be irrelevant to the main conflict. Those extra goals could add irrelevant conflicts to the summarized log. We could improve the log by first minimizing the conflict set.

We could minimize the conflict set by removing one goal at a time and checking whether the remaining goals form a conflict set, until no more goals can be removed. One way to check that a set of goals X forms a conflict set is to run the solver and have it prefer goals from X, and then check that the solver's final conflict set is equal to X or a subset of X.

The feature might increase run time significantly, but we could control it with a flag, for use in difficult cases where it is worth waiting for a better error message.

See https://old.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/ for an example where this feature could have helped. I reproduced the original error message with tomjaguarpaw/haskell-opaleye@6eabe92, GHC 8.6.1, and cabal-install-2.0.0.1. I installed hashable-1.2.7.0 and then ran cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z. cabal HEAD (738a511) gives a better error message, but it still contains irrelevant lines about opaleye tests and the dotenv package that could be removed by this feature:

Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: opaleye-0.6.7003.0 (user goal)
[__1] rejecting: opaleye:!test (constraint from config file, command line
flag, or user target requires opposite flag selection)
[__1] trying: opaleye:*test
[__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
[__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
[__4] next goal: contravariant (dependency of opaleye)
[__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
<1.5)
[__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
[__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
contravariant-1.2.0.1, contravariant-1.2 (conflict:
transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
<0.5)
[__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
[__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
base, dotenv, opaleye:test
grayjay added a commit to grayjay/cabal that referenced this issue Nov 12, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
grayjay added a commit to grayjay/cabal that referenced this issue Nov 17, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
grayjay added a commit to grayjay/cabal that referenced this issue Nov 18, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
@grayjay grayjay self-assigned this Nov 18, 2018
grayjay added a commit to grayjay/cabal that referenced this issue Nov 29, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
grayjay added a commit to grayjay/cabal that referenced this issue Dec 1, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
grayjay added a commit to grayjay/cabal that referenced this issue Dec 2, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
grayjay added a commit to grayjay/cabal that referenced this issue Dec 3, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
grayjay added a commit to grayjay/cabal that referenced this issue Dec 5, 2018
…haskell#5647).

This commit improves the error message in the case where the solver finds no
solution after an exhaustive search.  Previously, cabal generated the error
message by rerunning the solver and having it prefer goals from the final
conflict set, so that the error message only mentioned conflicts between
packages from the final conflict set.  Conflicts relating to the final conflict
set are more likely to be relevant, because the conflict set contains all
variables that are involved in one conflict that makes the dependencies
unsatisfiable.  However, the conflict set can also include variables that led to
some conflicts but aren't relevant to the main conflict.  This commit improves
the error message further by first trying to reduce the size of the conflict
set.

The current algorithm for reducing the conflict set simply reruns the solver
with different goal orders, so it has some downsides:

- Reducing the conflict set can be slow.  It is also possible for the solver to
  reach the backjump limit on a rerun, even though the first run completed.  In
  that case, it uses the original conflict set.

- The function can fail to remove some unnecessary variables from the conflict
  set.  In the worst case, it returns the original conflict set.

I tested the feature on the example in
https://www.reddit.com/r/haskell/comments/9rmh9s/how_to_read_cabal_solver_failure_output/
and haskell#5647.  Both runs used the command
"cabal install --only-dependencies --enable-tests --force-reinstalls --index-state=2018-10-26T20:30:16Z".

Before:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] rejecting: opaleye:!test (constraint from config file, command line
    flag, or user target requires opposite flag selection)
    [__1] trying: opaleye:*test
    [__2] trying: dotenv-0.6.0.3 (dependency of opaleye *test)
    [__3] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__4] next goal: contravariant (dependency of opaleye)
    [__4] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__4] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__4] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__4] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__4] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: transformers, contravariant, opaleye,
    base, dotenv, opaleye:test

After:

    Resolving dependencies...
    cabal: Could not resolve dependencies:
    [__0] trying: opaleye-0.6.7003.0 (user goal)
    [__1] trying: transformers-0.5.5.0/installed-0.5... (dependency of opaleye)
    [__2] next goal: contravariant (dependency of opaleye)
    [__2] rejecting: contravariant-1.5 (conflict: opaleye => contravariant>=1.2 &&
    <1.5)
    [__2] rejecting: contravariant-1.4.1, contravariant-1.4 (conflict:
    transformers => base==4.12.0.0/installed-4.1..., contravariant => base<4.12)
    [__2] rejecting: contravariant-1.3.3, contravariant-1.3.2,
    contravariant-1.3.1.1, contravariant-1.3.1, contravariant-1.3,
    contravariant-1.2.2.1, contravariant-1.2.2, contravariant-1.2.1,
    contravariant-1.2.0.1, contravariant-1.2 (conflict:
    transformers==0.5.5.0/installed-0.5..., contravariant => transformers>=0.2 &&
    <0.5)
    [__2] rejecting: contravariant-1.1, contravariant-1.0, contravariant-0.6.1.1,
    contravariant-0.6.1, contravariant-0.6, contravariant-0.5.2,
    contravariant-0.5.1, contravariant-0.5, contravariant-0.4.4,
    contravariant-0.4.3, contravariant-0.4.1, contravariant-0.4,
    contravariant-0.3, contravariant-0.2.0.2, contravariant-0.2.0.1,
    contravariant-0.2, contravariant-0.1.3, contravariant-0.1.2.1,
    contravariant-0.1.2, contravariant-0.1.1, contravariant-0.1.0.1,
    contravariant-0.1.0 (conflict: opaleye => contravariant>=1.2 && <1.5)
    [__2] fail (backjumping, conflict set: contravariant, opaleye, transformers)
    After searching the rest of the dependency tree exhaustively, these were the
    goals I've had most trouble fulfilling: base, opaleye, contravariant,
    transformers

In this case, the feature found a minimal conflict set, {base, opaleye,
contravariant, transformers}, by removing dotenv and opaleye:test from the
original conflict set.
23Skidoo added a commit that referenced this issue Dec 5, 2018
Solver: Improve error message by finding a minimal conflict set (issue #5647).
@grayjay
Copy link
Collaborator Author

grayjay commented Dec 10, 2018

This feature was implemented in #5680, but it is off by default because it reruns the solver and increases run time significantly. I'll leave this issue open for finding a way to control the run time and changing the default to true.

One way to limit the run time is to apply the backjump limit to the combined solver runs, for example, count 5 runs with 100 backjumps each as 500 backjumps. That would require returning the backjump count from the solver, which is related to #4594.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant