On Fri, 09 Jun 2017 14:54:07 +0200
Michał Górny <mgo...@gentoo.org> wrote:

> On pią, 2017-06-09 at 13:41 +0200, Alexis Ballier wrote:
> > On Fri, 09 Jun 2017 11:19:20 +0200
> > Michał Górny <mgo...@gentoo.org> wrote:
> >   
> > > On śro, 2017-06-07 at 11:56 +0200, Alexis Ballier wrote:  
> > > > On Wed, 07 Jun 2017 11:27:59 +0200
> > > > Michał Górny <mgo...@gentoo.org> wrote:
> > > >     
> > > > > On śro, 2017-06-07 at 10:17 +0200, Alexis Ballier wrote:    
> > > > > > > Also, do I presume correctly that for all supported cases
> > > > > > > (i.e. those which your nsolve does not reject), solve and
> > > > > > > nsolve are compatible?     
> > > > > > 
> > > > > > Not sure what you mean here. nsolve does not solve
> > > > > > anything, it just validates REQUIRED_USE so that it is
> > > > > > guaranteed it can be solved.      
> > > > > 
> > > > > What I mean is whether you can guarantee that:
> > > > > 
> > > > > a. for every X that nsolve(X) == ok, solve() will be able to
> > > > > find a valid solution,    
> > > > 
> > > > yes
> > > >     
> > > > > b. for every X that solve() can solve reliably, nsolve(X) ==
> > > > > ok.    
> > > > 
> > > > no and that's not really possible    
> > > 
> > > I thought so. To expand it a little, could you confirm whether I
> > > correctly presume that:
> > > 
> > > a. for all 'good' results, the plain iterative solve() should be
> > > able to find a solution with a single iteration?  
> > 
> > yes that's the point of it
> >   
> > > b. for all 'need toposort' results, the solve() should be able to
> > > find a solution with n>=1 iterations?  
> > 
> > yes; though n is only bounded by the # of clauses (expanded as
> > implications) while it can be 1 if reorderer properly; I wouldn't
> > bother doing several iterations and just reject that at repoman
> > side since it's easily solved  
> 
> I would prefer not having Portage fail randomly on local ebuilds where
> the cost of multiple iterations is practically zero, and certainly
> it's not worth the effort to ensure a particular ordering.


Yep, makes sense.

It makes it harder to guess though: "a? ( b ) !b? ( a )" will
enable both a and b if USE=-*. This is not obvious to me from a single
read of the constraint. I need to tell myself "Oh, I've now enabled 'a'
and I need to recheck the first clause.".

I was thinking more from a spec perspective where I would definitely
not want to mandate PM to do fixpoint(solve) or having to detect a
cycle before failing. Also, I believe that forcing a single pass solver
will help in ensuring that the solver solves it the way the developer
writing the constraint meant it.


[...]
> > > > > > > Implication(useflag, consequence) ->      
> > > > > > 
> > > > > >                  if not input[useflag]: raise
> > > > > > "impossible"      
> > > > > 
> > > > > Why impossible? Unless I'm missing something, it's false
> > > > > already.    
> > > > 
> > > > 'foo? bar' is always true if foo is false; so it's impossible to
> > > > make it false    
> > > 
> > > Yes, you are correct. I was actually thinking of 'if LHS is
> > > false, we do not enforce RHS'.  
> > 
> > I'm wrong actually. It can be falsified by setting foo to True and
> > bar to False. More on it below.  
> 
> Well, I'm not sure if it can still plainly apply here but the generic
> contract was that in implication clauses only RHS is mutable.


Yeah, that's basically what I inferred from trying to figure out the
meaning out of it later on.

[...]
> > > > > > 
> > > > > > Note how the above favors leftmost in all cases. If it
> > > > > > needs to change something, it always tries to leave the
> > > > > > leftmost untouched. Note also that it processes everything
> > > > > > left to right (the AllOf case where
> > > > > > REQUIRED_USE="AllOf(list of clauses)" ).      
> > > > > 
> > > > > You need to be able to reorder the clauses to handle use.force
> > > > > and use.mask.    
> > > > 
> > > > Not sure if reorder is the best way. It sure works, but maybe
> > > > we'd want a repoman error if e.g. 'foo? ( bar )' is in
> > > > REQUIRED_USE, bar is masked but not foo. That'd be a matter of
> > > > eliminating the constants in the ast and if we get 'false' for
> > > > a profile we error out.    
> > > 
> > > Yes, we want that. It makes sense for pure implications. For n-ary
> > > thingies, it's harder than that and I'd rather not require
> > > developers to figure out a specific order to make things work.
> > > 
> > > Think of the following:
> > > 
> > >   || ( X Y )
> > > 
> > > with X being masked on profile A, Y being masked on profile B. You
> > > just can't get it right then.
> > > 
> > > Of course, this is a bit unrealistic but I think a case when a
> > > preferred (newer) provider is masked on some architectures is
> > > realistic. I don't think we want to prefer a worse solution (or go
> > > back to applying package.use workarounds) because of fringe
> > > arches.
> > > 
> > > Another question is whether we could solve this specific problem
> > > via applying some kind of 'if forced/masked' logic into the AST.  
> > 
> > I think we're having communication interferences here :) That's
> > exactly what I'm talking about.
> > 
> > More specifically:
> > 
> > For each profile:
> >     formula=replace_masks_and_force_by_constants(required_use)
> >     simplify(formula)
> >     nsolve(formula)
> > 
> > In your example above, we'd call 'nsolve("|| ( X )")' and
> > 'nsolve("|| ( Y )")' (or even simpler, depending on how simplify()
> > is defined). If both X and Y are masked on a profile, then that'd
> > reduce to 'nsolve("False")' which would rant.  
> 
> So you're talking about reducing prior to transforming? Yes, that'd
> work. As I mentioned in one of the first mails wrt my reference
> implementation, I've used reordering (stable sort) instead of reducing
> since it was simpler.
> 
> If you reduce (simplify), you need to account for special cases like
> getting '|| ()' etc. If you reorder only, things just fail the normal
> way.

While the reordering idea seems nice as it factors both user
preference and masks, the problem with reordering is that nothing
guarantees that the solver won't try to enable a masked flag. We'd have
to deal with that somehow.
I think reordering should be kept for user preferences
(soft-enable/soft-disable) while masks for hard-no's or hard-yes'es.


Be careful with reordering though:
'^^ ( a b ) b? ( a )' can be solved in one pass.
(it disables b if both are set and enables a if none are set)

while:
'^^ ( b a ) b? ( a )' loops
(if both are set it disables 'a' for the 1st clause but then enables it
for the 2nd)

This is not checked by nsolve().

> > I think that for solve(), reordering is basically equivalent since
> > we always favour leftmost. However, we have to somehow guarantee
> > that solve() will never try to enable a masked flag. This is
> > guaranteed by the above checks on profiles if enforced by repoman.
> > If we apply the same logic for solve that'd basically be built-in
> > (masked/forced flags wont appear in solve()'s input anymore), which
> > I believe is better. Keep in mind that users can mask and force
> > some useflags, so the repoman check won't catch that.
> > 
> >   
> > > > > > So, the very first thing to do is to agree that the above
> > > > > > solver (the trueify function) is what we want to implement
> > > > > > and set this in stone. There's no point in implementing a
> > > > > > proper requse checker if the algorithm is meant to change.
> > > > > > Having a formal definition will also be necessary to
> > > > > > mandate that in future EAPIs.
> > > > > > 
> > > > > > Then, and only then, we'd need to have the above solver
> > > > > > implemented into portage (hidden under a FEATURES) and
> > > > > > import my nsolve into repoman (after due cleanup).
> > > > > >       
> > > > > 
> > > > > Yes, that's my goal. However, before we can set the algorithm
> > > > > in stone we need to verify that it will work in all of the
> > > > > supported cases.    
> > > > 
> > > > Yep, that's the point of nsolve/classify :)
> > > >     
> > > > > Preferably it should also be as simple as possible to avoid
> > > > > putting too much complexity in the spec.    
> > > > 
> > > > Yes; not sure if anything simpler than the above can be achieved
> > > > though.   
> > > 
> > > Actually, I think we should roughly proceed in the following way:
> > > 
> > > 1. Design the solve() algorithm.
> > > 
> > > 1a. Test solve() on isolated test cases.
> > > 
> > > 1b. Integrate solve() into Portage (as optional feature) and
> > > encourage wider testing.
> > > 
> > > 2. Design the nsolve() verification based on solve().
> > > 
> > > 2a. Test nsolve() separately.
> > > 
> > > 2b. Start fixing simple cases that do not bring any controversy
> > > (like most of the reorderings).
> > > 
> > > 2c. Integrate nsolve() into repoman.
> > > 
> > > 3. Spec the whole thing and decide how to go next.
> > > 
> > > 
> > > Assuming that 1* and 2* is to be done simultaneously, and each
> > > subpoint implies that if we hit any walls, we can go back to a
> > > previous point and fix the design.
> > > 
> > > I think we're mostly past 1a and 2a now. We should try to think a
> > > bit more about the corner cases right now, and when we're done
> > > with that, the next step would be to:
> > > 
> > > A. start fixing some cases ('need topo sort' at least?) to improve
> > > testing area for the implementation,
> > > 
> > > B. integrate solve() into Portage,
> > > 
> > > C. maybe optionally integrate nsolve() into repoman.
> > > 
> > > However, I don't want to set the spec into stone until we have
> > > some live testing via Portage with FEATURES=solve-required-use or
> > > alike. For repoman, we can add the verification optionally (i.e.
> > > with the above feature or via a separate command-line option) but
> > > I don't think we should enable it by default until we verify it.
> > >   
> > 
> > Yes, I agree. Since solve() and nsolve() are very related we should
> > carefully think how to organize the code then in order to avoid
> > duplication or them getting out of sync.
> > 
> > As for enabling it into repoman, I'm not sure which is best. Safest
> > way is obviously as you suggest, but a warning is not a blocker, so
> > enabling it by default can have a nice effect of receiving hate
> > mails from angry developers if something is wrong.
> >   
> 
> There's wisdom in this as well. I'm just a little bit worried that
> we're first make developers change their REQUIRED_USE, and then have
> to change the algorithm anyway ;-).


Yeah, I wouldn't enable it without some good testing on the whole tree
and starting to deal with the annoying cases. But then I'd assume it'd
be good.

Alexis.

Reply via email to