github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/resolver/solver/search.go (about)

     1  package solver
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/go-air/gini/inter"
     7  	"github.com/go-air/gini/z"
     8  )
     9  
    10  type choice struct {
    11  	prev, next *choice
    12  	index      int // index of next unguessed literal
    13  	candidates []z.Lit
    14  }
    15  
    16  type guess struct {
    17  	m          z.Lit // if z.LitNull, this choice was satisfied by a previous assumption
    18  	index      int   // index of guessed literal in candidates
    19  	children   int   // number of choices introduced by making this guess
    20  	candidates []z.Lit
    21  }
    22  
    23  type search struct {
    24  	s                      inter.S
    25  	lits                   *litMapping
    26  	assumptions            map[z.Lit]struct{} // set of assumed lits - duplicates guess stack - for fast lookup
    27  	guesses                []guess            // stack of assumed guesses
    28  	headChoice, tailChoice *choice            // deque of unmade choices
    29  	tracer                 Tracer
    30  	result                 int
    31  	buffer                 []z.Lit
    32  }
    33  
    34  func (h *search) PushGuess() {
    35  	c := h.PopChoiceFront()
    36  	g := guess{
    37  		m:          z.LitNull,
    38  		index:      c.index,
    39  		candidates: c.candidates,
    40  	}
    41  	if g.index < len(g.candidates) {
    42  		g.m = g.candidates[g.index]
    43  	}
    44  
    45  	// Check whether or not this choice can be satisfied by an
    46  	// existing assumption.
    47  	for _, m := range g.candidates {
    48  		if _, ok := h.assumptions[m]; ok {
    49  			g.m = z.LitNull
    50  			break
    51  		}
    52  	}
    53  
    54  	h.guesses = append(h.guesses, g)
    55  	if g.m == z.LitNull {
    56  		return
    57  	}
    58  
    59  	variable := h.lits.VariableOf(g.m)
    60  	for _, constraint := range variable.Constraints() {
    61  		var ms []z.Lit
    62  		for _, dependency := range constraint.order() {
    63  			ms = append(ms, h.lits.LitOf(dependency))
    64  		}
    65  		if len(ms) > 0 {
    66  			h.guesses[len(h.guesses)-1].children++
    67  			h.PushChoiceBack(choice{candidates: ms})
    68  		}
    69  	}
    70  
    71  	if h.assumptions == nil {
    72  		h.assumptions = make(map[z.Lit]struct{})
    73  	}
    74  	h.assumptions[g.m] = struct{}{}
    75  	h.s.Assume(g.m)
    76  	h.result, h.buffer = h.s.Test(h.buffer)
    77  }
    78  
    79  func (h *search) PopGuess() {
    80  	g := h.guesses[len(h.guesses)-1]
    81  	h.guesses = h.guesses[:len(h.guesses)-1]
    82  	if g.m != z.LitNull {
    83  		delete(h.assumptions, g.m)
    84  		h.result = h.s.Untest()
    85  	}
    86  	for g.children > 0 {
    87  		g.children--
    88  		h.PopChoiceBack()
    89  	}
    90  	c := choice{
    91  		index:      g.index,
    92  		candidates: g.candidates,
    93  	}
    94  	if g.m != z.LitNull {
    95  		c.index++
    96  	}
    97  	h.PushChoiceFront(c)
    98  }
    99  
   100  func (h *search) PushChoiceFront(c choice) {
   101  	if h.headChoice == nil {
   102  		h.headChoice = &c
   103  		h.tailChoice = &c
   104  		return
   105  	}
   106  	h.headChoice.prev = &c
   107  	c.next = h.headChoice
   108  	h.headChoice = &c
   109  }
   110  
   111  func (h *search) PopChoiceFront() choice {
   112  	c := h.headChoice
   113  	if c.next != nil {
   114  		c.next.prev = nil
   115  	} else {
   116  		h.tailChoice = nil
   117  	}
   118  	h.headChoice = c.next
   119  	return *c
   120  }
   121  
   122  func (h *search) PushChoiceBack(c choice) {
   123  	if h.tailChoice == nil {
   124  		h.headChoice = &c
   125  		h.tailChoice = &c
   126  		return
   127  	}
   128  	h.tailChoice.next = &c
   129  	c.prev = h.tailChoice
   130  	h.tailChoice = &c
   131  }
   132  
   133  func (h *search) PopChoiceBack() choice {
   134  	c := h.tailChoice
   135  	if c.prev != nil {
   136  		c.prev.next = nil
   137  	} else {
   138  		h.headChoice = nil
   139  	}
   140  	h.tailChoice = c.prev
   141  	return *c
   142  }
   143  
   144  func (h *search) Result() int {
   145  	return h.result
   146  }
   147  
   148  func (h *search) Lits() []z.Lit {
   149  	result := make([]z.Lit, 0, len(h.guesses))
   150  	for _, g := range h.guesses {
   151  		if g.m != z.LitNull {
   152  			result = append(result, g.m)
   153  		}
   154  	}
   155  	return result
   156  }
   157  
   158  func (h *search) Do(ctx context.Context, anchors []z.Lit) (int, []z.Lit, map[z.Lit]struct{}) {
   159  	for _, m := range anchors {
   160  		h.PushChoiceBack(choice{candidates: []z.Lit{m}})
   161  	}
   162  
   163  	for {
   164  		// Need to have a definitive result once all choices
   165  		// have been made to decide whether to end or
   166  		// backtrack.
   167  		if h.headChoice == nil && h.result == unknown {
   168  			h.result = h.s.Solve()
   169  		}
   170  
   171  		// Backtrack if possible, otherwise end.
   172  		if h.result == unsatisfiable {
   173  			h.tracer.Trace(h)
   174  			if len(h.guesses) == 0 {
   175  				break
   176  			}
   177  			h.PopGuess()
   178  			continue
   179  		}
   180  
   181  		// Satisfiable and no decisions left!
   182  		if h.headChoice == nil {
   183  			break
   184  		}
   185  
   186  		// Possibly SAT, keep guessing.
   187  		h.PushGuess()
   188  	}
   189  
   190  	lits := h.Lits()
   191  	set := make(map[z.Lit]struct{}, len(lits))
   192  	for _, m := range lits {
   193  		set[m] = struct{}{}
   194  	}
   195  	result := h.Result()
   196  
   197  	// Go back to the initial test scope.
   198  	for len(h.guesses) > 0 {
   199  		h.PopGuess()
   200  	}
   201  
   202  	return result, lits, set
   203  }
   204  
   205  func (h *search) Variables() []Variable {
   206  	result := make([]Variable, 0, len(h.guesses))
   207  	for _, g := range h.guesses {
   208  		if g.m != z.LitNull {
   209  			result = append(result, h.lits.VariableOf(g.candidates[g.index]))
   210  		}
   211  	}
   212  	return result
   213  }
   214  
   215  func (h *search) Conflicts() []AppliedConstraint {
   216  	return h.lits.Conflicts(h.s)
   217  }