tools/disable_tests/conditions.py
def simplify(cond: Condition) -> Condition:
  """Given a Condition, produce an equivalent but simpler Condition.

  This function uses the Quine-McCluskey algorithm. It's not implemented very
  efficiently, but it works and is fast enough for now.
  """

  if isinstance(cond, BaseCondition):
    return cond

  # Quine-McCluskey uses three values - true, false, and "don't care". The
  # latter represents two things:
  # * For values of input variables, the case where either true or false will
  #   suffice. This is used when combining conditions that differ only in that
  #   variable into one where that variable isn't specified.
  # * For resulting values of the function, the case where we don't care what
  #   value the function produces. We use this for mutually exclusive
  #   conditions.  For example, (linux & mac) is impossible, so we assign this a
  #   "don't care" value. This avoids producing a bunch of redundant stuff like
  #   (linux & ~mac).
  DONT_CARE = 2

  # First, compute the truth table of the function. We produce a set of
  # "minterms", which are the combinations of input values for which the output
  # is 1. We also produce a set of "don't care" values, which are the
  # combinations of input values for which we don't care what the output is.
  #
  # Both of these are represented via tuples of {0, 1} values, which the value
  # at index 'i' corresponds to variables[i].
  # TODO: This could use a more efficient representation. Some packed integer
  # using two bits per element or something.
  variables = list(sorted(find_terminals(cond)))
  dont_cares = []
  min_terms = []
  for possible_input in itertools.product([0, 1], repeat=len(variables)):
    # Generate every possible input, and evaluate the condition for that input.
    # This is exponential in the number of variables, but in practice the number
    # should be low (and is strictly bounded by len(TERMINALS)).
    true_vars = {variables[i] for i, val in enumerate(possible_input) if val}
    if any(len(group & true_vars) > 1 for group in CONDITION_GROUPS):
      # Any combination which sets more than one variable from the same group to
      # 1 is impossible, so we don't care about the output.
      dont_cares.append(possible_input)
    elif evaluate(cond, true_vars):
      min_terms.append(possible_input)

  # The meat of the algorithm. Try to combine minterms which differ by only a
  # single variable.
  # For example, (0, 1) and (0, 0) can be combined into (0, DONT_CARE), as the
  # value of the second variable doesn't affect the output.
  #
  # We work in rounds, combining together all minterms from the previous round
  # that can be. This may include combining the same minterm with multiple
  # different minterms. Keep going until no more minterms can be combined.
  #
  # Any minterm which can't be combined with another is a "prime implicant",
  # that is, it's a necessary part of the representation of the function. The
  # union of all prime implicants specifies the function.
  combined_some_minterms = True
  prev_round = set(min_terms + dont_cares)
  prime_implicants: List[Tuple] = []
  while combined_some_minterms:
    new_implicants = set()
    used = set()
    combined_some_minterms = False

    # TODO: Rather than taking combinations of the entire set of minterms, we
    # can instead group by the number of '1's. Then we only need to combine
    # elements from adjacent groups.
    for a, b in itertools.combinations(prev_round, 2):
      diff_index = None
      for i, (x, y) in enumerate(zip(a, b)):
        if x != y:
          if diff_index is not None:
            # In this case there are at least two points of difference, so these
            # two can't be combined.
            break
          diff_index = i
      else:
        if diff_index is not None:
          # Replace the sole differing variable with DONT_CARE to produce the
          # combined minterm. Flag both inputs as having been used, and
          # therefore as not being prime implicants.
          new_implicants.add(a[:diff_index] + (DONT_CARE, ) +
                             a[diff_index + 1:])
          used |= {a, b}
          combined_some_minterms = True

    # Collect any minterms that weren't used in this round as prime implicants.
    prime_implicants.extend(prev_round - used)
    prev_round = new_implicants

  # TODO: This isn't yet minimal - the set of prime implicants may have some
  # redundancy which can be reduced further. For now we just accept that and
  # use this set as-is. If we encounter any case for which we don't produce the
  # minimal result, we'll need to implement something like Petrick's method.

  # Finally, create our simplified condition using the computed set of prime
  # implicants.
  # TODO: Ordering. We should define some stable ordering to use. We probably
  # want to group stuff based on CONDITION_GROUPS, so all the OS-related
  # conditions are together, for instance. And then alphabetically within that.
  or_args: List[Condition] = []
  for pi in sorted(prime_implicants):
    and_args: List[Condition] = []
    for i, x in enumerate(pi):
      if x == DONT_CARE:
        continue

      var = variables[i]
      if x == 0:
        and_args.append(('not', var))
      else:
        assert x == 1
        and_args.append(var)

    or_args.append(op_of('and', and_args))

  return op_of('or', or_args)
Given a Condition, produce an equivalent but simpler Condition.

  This function uses the Quine-McCluskey algorithm. It's not implemented very
  efficiently, but it works and is fast enough for now.
  
Given a Condition, produce an equivalent but simpler Condition.
This function uses the Quine-McCluskey algorithm. It's not implemented very
efficiently, but it works and is fast enough for now.
Given a Condition produce an equivalent but simpler Condition . This function uses the Quine - McCluskey algorithm . It ' s not implemented very efficiently but it works and is fast enough for now .
def simplify(cond: Condition) -> Condition:
  if isinstance(cond, BaseCondition):
    return cond
  DONT_CARE = 2
   Both of these are represented via tuples of {0, 1} values, which the value
   at index 'i' corresponds to variables[i].
   TODO: This could use a more efficient representation. Some packed integer
   using two bits per element or something.
  variables = list(sorted(find_terminals(cond)))
  dont_cares = []
  min_terms = []
  for possible_input in itertools.product([0, 1], repeat=len(variables)):
     Generate every possible input, and evaluate the condition for that input.
     This is exponential in the number of variables, but in practice the number
     should be low (and is strictly bounded by len(TERMINALS)).
    true_vars = {variables[i] for i, val in enumerate(possible_input) if val}
    if any(len(group & true_vars) > 1 for group in CONDITION_GROUPS):
       Any combination which sets more than one variable from the same group to
       1 is impossible, so we don't care about the output.
      dont_cares.append(possible_input)
    elif evaluate(cond, true_vars):
      min_terms.append(possible_input)
   The meat of the algorithm. Try to combine minterms which differ by only a
   single variable.
   For example, (0, 1) and (0, 0) can be combined into (0, DONT_CARE), as the
   value of the second variable doesn't affect the output.
   We work in rounds, combining together all minterms from the previous round
   that can be. This may include combining the same minterm with multiple
   different minterms. Keep going until no more minterms can be combined.
   Any minterm which can't be combined with another is a "prime implicant",
   that is, it's a necessary part of the representation of the function. The
   union of all prime implicants specifies the function.
  combined_some_minterms = True
  prev_round = set(min_terms + dont_cares)
  prime_implicants: List[Tuple] = []
  while combined_some_minterms:
    new_implicants = set()
    used = set()
    combined_some_minterms = False
     TODO: Rather than taking combinations of the entire set of minterms, we
     can instead group by the number of '1's. Then we only need to combine
     elements from adjacent groups.
    for a, b in itertools.combinations(prev_round, 2):
      diff_index = None
      for i, (x, y) in enumerate(zip(a, b)):
        if x != y:
          if diff_index is not None:
             In this case there are at least two points of difference, so these
             two can't be combined.
            break
          diff_index = i
      else:
        if diff_index is not None:
           Replace the sole differing variable with DONT_CARE to produce the
           combined minterm. Flag both inputs as having been used, and
           therefore as not being prime implicants.
          new_implicants.add(a[:diff_index] + (DONT_CARE, ) +
                             a[diff_index + 1:])
          used |= {a, b}
          combined_some_minterms = True
     Collect any minterms that weren't used in this round as prime implicants.
    prime_implicants.extend(prev_round - used)
    prev_round = new_implicants
   TODO: This isn't yet minimal - the set of prime implicants may have some
   redundancy which can be reduced further. For now we just accept that and
   use this set as-is. If we encounter any case for which we don't produce the
   minimal result, we'll need to implement something like Petrick's method.
   Finally, create our simplified condition using the computed set of prime
   implicants.
   TODO: Ordering. We should define some stable ordering to use. We probably
   want to group stuff based on CONDITION_GROUPS, so all the OS-related
   conditions are together, for instance. And then alphabetically within that.
  or_args: List[Condition] = []
  for pi in sorted(prime_implicants):
    and_args: List[Condition] = []
    for i, x in enumerate(pi):
      if x == DONT_CARE:
        continue
      var = variables[i]
      if x == 0:
        and_args.append(('not', var))
      else:
        assert x == 1
        and_args.append(var)
    or_args.append(op_of('and', and_args))
  return op_of('or', or_args)
def simplify ( cond : Condition ) -> Condition : if isinstance ( cond , BaseCondition ) : return cond DONT_CARE = 2 variables = list ( sorted ( find_terminals ( cond ) ) ) dont_cares = [ ] min_terms = [ ] for possible_input in itertools . product ( [ 0 , 1 ] , repeat = len ( variables ) ) : true_vars = { variables [ i ] for i , val in enumerate ( possible_input ) if val } if any ( len ( group & true_vars ) > 1 for group in CONDITION_GROUPS ) : dont_cares . append ( possible_input ) elif evaluate ( cond , true_vars ) : min_terms . append ( possible_input ) combined_some_minterms = True prev_round = set ( min_terms + dont_cares ) prime_implicants : List [ Tuple ] = [ ] while combined_some_minterms : new_implicants = set ( ) used = set ( ) combined_some_minterms = False for a , b in itertools . combinations ( prev_round , 2 ) : diff_index = None for i , ( x , y ) in enumerate ( zip ( a , b ) ) : if x != y : if diff_index is not None : break diff_index = i else : if diff_index is not None : new_implicants . add ( a [ : diff_index ] + ( DONT_CARE , ) + a [ diff_index + 1 : ] ) used |= { a , b } combined_some_minterms = True prime_implicants . extend ( prev_round - used ) prev_round = new_implicants or_args : List [ Condition ] = [ ] for pi in sorted ( prime_implicants ) : and_args : List [ Condition ] = [ ] for i , x in enumerate ( pi ) : if x == DONT_CARE : continue var = variables [ i ] if x == 0 : and_args . append ( ( 'not' , var ) ) else : assert x == 1 and_args . append ( var ) or_args . append ( op_of ( 'and' , and_args ) ) return op_of ( 'or' , or_args )
Given a Condition, produce an equivalent but simpler Condition.
Given a Condition produce an equivalent but simpler Condition .
"""Given a Condition, produce an equivalent but simpler Condition.

  This function uses the Quine-McCluskey algorithm. It's not implemented very
  efficiently, but it works and is fast enough for now.
  """ # Quine-McCluskey uses three values - true, false, and "don't care". The # latter represents two things: # * For values of input variables, the case where either true or false will #   suffice. This is used when combining conditions that differ only in that #   variable into one where that variable isn't specified. # * For resulting values of the function, the case where we don't care what #   value the function produces. We use this for mutually exclusive #   conditions.  For example, (linux & mac) is impossible, so we assign this a #   "don't care" value. This avoids producing a bunch of redundant stuff like #   (linux & ~mac). # First, compute the truth table of the function. We produce a set of # "minterms", which are the combinations of input values for which the output # is 1. We also produce a set of "don't care" values, which are the # combinations of input values for which we don't care what the output is. # # Both of these are represented via tuples of {0, 1} values, which the value # at index 'i' corresponds to variables[i]. # TODO: This could use a more efficient representation. Some packed integer # using two bits per element or something. # Generate every possible input, and evaluate the condition for that input. # This is exponential in the number of variables, but in practice the number # should be low (and is strictly bounded by len(TERMINALS)). # Any combination which sets more than one variable from the same group to # 1 is impossible, so we don't care about the output. # The meat of the algorithm. Try to combine minterms which differ by only a # single variable. # For example, (0, 1) and (0, 0) can be combined into (0, DONT_CARE), as the # value of the second variable doesn't affect the output. # # We work in rounds, combining together all minterms from the previous round # that can be. This may include combining the same minterm with multiple # different minterms. Keep going until no more minterms can be combined. # # Any minterm which can't be combined with another is a "prime implicant", # that is, it's a necessary part of the representation of the function. The # union of all prime implicants specifies the function. # TODO: Rather than taking combinations of the entire set of minterms, we # can instead group by the number of '1's. Then we only need to combine # elements from adjacent groups. # In this case there are at least two points of difference, so these # two can't be combined. # Replace the sole differing variable with DONT_CARE to produce the # combined minterm. Flag both inputs as having been used, and # therefore as not being prime implicants. # Collect any minterms that weren't used in this round as prime implicants. # TODO: This isn't yet minimal - the set of prime implicants may have some # redundancy which can be reduced further. For now we just accept that and # use this set as-is. If we encounter any case for which we don't produce the # minimal result, we'll need to implement something like Petrick's method. # Finally, create our simplified condition using the computed set of prime # implicants. # TODO: Ordering. We should define some stable ordering to use. We probably # want to group stuff based on CONDITION_GROUPS, so all the OS-related # conditions are together, for instance. And then alphabetically within that.
cond
Condition