1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101

content / test / gpu / gold_inexact_matching / binary_search_parameter_optimizer.py [blame]

# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


import enum
import logging
from typing import Optional, Tuple

import gold_inexact_matching.base_parameter_optimizer as base_optimizer
from gold_inexact_matching import common_typing as ct
from gold_inexact_matching import parameter_set


class _UnlockedParameter(enum.Enum):
  MAX_DIFF = 1
  DELTA_THRESHOLD = 2
  EDGE_THRESHOLD = 3


class BinarySearchParameterOptimizer(base_optimizer.BaseParameterOptimizer):
  """A ParameterOptimizer for use with a single changing parameter.

  The ideal optimizer if only one parameter needs to be varied, e.g. finding
  the best Sobel edge threshold to use when not using any additional fuzzy
  diffing.
  """

  def __init__(self, args: ct.ParsedCmdArgs, test_name: str):
    self._unlocked_parameter: Optional[_UnlockedParameter] = None
    super().__init__(args, test_name)

  def _VerifyArgs(self) -> None:
    super()._VerifyArgs()

    max_diff_locked = self._args.max_max_diff == self._args.min_max_diff
    delta_threshold_locked = (
        self._args.max_delta_threshold == self._args.min_delta_threshold)
    edge_threshold_locked = (
        self._args.max_edge_threshold == self._args.min_delta_threshold)

    if not ((max_diff_locked ^ delta_threshold_locked) or
            (delta_threshold_locked ^ edge_threshold_locked)):
      raise RuntimeError(
          'Binary search optimization requires all but one parameter to be '
          'locked (min == max).')

    if not max_diff_locked:
      self._unlocked_parameter = _UnlockedParameter.MAX_DIFF
    elif not delta_threshold_locked:
      self._unlocked_parameter = _UnlockedParameter.DELTA_THRESHOLD
    else:
      self._unlocked_parameter = _UnlockedParameter.EDGE_THRESHOLD

  def _RunOptimizationImpl(self) -> None:
    known_good, known_bad = self._GetStartingValues()
    while (abs(known_good - known_bad)) > 1:
      midpoint = (known_good + known_bad) // 2
      parameters = self._CreateParameterSet(midpoint)
      success, _, _ = self._RunComparisonForParameters(parameters)
      if success:
        logging.info('Found good parameters %s', parameters)
        known_good = midpoint
      else:
        logging.info('Found bad parameters %s', parameters)
        known_bad = midpoint
    print('Found optimal parameters: %s' % parameters)

  def _GetStartingValues(self) -> Tuple[int, int]:
    """Gets the initial good/bad values for the binary search.

    Returns:
      A tuple (known_good, assumed_bad). |known_good| is a value that is known
      to make the comparison succeed. |assumed_bad| is a value that is expected
      to make the comparison fail, although it has not necessarily been tested
      yet.
    """
    if self._unlocked_parameter == _UnlockedParameter.MAX_DIFF:
      return self._args.max_max_diff, self._args.min_max_diff
    if self._unlocked_parameter == _UnlockedParameter.DELTA_THRESHOLD:
      return self._args.max_delta_threshold, self._args.min_delta_threshold
    return self._args.min_edge_threshold, self._args.max_edge_threshold

  def _CreateParameterSet(self, value: int) -> parameter_set.ParameterSet:
    """Creates a parameter_set.ParameterSet to test.

    Args:
      value: The value to set the variable parameter to.

    Returns:
      A parameter_set.ParameterSet with the variable parameter set to |value|
      and the other parameters set to their fixed values.
    """
    if self._unlocked_parameter == _UnlockedParameter.MAX_DIFF:
      return parameter_set.ParameterSet(value, self._args.min_delta_threshold,
                                        self._args.min_edge_threshold)
    if self._unlocked_parameter == _UnlockedParameter.DELTA_THRESHOLD:
      return parameter_set.ParameterSet(self._args.min_max_diff, value,
                                        self._args.min_edge_threshold)
    return parameter_set.ParameterSet(self._args.min_max_diff,
                                      self._args.min_delta_threshold, value)