@juliank@mastodon.social
@juliank@mastodon.social avatar

juliank

@juliank@mastodon.social

Debian Developer, Ubuntu Core Developer, Software Engineer II at Canonical. Your friendly neighborhood APT maintainer. Vegan. He/him.

Love cooking, cycling, walking, music, and netflix.

This profile is from a federated server and may be incomplete. Browse more on the original instance.

juliank, to random
@juliank@mastodon.social avatar

I do have fancy profiling graph for the solver. I ran the solver 20 times from Python on a cache, it took:

Resolve: 90.10825674049556 +/- 7.953068206569682 ms

Same range as current solver.

Thanks to -fno-omit-frame-pointer on Ubuntu 24.04 this call graph actually works reasonably well!

juliank, to random
@juliank@mastodon.social avatar

So you told solver3 to

apt install foo=1 --no-strict-pinning

But you have foo=2 and foo=3 too. What will it do?

(1) It marks foo=1 for install
(2) It marks foo=2, foo=3 and (3) their transitive reverse deps as not for install

Now it reaches a bar with depends foo. It will only be able to pick foo=1 due to (1) above.

You reach a baz=1/2/3 Depends foo=1/2/3. Now baz=2/3 have been rejected already in (2) so it will just try 1 directly.

Without (3) we'd try all baz in order and backtrack.

juliank, to random
@juliank@mastodon.social avatar

Lots of changes for solver3. I made it very slow over the past day, but I solved the fundamental issue in my approach now and we're back to competitive with the classic apt solver.

There's still more performance potential incoming. One problem is that we queue dependencies that have a single solution rather than recursively install them.

If we mark them recursively we can do it in one big transaction and avoid the need for some rescoring if we see some Conflicts.

So much fun.

juliank,
@juliank@mastodon.social avatar

Also more comments, some cleanup here and there, basically polishing this to get production ready. I think all upgrade variants, install, remove, autoremove work now.

Still no autoremove messaging however. Later!

Backtracking seems solid now.
Work items are ordered by actually possible choices, rather than all of them.
Performance is up 100%.

Anyhow it's getting late and I should have been asleep 2 hours ago already. Gaaar.

Toodles.

juliank, to random
@juliank@mastodon.social avatar

I was missing negative unit propagation in my dependency solver hence it was just running forever trying to install apt-utils=2.7.14build2 in all places it could backtrack to.

Like it doesn't do logical backtracking, it just backtracks to the previous decision level.

Now I propagate correctly

apt=2.7.14build2
-> not apt=<other version>
-> not any of its reverse dependencies

And it works correctly.

juliank,
@juliank@mastodon.social avatar

So yes if you squint you find that at its current stage, solver3 is a DPLL solver without pure literal elimination.

Well almost: It's not entirely as it's backtracking is not strictly chronological.

If I queue

A|B, C|D

I try A and fail, my queue now looks like

C|D, A|B

(i.e. the queue is a heap, and insertion is stable, so A|B is not moved ahead of C|D since they are equal).

This may be a problem, I think I run into local minima of unsatisfied dependencies.

juliank,
@juliank@mastodon.social avatar

Notably apk does not order its work items by the number of choices it makes but by the depth of its dependency trees and that is curious.

juliank, to random
@juliank@mastodon.social avatar

Anyway so solver3 currently stores a partial implication graph; recording only the first thing it sees implying something.

To implement conflict-driven clause learning like the state of the ART sat solvers we're going to need to make that a list.

But then we can start doing the dance of finding the cut-off point in the graph and non-chronologically backjumping that CDCL does.

But even now it works quite nice, there's not much need to do backtracking really in most cases.

juliank,
@juliank@mastodon.social avatar

Heck, we do not even do unit propagation of negative units yet; i.e. if we mark a package as not being installed we do not mark its reverse dependencies as not being installed.

This requires us to add "negative" work items. So like "install one of these" it is "mark all of these as broken".

i.e. we install a, a Conflicts b. We insert a work item to mark b as not installable.

We get to "b is not installable". We mark it as such and then queue its reverse dependencies as not installable.

juliank,
@juliank@mastodon.social avatar

Note the clue is that we are transforming a normal recursive graph walk into an iterative algorithm by using a datastructure.

If you use a stack you get the same as a normal algorithm.

But we actually use a priority queue, which means we don't actually walk the graph as breadth-first or depth-first, but "strongest first".

This is effectively the same thing as unit propagation in a SAT solver. As long as we have A->B we mark B for installing before trying to make a choice.

juliank, to random
@juliank@mastodon.social avatar

DPLL has a "Pure literal elimination" phase that makes it a good sat solver. If a literal exists only in a single polarity in your formular, you can just assign it that value.

This does not work for dependency solvers however. To translate this to a dependency solver:

It would install every package that has no conflicts

Anyway thinking about literals and CNF formular is much harder than thinking about the whole thing in terms of dependencies.

juliank, to random
@juliank@mastodon.social avatar

APT solver update:

I didn't actually hack on it but I remembered I had dumps from the noble time_t upgrades with problems, and I ran solver3 on those and it performed decently, so I am quite happy.

Shows the backtracking is working correctly.

Still need to actually go and record the unsatisfiability cores (i.e. A->B->C->D and X->Y->Z but D conflicts Z) before backtracking so I can show them to the user at the end. And to avoid making the same bad decisions again in different branches...

juliank, to random
@juliank@mastodon.social avatar

APT solver as a snap:

I anticipate the new apt solver to be available in the snap store this or next week, as the "solver3" snap. You will then be able to use it using

apt --solver /snap/bin/solver3

This is quite a slow experience as it uses the external dependency solver protocol but you can test with it on all releases.

For performance evaluation you'll have to wait for the 2.9.x release and use --solver 3.0.

zacchiro, to random
@zacchiro@mastodon.xyz avatar

So-called "technology transfer" at universities is a disease for [open] science. Episode .

A student of mine asked the authors of a security paper, involving LLMs and cyberattacks, details about their experimental setting, which were crucial for reproducibility (and reviewability…).

Answer (from tech. transfer staff): « To receive the code and the prompts, [university] would need to enter into a license agreement (with strong confidentiality clauses and a $30,000 onetime fee) »

juliank,
@juliank@mastodon.social avatar

@zacchiro ffs

juliank, to random
@juliank@mastodon.social avatar

Took a bit to figure out but
std::sort()

reads from outside the given range if your operator< is inconsistent, aka returning true for A==B.

Causing stuff to crash of course.

juliank, to random
@juliank@mastodon.social avatar

So I am missing error reporting somewhere on the backtracking path; but if backtracking is successful, you are good to go. e.g. install libapt-pkg6.0t64=2.7.14build2 and change versions accordingly to satisfy dependencies. Works.

juliank, to random
@juliank@mastodon.social avatar

Solver3 status:

Backtracking is hilariously broken but we are getting somewhere. It's only enabled for Recommends right now but there's some weird memory issues everywhere due to the item moving between two std::vector. No Conflicts learning

The barebones why sort of works:
I can print you dependency chains outlining the reason for each package being installed. Notably they're not the most readable, as they do cross provides.

So a Depends B, x Provides B, x depends c

Shows up as a -> x -> b.

juliank, to random
@juliank@mastodon.social avatar

Well so far the archive is pretty fallback free, so solver3 can now install ubuntu-desktop and kubuntu-desktop and the end result is comparable to apt.

Minor differences if you install both ubuntu and kubuntu desktops, you lose 3 packages:

  • qtspeech5-speechd-plugin because qtspeech5-flite-plugin is already installed.

  • sddm-theme-maya because sddm-theme-breeze is already installed

  • xserver-xorg-input-all xserver-xorg-input-libinput because xserver-xorg-input-wacom is installed (UGH)

juliank, to random
@juliank@mastodon.social avatar

Technically each dependency can be required by multiple packages but I only track the first one I see.

juliank, to random
@juliank@mastodon.social avatar

The problem with the solver design without optimization is that each fallback doubles the runtime, in the worst case:

Let's say you have a 100 packages you want to upgrade. But all of them fail to be upgraded.

You need 2**100 attempts to figure that out.

If instead you upgrade the first package first, then try to upgrade the 2nd package, and so on, you need 2*100 attempts, but the solver will be less optimal.

juliank,
@juliank@mastodon.social avatar

Your resulting graph will be less strongly connected because if you have an A depends X|Y and B depends Y, you get X, Y installed because you fully solve A before solving B.

But the advantage is that it is 6e27 times faster.

juliank,
@juliank@mastodon.social avatar

But also we don't have to compromise that much, we can still be 6e27 times faster and get some of the benefit: You can cheat and pull up the common dependencies of all your New Version | Installed version items and solve them first.

juliank, to random
@juliank@mastodon.social avatar

Can I make the dependency solver calculate strongly connected components of the dependency graph to speed it up?

juliank,
@juliank@mastodon.social avatar

The weird spoiler is that we already do in a sense because we solve all single-choice dependencies first, so we will effectively solve strongly connected components easily.

But or groups are a challenge. Many solutions will share dependencies and we need to extract them.

juliank, to random
@juliank@mastodon.social avatar

Regarding unsatisfiable dependencies:

I believe they are a bug in the archive. Falling back from A to B in A|B because A is uninstallable is a bad thing.

You temporarily break A's dependencies and then people installing during that time get B while everyone before and after gets A, leading to unintentional deviation.

1/2

juliank,
@juliank@mastodon.social avatar

How can we address the issue or limit it?

The fallback is only bad if A cannot be satisfied on its own. If A can be satisfied on its own it's only unsatisfiable within the context of your system, so it's not a packaging bug, but the result from a choice you made.

Translating this to a solver, if A fails to install due to missing dependencies, the solving will fail. But if A fails to install due to Conflicts, we will fall back to B.

  • All
  • Subscribed
  • Moderated
  • Favorites
  • Leos
  • kavyap
  • ngwrru68w68
  • hgfsjryuu7
  • magazineikmin
  • InstantRegret
  • DreamBathrooms
  • thenastyranch
  • Youngstown
  • slotface
  • PowerRangers
  • khanakhh
  • vwfavf
  • rosin
  • anitta
  • osvaldo12
  • everett
  • tester
  • cubers
  • mdbf
  • tacticalgear
  • ethstaker
  • Durango
  • modclub
  • normalnudes
  • cisconetworking
  • GTA5RPClips
  • provamag3
  • All magazines