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

     1  package solver
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/go-air/gini/inter"
     8  	"github.com/go-air/gini/logic"
     9  	"github.com/go-air/gini/z"
    10  )
    11  
    12  type DuplicateIdentifier Identifier
    13  
    14  func (e DuplicateIdentifier) Error() string {
    15  	return fmt.Sprintf("duplicate identifier %q in input", Identifier(e))
    16  }
    17  
    18  type inconsistentLitMapping []error
    19  
    20  func (inconsistentLitMapping) Error() string {
    21  	return "internal solver failure"
    22  }
    23  
    24  // litMapping performs translation between the input and output types of
    25  // Solve (Constraints, Variables, etc.) and the variables that
    26  // appear in the SAT formula.
    27  type litMapping struct {
    28  	inorder     []Variable
    29  	variables   map[z.Lit]Variable
    30  	lits        map[Identifier]z.Lit
    31  	constraints map[z.Lit]AppliedConstraint
    32  	c           *logic.C
    33  	errs        inconsistentLitMapping
    34  }
    35  
    36  // newLitMapping returns a new litMapping with its state initialized based on
    37  // the provided slice of Variables. This includes construction of
    38  // the translation tables between Variables/Constraints and the
    39  // inputs to the underlying solver.
    40  func newLitMapping(variables []Variable) (*litMapping, error) {
    41  	d := litMapping{
    42  		inorder:     variables,
    43  		variables:   make(map[z.Lit]Variable, len(variables)),
    44  		lits:        make(map[Identifier]z.Lit, len(variables)),
    45  		constraints: make(map[z.Lit]AppliedConstraint),
    46  		c:           logic.NewCCap(len(variables)),
    47  	}
    48  
    49  	// First pass to assign lits:
    50  	for _, variable := range variables {
    51  		im := d.c.Lit()
    52  		if _, ok := d.lits[variable.Identifier()]; ok {
    53  			return nil, DuplicateIdentifier(variable.Identifier())
    54  		}
    55  		d.lits[variable.Identifier()] = im
    56  		d.variables[im] = variable
    57  	}
    58  
    59  	for _, variable := range variables {
    60  		for _, constraint := range variable.Constraints() {
    61  			m := constraint.apply(d.c, &d, variable.Identifier())
    62  			if m == z.LitNull {
    63  				// This constraint doesn't have a
    64  				// useful representation in the SAT
    65  				// inputs.
    66  				continue
    67  			}
    68  
    69  			d.constraints[m] = AppliedConstraint{
    70  				Variable:   variable,
    71  				Constraint: constraint,
    72  			}
    73  		}
    74  	}
    75  
    76  	return &d, nil
    77  }
    78  
    79  // LitOf returns the positive literal corresponding to the Variable
    80  // with the given Identifier.
    81  func (d *litMapping) LitOf(id Identifier) z.Lit {
    82  	m, ok := d.lits[id]
    83  	if ok {
    84  		return m
    85  	}
    86  	d.errs = append(d.errs, fmt.Errorf("variable %q referenced but not provided", id))
    87  	return z.LitNull
    88  }
    89  
    90  // VariableOf returns the Variable corresponding to the provided
    91  // literal, or a zeroVariable if no such Variable exists.
    92  func (d *litMapping) VariableOf(m z.Lit) Variable {
    93  	i, ok := d.variables[m]
    94  	if ok {
    95  		return i
    96  	}
    97  	d.errs = append(d.errs, fmt.Errorf("no variable corresponding to %s", m))
    98  	return zeroVariable{}
    99  }
   100  
   101  // ConstraintOf returns the constraint application corresponding to
   102  // the provided literal, or a zeroConstraint if no such constraint
   103  // exists.
   104  func (d *litMapping) ConstraintOf(m z.Lit) AppliedConstraint {
   105  	if a, ok := d.constraints[m]; ok {
   106  		return a
   107  	}
   108  	d.errs = append(d.errs, fmt.Errorf("no constraint corresponding to %s", m))
   109  	return AppliedConstraint{
   110  		Variable:   zeroVariable{},
   111  		Constraint: zeroConstraint{},
   112  	}
   113  }
   114  
   115  // Error returns a single error value that is an aggregation of all
   116  // errors encountered during a litMapping's lifetime, or nil if there have
   117  // been no errors. A non-nil return value likely indicates a problem
   118  // with the solver or constraint implementations.
   119  func (d *litMapping) Error() error {
   120  	if len(d.errs) == 0 {
   121  		return nil
   122  	}
   123  	s := make([]string, len(d.errs))
   124  	for i, err := range d.errs {
   125  		s[i] = err.Error()
   126  	}
   127  	return fmt.Errorf("%d errors encountered: %s", len(s), strings.Join(s, ", "))
   128  }
   129  
   130  // AddConstraints adds the current constraints encoded in the embedded circuit to the
   131  // solver g
   132  func (d *litMapping) AddConstraints(g inter.S) {
   133  	d.c.ToCnf(g)
   134  }
   135  
   136  func (d *litMapping) AssumeConstraints(s inter.S) {
   137  	for m := range d.constraints {
   138  		s.Assume(m)
   139  	}
   140  }
   141  
   142  // CardinalityConstrainer constructs a sorting network to provide
   143  // cardinality constraints over the provided slice of literals. Any
   144  // new clauses and variables are translated to CNF and taught to the
   145  // given inter.Adder, so this function will panic if it is in a test
   146  // context.
   147  func (d *litMapping) CardinalityConstrainer(g inter.Adder, ms []z.Lit) *logic.CardSort {
   148  	clen := d.c.Len()
   149  	cs := d.c.CardSort(ms)
   150  	marks := make([]int8, clen, d.c.Len())
   151  	for i := range marks {
   152  		marks[i] = 1
   153  	}
   154  	for w := 0; w <= cs.N(); w++ {
   155  		marks, _ = d.c.CnfSince(g, marks, cs.Leq(w))
   156  	}
   157  	return cs
   158  }
   159  
   160  // AnchorIdentifiers returns a slice containing the Identifiers of
   161  // every Variable with at least one "anchor" constraint, in the
   162  // order they appear in the input.
   163  func (d *litMapping) AnchorIdentifiers() []Identifier {
   164  	var ids []Identifier
   165  	for _, variable := range d.inorder {
   166  		for _, constraint := range variable.Constraints() {
   167  			if constraint.anchor() {
   168  				ids = append(ids, variable.Identifier())
   169  				break
   170  			}
   171  		}
   172  	}
   173  	return ids
   174  }
   175  
   176  func (d *litMapping) Variables(g inter.S) []Variable {
   177  	var result []Variable
   178  	for _, i := range d.inorder {
   179  		if g.Value(d.LitOf(i.Identifier())) {
   180  			result = append(result, i)
   181  		}
   182  	}
   183  	return result
   184  }
   185  
   186  func (d *litMapping) Lits(dst []z.Lit) []z.Lit {
   187  	if cap(dst) < len(d.inorder) {
   188  		dst = make([]z.Lit, 0, len(d.inorder))
   189  	}
   190  	dst = dst[:0]
   191  	for _, i := range d.inorder {
   192  		m := d.LitOf(i.Identifier())
   193  		dst = append(dst, m)
   194  	}
   195  	return dst
   196  }
   197  
   198  func (d *litMapping) Conflicts(g inter.Assumable) []AppliedConstraint {
   199  	whys := g.Why(nil)
   200  	as := make([]AppliedConstraint, 0, len(whys))
   201  	for _, why := range whys {
   202  		if a, ok := d.constraints[why]; ok {
   203  			as = append(as, a)
   204  		}
   205  	}
   206  	return as
   207  }